import { afterEach, describe, expect, it, vi } from 'vitest';
import { cleanup, render } from 'vitest-browser-svelte';
import { page } from 'vitest/browser';
import Page from './+page.svelte';

const tick = () => new Promise((r) => setTimeout(r, 0));

vi.mock('$app/navigation', () => ({ goto: vi.fn() }));
vi.mock('$app/state', () => ({ page: { url: new URL('http://localhost/persons') } }));

const makePerson = (overrides = {}) => ({
	id: '1',
	firstName: 'Max',
	lastName: 'Mustermann',
	displayName: 'Max Mustermann',
	documentCount: 0,
	provisional: false,
	personType: 'PERSON',
	...overrides
});

const defaultStats = { totalPersons: 0, totalDocuments: 0 };
const emptyData = {
	user: undefined,
	canWrite: true,
	canAnnotate: false,
	canBlogWrite: false,
	q: '',
	persons: [],
	stats: defaultStats,
	totalElements: 0,
	totalPages: 0,
	pageNumber: 0,
	pageSize: 50,
	filters: { type: undefined, familyOnly: false, hasDocuments: false, review: false },
	needsReviewCount: 0
};
const dataWithPersons = {
	...emptyData,
	persons: [makePerson()],
	stats: { totalPersons: 1, totalDocuments: 3 }
};

afterEach(cleanup);

// ─── Rendering ────────────────────────────────────────────────────────────────

describe('Persons page – rendering', () => {
	it('renders the search input', async () => {
		render(Page, { data: emptyData });
		await expect.element(page.getByRole('textbox')).toBeInTheDocument();
	});

	it('pre-fills the search input from data.q', async () => {
		render(Page, { data: { ...emptyData, q: 'Müller' } });
		await expect.element(page.getByRole('textbox')).toHaveValue('Müller');
	});

	it('shows empty state when no persons', async () => {
		render(Page, { data: emptyData });
		await expect.element(page.getByText('Keine Personen gefunden')).toBeInTheDocument();
	});

	it('renders person cards', async () => {
		render(Page, { data: dataWithPersons });
		await expect.element(page.getByText('Max Mustermann')).toBeInTheDocument();
	});

	it('links person card to detail page', async () => {
		render(Page, { data: dataWithPersons });
		await expect
			.element(page.getByRole('link', { name: /Max Mustermann/ }))
			.toHaveAttribute('href', '/persons/1');
	});

	it('shows alias in italic when provided', async () => {
		render(Page, { data: { ...emptyData, persons: [makePerson({ alias: 'Maxi' })] } });
		await expect.element(page.getByText('„Maxi"')).toBeInTheDocument();
	});

	it('shows life date range when birthYear is provided', async () => {
		render(Page, { data: { ...emptyData, persons: [makePerson({ birthYear: 1900 })] } });
		await expect.element(page.getByText('* 1900')).toBeInTheDocument();
	});

	it('shows stats bar with person and document counts', async () => {
		render(Page, { data: dataWithPersons });
		await expect.element(page.getByText(/1 Person/)).toBeInTheDocument();
		await expect.element(page.getByText(/3 Dokumente/)).toBeInTheDocument();
	});
});

// ─── Keystroke preservation (issue #34) ──────────────────────────────────────

describe('Persons page – search input keystroke preservation', () => {
	it('does not overwrite the search input while the user is focused and stale data arrives', async () => {
		const { rerender } = render(Page, { data: emptyData });

		const input = page.getByRole('textbox');

		// User types "abc" — input is focused
		await input.click();
		await input.fill('abc');

		// Simulate a navigation completing with stale data (q='a') while the user is still typing
		await rerender({ data: { ...emptyData, q: 'a' } });
		await tick();

		// Input must still show what the user typed, not the stale URL value
		await expect.element(input).toHaveValue('abc');
	});
});
