import { describe, it, expect, afterEach } from 'vitest';
import { cleanup, render } from 'vitest-browser-svelte';
import { page } from 'vitest/browser';
import * as m from '$lib/paraglide/messages.js';
import Page from './+page.svelte';
import { makeEntry, makeYear, makeTimelineDTO } from '$lib/timeline/test-factories';

afterEach(() => cleanup());

const event = (title: string) =>
	makeEntry({
		kind: 'EVENT',
		derived: true,
		derivedType: 'BIRTH',
		title,
		senderName: '',
		receiverName: '',
		documentId: undefined
	});

// The route's PageData merges the layout's auth fields with the page load's
// timeline; the component only reads `timeline`, but the full shape keeps the
// test type-clean.
const pageData = (timeline: ReturnType<typeof makeTimelineDTO>) => ({
	user: undefined,
	canWrite: false,
	canAnnotate: false,
	canBlogWrite: false,
	timeline
});

describe('/zeitstrahl page', () => {
	it('wraps the timeline in a padded canvas surface, without an outer border (REQ-001)', () => {
		render(Page, {
			data: pageData(makeTimelineDTO({ years: [makeYear(1909, [makeEntry()])] }))
		});
		const canvas = document.querySelector('[data-testid="timeline-canvas"]');
		expect(canvas).not.toBeNull();
		expect(canvas?.classList.contains('bg-canvas')).toBe(true);
		// The outer border was dropped in review (the page is already bg-canvas).
		expect(canvas?.classList.contains('border')).toBe(false);
		// the timeline renders inside the surface
		expect(canvas?.querySelector('ol')).not.toBeNull();
	});

	it('renders the meta sub-line with range and counts, no grouping segment (REQ-011)', () => {
		const dto = makeTimelineDTO({
			years: [
				makeYear(1909, [
					makeEntry({ documentId: 'a' }),
					makeEntry({ documentId: 'b' }),
					event('Geburt')
				]),
				makeYear(1924, [makeEntry({ documentId: 'c' }), event('Tod')])
			]
		});
		render(Page, { data: pageData(dto) });
		const sub = document.querySelector('[data-testid="timeline-meta"]');
		expect(sub?.textContent).toContain('1909–1924');
		expect(sub?.textContent).toContain(m.timeline_letters_count({ count: 3 }));
		expect(sub?.textContent).toContain(m.timeline_events_count({ count: 2 }));
		// REQ-011: the toggle-free view drops the grouping meta segment.
		expect(sub?.textContent).not.toContain('Gruppierung');
	});

	it('omits the range segment when there are no year bands (REQ-002)', () => {
		render(Page, {
			data: pageData(makeTimelineDTO({ undated: [makeEntry({ documentId: 'u1' })] }))
		});
		const sub = document.querySelector('[data-testid="timeline-meta"]');
		expect(sub).not.toBeNull();
		expect(sub?.textContent).not.toContain('–');
		expect(sub?.textContent).toContain(m.timeline_letters_count_singular());
	});

	it('omits the entire sub-line for an empty timeline (REQ-002)', () => {
		render(Page, { data: pageData(makeTimelineDTO()) });
		expect(document.querySelector('[data-testid="timeline-meta"]')).toBeNull();
	});

	it('drops the letters segment instead of showing "0 Briefe" (REQ-002)', () => {
		render(Page, {
			data: pageData(makeTimelineDTO({ years: [makeYear(1914, [event('Geburt')])] }))
		});
		const sub = document.querySelector('[data-testid="timeline-meta"]');
		expect(sub).not.toBeNull();
		expect(sub?.textContent).not.toContain(m.timeline_letters_count({ count: 0 }));
		expect(sub?.textContent).not.toContain('Gruppierung');
	});

	it('drops the events segment instead of showing "0 Ereignisse" (REQ-002)', () => {
		render(Page, {
			data: pageData(makeTimelineDTO({ undated: [makeEntry({ documentId: 'u1' })] }))
		});
		const sub = document.querySelector('[data-testid="timeline-meta"]');
		expect(sub).not.toBeNull();
		expect(sub?.textContent).not.toContain(m.timeline_events_count({ count: 0 }));
	});

	it('uses singular count labels for exactly one letter and one event (REQ-002)', () => {
		render(Page, {
			data: pageData(
				makeTimelineDTO({
					years: [makeYear(1914, [makeEntry({ documentId: 'a' }), event('Geburt')])]
				})
			)
		});
		const sub = document.querySelector('[data-testid="timeline-meta"]');
		expect(sub?.textContent).toContain(m.timeline_letters_count_singular());
		expect(sub?.textContent).toContain(m.timeline_events_count_singular());
		// never the "1 Briefe"/"1 Ereignisse" plural forms for a count of one
		expect(sub?.textContent).not.toContain(m.timeline_letters_count({ count: 1 }));
		expect(sub?.textContent).not.toContain(m.timeline_events_count({ count: 1 }));
	});
});

