<script lang="ts">
import { onMount, onDestroy } from 'svelte';
import { goto } from '$app/navigation';
import { page, navigating } from '$app/state';
import * as m from '$lib/paraglide/messages.js';
import {
	getNotificationStore,
	type NotificationItem
} from '$lib/notification/notifications.svelte';
import ChronikFuerDichBox from '$lib/activity/ChronikFuerDichBox.svelte';
import ChronikFilterPills from '$lib/activity/ChronikFilterPills.svelte';
import ChronikTimeline from '$lib/activity/ChronikTimeline.svelte';
import ChronikEmptyState from '$lib/activity/ChronikEmptyState.svelte';
import ChronikErrorCard from '$lib/activity/ChronikErrorCard.svelte';
import type { components } from '$lib/generated/api';
import type { FilterValue } from './+page.server';
import { applyClientFilter } from './clientFilter';

type ActivityFeedItemDTO = components['schemas']['ActivityFeedItemDTO'];

interface Props {
	data: {
		filter: FilterValue;
		activityFeed: ActivityFeedItemDTO[];
		unreadNotifications: components['schemas']['NotificationDTO'][];
		loadError: string | null;
	};
}

const { data }: Props = $props();

const notificationStore = getNotificationStore();

// Prefer the live SSE store for unread items so newly arriving mentions
// prepend without a reload. On first mount, seed from the server-loaded unread
// set if the store hasn't populated yet.
onMount(() => {
	notificationStore.init();
});

onDestroy(() => {
	notificationStore.destroy();
});

const liveUnread = $derived<NotificationItem[]>(
	notificationStore.notifications.filter((n) => !n.read)
);

const seedUnread = $derived<NotificationItem[]>(
	data.unreadNotifications
		.filter((n): n is typeof n & { documentId: string; referenceId: string } =>
			Boolean(n.documentId && n.referenceId)
		)
		.map((n) => ({
			id: n.id,
			type: n.type,
			documentId: n.documentId,
			documentTitle: n.documentTitle ?? null,
			referenceId: n.referenceId,
			annotationId: n.annotationId ?? null,
			read: n.read,
			createdAt: n.createdAt,
			actorName: n.actorName ?? ''
		}))
);

// If the store has any data (including zero after mark-all), trust it;
// otherwise fall back to the SSR-seeded unread set.
const unread = $derived<NotificationItem[]>(
	notificationStore.notifications.length > 0 ? liveUnread : seedUnread
);

async function onFilterChange(v: FilterValue) {
	const url = new URL(page.url);
	if (v === 'alle') url.searchParams.delete('filter');
	else url.searchParams.set('filter', v);
	await goto(`${url.pathname}${url.search}`, {
		keepFocus: true,
		noScroll: true,
		replaceState: true
	});
}

const displayFeed = $derived(applyClientFilter(data.activityFeed, data.filter));

const isEmpty = $derived(displayFeed.length === 0);
const emptyVariant = $derived<'first-run' | 'filter-empty' | 'inbox-zero'>(
	data.activityFeed.length === 0 ? 'first-run' : 'filter-empty'
);

function retry() {
	location.reload();
}
</script>

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

<main class="mx-auto max-w-3xl px-4 py-8 sm:px-6 lg:px-8">
	<header class="mb-6 flex items-baseline justify-between">
		<h1 class="font-serif text-2xl text-ink">{m.chronik_page_title()}</h1>
	</header>

	{#if data.loadError === 'activity'}
		<ChronikErrorCard onRetry={retry} />
	{:else}
		<ChronikFuerDichBox
			unread={unread}
			optimisticMarkRead={notificationStore.optimisticMarkRead}
			optimisticMarkAllRead={notificationStore.optimisticMarkAllRead}
		/>

		<div class="mt-6">
			<ChronikFilterPills value={data.filter} onChange={onFilterChange} />
		</div>

		<div aria-live="polite" aria-atomic="false" aria-busy={!!navigating.type}>
			{#if isEmpty}
				<div class="mt-8">
					<ChronikEmptyState variant={emptyVariant} />
				</div>
			{:else}
				<ChronikTimeline items={displayFeed} />
			{/if}
		</div>
	{/if}
</main>
