import { describe, it, expect, afterEach } from 'vitest';
import { cleanup, render } from 'vitest-browser-svelte';
import { page } from 'vitest/browser';
import { m } from '$lib/paraglide/messages.js';
import type { components } from '$lib/generated/api';

const { default: JourneyItemCard } = await import('./JourneyItemCard.svelte');

afterEach(cleanup);

declare global {
	interface Window {
		__xss_note?: number;
	}
}

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

const baseItem = (overrides: Partial<JourneyItemView> = {}): JourneyItemView => ({
	id: 'item1',
	position: 0,
	document: {
		id: 'd1',
		title: 'Brief an Helene',
		documentDate: '1923-05-15',
		datePrecision: 'DAY',
		receiverCount: 0
	},
	...overrides
});

describe('JourneyItemCard', () => {
	it('renders the document title', async () => {
		render(JourneyItemCard, { props: { item: baseItem() } });

		await expect.element(page.getByText('Brief an Helene')).toBeVisible();
	});

	it('renders the document date in the meta line when documentDate is present', async () => {
		render(JourneyItemCard, { props: { item: baseItem() } });

		await expect.element(page.getByText(/1923/)).toBeVisible();
	});

	it('"Brief öffnen" link points to /documents/:id', async () => {
		render(JourneyItemCard, { props: { item: baseItem() } });

		const link = page.getByRole('link', { name: /öffnen/i });
		await expect.element(link).toBeInTheDocument();
		const el = await link.element();
		expect(el.getAttribute('href')).toContain('/documents/d1');
	});

	it('"Brief öffnen" link meets the 44px touch-target floor', async () => {
		// Primary tap action of the phone read path — WCAG 2.5.5 / house rule.
		render(JourneyItemCard, { props: { item: baseItem() } });

		const link = page.getByRole('link', { name: /öffnen/i });
		await expect.element(link).toBeInTheDocument();
		const height = link.element().getBoundingClientRect().height;
		expect(height).toBeGreaterThanOrEqual(44);
	});

	it('"Brief öffnen" link has dated aria-label when documentDate is present', async () => {
		render(JourneyItemCard, { props: { item: baseItem() } });

		const link = page.getByRole('link', { name: /1923/i });
		await expect.element(link).toBeInTheDocument();
	});

	it('"Brief öffnen" link has undated aria-label when documentDate is absent', async () => {
		render(JourneyItemCard, {
			props: {
				item: baseItem({
					document: { id: 'd2', title: 'Ohne Datum', datePrecision: 'UNKNOWN', receiverCount: 0 }
				})
			}
		});

		const link = page.getByRole('link', { name: m.journey_item_open_aria_undated() });
		await expect.element(link).toBeInTheDocument();
	});

	it('omits date from meta line when documentDate is absent', async () => {
		render(JourneyItemCard, {
			props: {
				item: baseItem({
					document: { id: 'd2', title: 'Ohne Datum', datePrecision: 'UNKNOWN', receiverCount: 0 }
				})
			}
		});

		await expect.element(page.getByText(/1923/)).not.toBeInTheDocument();
	});

	it('renders sender→receiver in meta line when both are present', async () => {
		render(JourneyItemCard, {
			props: {
				item: baseItem({
					document: {
						id: 'd1',
						title: 'Brief an Helene',
						documentDate: '1923-05-15',
						datePrecision: 'DAY',
						senderName: 'Franz Raddatz',
						receiverName: 'Emma Müller',
						receiverCount: 1
					}
				})
			}
		});

		await expect.element(page.getByText(/Franz Raddatz/)).toBeVisible();
		await expect.element(page.getByText(/Emma Müller/)).toBeVisible();
	});

	it('renders note as annotation block when note is present', async () => {
		render(JourneyItemCard, { props: { item: baseItem({ note: 'Ein wichtiger Brief' }) } });

		await expect.element(page.getByText('Ein wichtiger Brief')).toBeVisible();
	});

	it('annotation block uses the brand mint accent border (#798)', async () => {
		render(JourneyItemCard, { props: { item: baseItem({ note: 'Ein wichtiger Brief' }) } });

		const note = document.querySelector('[class*="border-l-2"]');
		expect(note).not.toBeNull();
		expect(note!.className).toContain('border-brand-mint');
	});

	it('card uses the surface token, not bg-white, so dark mode remaps it', async () => {
		render(JourneyItemCard, { props: { item: baseItem() } });

		const card = document.querySelector('[class*="border-line"]');
		expect(card).not.toBeNull();
		expect(card!.className).toContain('bg-surface');
		expect(card!.className).not.toContain('bg-white');
	});

	it('annotation block is tinted with bg-muted to stand off the white card', async () => {
		render(JourneyItemCard, { props: { item: baseItem({ note: 'Ein wichtiger Brief' }) } });

		const note = document.querySelector('[class*="border-l-2"]');
		expect(note!.className).toContain('bg-muted');
	});

	it('reading text sizes meet the accessibility floor (#800)', async () => {
		render(JourneyItemCard, { props: { item: baseItem({ note: 'Ein wichtiger Brief' }) } });

		const title = page.getByText('Brief an Helene');
		expect((await title.element()).className).toContain('text-base');

		const link = await page.getByRole('link', { name: /öffnen/i }).element();
		expect(link.className).toContain('text-sm');
		expect(link.className).not.toContain('text-xs');

		const noteText = document.querySelector('[class*="border-l-2"] p');
		expect(noteText!.className).toContain('text-base');
		expect(noteText!.className).not.toContain('text-xs');
	});

	it('omits annotation block when note is blank or whitespace', async () => {
		render(JourneyItemCard, { props: { item: baseItem({ note: '   ' }) } });

		await expect.element(page.getByText(/ {3}/)).not.toBeInTheDocument();
	});

	it('omits annotation block when note is absent', async () => {
		render(JourneyItemCard, { props: { item: baseItem({ note: undefined }) } });

		const notes = document.querySelectorAll('[class*="border-mint"]');
		expect(notes.length).toBe(0);
	});

	it('XSS: note is rendered as plaintext — injected payload does not execute', async () => {
		// Note uses Svelte text interpolation ({item.note}), NOT {@html}.
		render(JourneyItemCard, {
			props: {
				item: baseItem({
					note: '<img src=x onerror="window.__xss_note=1">'
				})
			}
		});

		expect(window.__xss_note).toBeUndefined();
		expect(document.body.textContent).toContain('<img src=x onerror=');
	});
});