describe('/zeitstrahl layer filter (#780)', () => {
	const letter = (title: string, documentId: string) => makeEntry({ documentId, title });
	const historical = (title: string) =>
		makeEntry({
			kind: 'EVENT',
			type: 'HISTORICAL',
			derived: false,
			eventId: 'h1',
			documentId: undefined,
			title,
			senderName: '',
			receiverName: ''
		});
	const personal = (title: string) =>
		makeEntry({
			kind: 'EVENT',
			type: 'PERSONAL',
			derived: false,
			eventId: 'p1',
			documentId: undefined,
			title,
			senderName: '',
			receiverName: ''
		});

	const mixed = () =>
		makeTimelineDTO({
			years: [
				makeYear(1915, [
					letter('Brief Eins', 'd1'),
					historical('Erster Weltkrieg'),
					personal('Umzug nach Berlin')
				])
			]
		});

	async function openBar() {
		await page.getByTestId('timeline-filter-trigger').click();
	}

	it('hides letter cards when the Letters layer is off and restores them, with no fetch (REQ-005/002)', async () => {
		render(Page, { data: pageData(mixed()) });
		await expect.element(page.getByText('Brief Eins')).toBeVisible();
		await openBar();
		await page.getByTestId('timeline-filter-letters').click();
		await expect.poll(() => page.getByText('Brief Eins').query()).toBeNull();
		await expect.element(page.getByText('Umzug nach Berlin')).toBeVisible();
		await page.getByTestId('timeline-filter-letters').click();
		await expect.element(page.getByText('Brief Eins')).toBeVisible();
	});

	it('hides historical event cards when the Historical layer is off (REQ-004)', async () => {
		render(Page, { data: pageData(mixed()) });
		await openBar();
		await page.getByTestId('timeline-filter-historical').click();
		await expect.poll(() => page.getByText('Erster Weltkrieg').query()).toBeNull();
		await expect.element(page.getByText('Brief Eins')).toBeVisible();
		await expect.element(page.getByText('Umzug nach Berlin')).toBeVisible();
	});

	it('hides personal event cards when the Personal layer is off (REQ-003)', async () => {
		render(Page, { data: pageData(mixed()) });
		await openBar();
		await page.getByTestId('timeline-filter-personal').click();
		await expect.poll(() => page.getByText('Umzug nach Berlin').query()).toBeNull();
		await expect.element(page.getByText('Brief Eins')).toBeVisible();
		await expect.element(page.getByText('Erster Weltkrieg')).toBeVisible();
	});

	it('shows the filtered-empty message + reset below the open bar when all layers are off (REQ-006)', async () => {
		render(Page, { data: pageData(mixed()) });
		await openBar();
		await page.getByTestId('timeline-filter-personal').click();
		await page.getByTestId('timeline-filter-historical').click();
		await page.getByTestId('timeline-filter-letters').click();
		await expect.element(page.getByText(m.timeline_filter_empty_state())).toBeVisible();
		await expect.element(page.getByTestId('timeline-filter-empty-reset')).toBeVisible();
		// the generic TimelineView empty state is never what shows for a filtered-empty view
		expect(page.getByText(m.timeline_empty_state()).query()).toBeNull();
		// the one-click reset restores every layer
		await page.getByTestId('timeline-filter-empty-reset').click();
		await expect.element(page.getByText('Brief Eins')).toBeVisible();
	});

	it('recomputes the meta-line counts from the filtered view, so a hidden layer drops out of the totals (#780, resolves D1)', async () => {
		render(Page, { data: pageData(mixed()) });
		const meta = page.getByTestId('timeline-meta');
		// all layers on → the one letter and the two events are counted
		await expect.element(meta).toHaveTextContent(m.timeline_letters_count_singular());
		await expect.element(meta).toHaveTextContent(m.timeline_events_count({ count: 2 }));

		await openBar();
		await page.getByTestId('timeline-filter-letters').click();

		// the hidden letter leaves the count instead of lingering as "1 Brief";
		// the event total is untouched
		await expect.element(meta).not.toHaveTextContent(m.timeline_letters_count_singular());
		await expect.element(meta).toHaveTextContent(m.timeline_events_count({ count: 2 }));
	});
});

describe('/zeitstrahl curator affordances (#842)', () => {
	const curated = (eventId: string) =>
		makeEntry({
			kind: 'EVENT',
			type: 'PERSONAL',
			derived: false,
			eventId,
			title: 'Auswanderung',
			senderName: '',
			receiverName: '',
			documentId: undefined
		});

	const withWrite = (timeline: ReturnType<typeof makeTimelineDTO>) => ({
		...pageData(timeline),
		canWrite: true
	});

	it('renders the add-event CTA in a wrapping header when the viewer can write (REQ-001)', () => {
		render(Page, { data: withWrite(makeTimelineDTO({ years: [makeYear(1909, [makeEntry()])] })) });
		const add = document.querySelector(
			'[data-testid="timeline-add-event"]'
		) as HTMLAnchorElement | null;
		expect(add).not.toBeNull();
		expect(add?.getAttribute('href')).toBe('/zeitstrahl/events/new');
		// The header wraps so the CTA drops below the heading at narrow widths (≤360px)
		// rather than overflowing — REQ-001.
		expect(add?.closest('header')?.classList.contains('flex-wrap')).toBe(true);
	});

	it('renders no add-event CTA when the viewer cannot write (REQ-002)', () => {
		render(Page, {
			data: pageData(makeTimelineDTO({ years: [makeYear(1909, [makeEntry()])] }))
		});
		expect(document.querySelector('[data-testid="timeline-add-event"]')).toBeNull();
	});

	it('threads canWrite to the timeline so a curator sees an event edit link (REQ-001/009)', () => {
		render(Page, {
			data: withWrite(makeTimelineDTO({ years: [makeYear(1924, [curated('p9')])] }))
		});
		expect(document.querySelector('a[href="/zeitstrahl/events/p9/edit"]')).not.toBeNull();
	});

	it('shows no event edit link to a reader (REQ-007)', () => {
		render(Page, {
			data: pageData(makeTimelineDTO({ years: [makeYear(1924, [curated('p9')])] }))
		});
		expect(document.querySelector('[data-testid="event-edit"]')).toBeNull();
	});
});
