<script lang="ts">
import * as m from '$lib/paraglide/messages.js';
import TimelineView from '$lib/timeline/TimelineView.svelte';
import TimelineFilters from '$lib/timeline/TimelineFilters.svelte';
import GroupingControl from '$lib/timeline/GroupingControl.svelte';
import { timelineMeta } from '$lib/timeline/timelineMeta';
import { filterTimeline } from '$lib/timeline/timelineFilter';
import { hasLooseLetters, type GroupingMode } from '$lib/timeline/timelineGrouping';
import type { PageData } from './$types';

let { data }: { data: PageData } = $props();

const hasContent = $derived(data.timeline.years.length > 0 || data.timeline.undated.length > 0);

// Layer-filter state (#780). Layer hiding is client-side only — the whole
// timeline is loaded once by #779's SSR load and we derive a filtered view of
// it here; there is no goto, no URL param, and no extra fetch.
let personalOn = $state(true);
let historicalOn = $state(true);
let lettersOn = $state(true);

// Grouping state (#827) lives here beside the layer-filter state; the regroup is a
// pure client-side transform over the already-filtered view — filter-then-group.
let groupingMode = $state<GroupingMode>('date');

const filteredTimeline = $derived(
	filterTimeline(data.timeline, { personalOn, historicalOn, lettersOn })
);
const filteredEmpty = $derived(
	filteredTimeline.years.length === 0 && filteredTimeline.undated.length === 0
);
// The grouping control is only meaningful while loose letters remain in the filtered
// view; with the Letters layer off there is nothing to regroup, so it disables but
// keeps its selected mode (REQ-018).
const hasLetters = $derived(hasLooseLetters(filteredTimeline));

// Meta-line figures track the *filtered* view, so the header counts always
// match what is actually on screen once layers are toggled off (#780 — this
// closes the prior D1 limitation, where the counts stayed on the full timeline).
const meta = $derived(timelineMeta(filteredTimeline));

function resetFilters() {
	personalOn = true;
	historicalOn = true;
	lettersOn = true;
}

// Compose the sub-line from segments joined by " · " so the range drops out
// cleanly when there are no year bands; the whole line is absent when the
// timeline is empty (REQ-002). Counts come from the route alone, never from
// TimelineView.
const metaLine = $derived.by(() => {
	const segments: string[] = [];
	if (meta.firstYear !== null && meta.lastYear !== null) {
		segments.push(`${meta.firstYear}–${meta.lastYear}`);
	}
	// A zero-count segment ("0 Briefe") reads as a data error — drop it; a count
	// of one takes the singular key ("1 Brief"), per the project plural convention.
	if (meta.letterCount > 0) {
		segments.push(
			meta.letterCount === 1
				? m.timeline_letters_count_singular()
				: m.timeline_letters_count({ count: meta.letterCount })
		);
	}
	if (meta.eventCount > 0) {
		segments.push(
			meta.eventCount === 1
				? m.timeline_events_count_singular()
				: m.timeline_events_count({ count: meta.eventCount })
		);
	}
	segments.push(
		groupingMode === 'event'
			? m.timeline_grouping_event()
			: groupingMode === 'thema'
				? m.timeline_grouping_thema()
				: m.timeline_grouping_date()
	);
	return segments.join(' · ');
});
</script>

<svelte:head>
	<title>{m.timeline_heading()}</title>
</svelte:head>

<div class="mx-auto max-w-5xl px-4 py-8">
	<!-- The .tl-canvas sheet: a padded canvas surface for the timeline. The outer
	     border is intentionally omitted (the page is already bg-canvas), per the
	     review of REQ-001 — the sheet reads through its padding, not a frame line. -->
	<div data-testid="timeline-canvas" class="rounded-[10px] bg-canvas p-6">
		<!-- Wrapping header so the CTA drops below the heading at narrow widths
		     (≤360px) instead of overflowing — #842 REQ-001. -->
		<header class="flex flex-wrap items-center justify-between gap-3">
			<h1 class="font-serif text-2xl font-bold text-brand-navy">{m.timeline_heading()}</h1>
			{#if data.canWrite}
				<a
					data-testid="timeline-add-event"
					href="/zeitstrahl/events/new"
					class="inline-flex h-11 items-center rounded bg-primary px-4 font-sans text-sm font-medium text-primary-fg hover:opacity-90 focus:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring"
				>
					{m.timeline_add_event()}
				</a>
			{/if}
		</header>
		{#if hasContent}
			<p data-testid="timeline-meta" class="mt-1 mb-3 font-sans text-xs text-ink-3">{metaLine}</p>
			<!-- Grouping toggle stacked above the #780 layer-filter trigger so the two read as
			     one control cluster in the header (REQ-010); the top-right corner stays the
			     add-event CTA. Disabled — but kept in place — when no loose letters remain
			     (REQ-018). -->
			<div class="mb-3" data-testid="grouping-cluster">
				<GroupingControl bind:mode={groupingMode} disabled={!hasLetters} />
			</div>
			<TimelineFilters
				bind:personalOn={personalOn}
				bind:historicalOn={historicalOn}
				bind:lettersOn={lettersOn}
			/>
		{/if}
		{#if hasContent && filteredEmpty}
			<!-- Filtered-empty: a calm message + one-click reset below the still-open
			     filter bar — never a blank page, and never the generic "no events"
			     state (which would imply the archive itself is empty). REQ-006. -->
			<div data-testid="timeline-filter-empty" class="py-12 text-center">
				<p class="font-serif text-base text-ink-2">{m.timeline_filter_empty_state()}</p>
				<button
					type="button"
					data-testid="timeline-filter-empty-reset"
					onclick={resetFilters}
					class="mt-3 inline-flex min-h-[44px] items-center font-sans text-sm text-primary underline-offset-2 hover:underline"
				>
					{m.timeline_filter_reset()}
				</button>
			</div>
		{:else}
			<TimelineView
				timeline={filteredTimeline}
				canWrite={data.canWrite}
				groupingMode={groupingMode}
			/>
		{/if}
	</div>
</div>
