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

afterEach(cleanup);

const personPersonal = {
	id: 'p1',
	personType: 'PERSON',
	title: 'Frau Dr.',
	firstName: 'Anna',
	lastName: 'Schmidt',
	alias: 'Anni',
	birthDate: '1899-03-14' as string | null,
	birthDatePrecision: 'DAY' as string | null,
	deathDate: '1972-01-01' as string | null,
	deathDatePrecision: 'YEAR' as string | null,
	notes: 'Wohnte in Berlin.'
};

const personInstitution = {
	id: 'p2',
	personType: 'INSTITUTION',
	title: null,
	firstName: null,
	lastName: 'Acme GmbH',
	alias: null,
	birthDate: null,
	birthDatePrecision: null,
	deathDate: null,
	deathDatePrecision: null,
	notes: null
};

describe('PersonEditForm', () => {
	it('renders the firstName input for the PERSON personType', async () => {
		render(PersonEditForm, { props: { person: personPersonal } });

		await expect.element(page.getByLabelText(/vorname/i)).toBeVisible();
	});

	it('renders the title input only for the PERSON personType', async () => {
		render(PersonEditForm, { props: { person: personPersonal } });

		await expect.element(page.getByLabelText(/titel/i)).toBeVisible();
	});

	it('hides the firstName / title / alias / life-date fields for INSTITUTION', async () => {
		render(PersonEditForm, { props: { person: personInstitution } });

		await expect.element(page.getByLabelText(/vorname/i)).not.toBeInTheDocument();
		await expect.element(page.getByLabelText(/^titel$/i)).not.toBeInTheDocument();
		await expect.element(page.getByLabelText(/rufname/i)).not.toBeInTheDocument();
		await expect.element(page.getByLabelText(/^geburtsdatum$/i)).not.toBeInTheDocument();
		await expect.element(page.getByLabelText(/^sterbedatum$/i)).not.toBeInTheDocument();
	});

	it('uses the "Nachname" label for PERSON', async () => {
		render(PersonEditForm, { props: { person: personPersonal } });

		await expect.element(page.getByLabelText(/nachname \*/i)).toBeVisible();
	});

	it('uses the "Name" label for INSTITUTION', async () => {
		render(PersonEditForm, { props: { person: personInstitution } });

		await expect.element(page.getByLabelText(/^name \*$/i)).toBeVisible();
	});

	it('hydrates inputs from the person prop', async () => {
		render(PersonEditForm, { props: { person: personPersonal } });

		const firstName = (await page.getByLabelText(/vorname/i).element()) as HTMLInputElement;
		const lastName = (await page.getByLabelText(/nachname/i).element()) as HTMLInputElement;
		const alias = (await page.getByLabelText(/rufname/i).element()) as HTMLInputElement;
		const title = (await page.getByLabelText(/^titel/i).element()) as HTMLInputElement;
		expect(firstName.value).toBe('Anna');
		expect(lastName.value).toBe('Schmidt');
		expect(alias.value).toBe('Anni');
		expect(title.value).toBe('Frau Dr.');
	});

	it('renders an existing DAY-precision birth date as dd.mm.yyyy in the date input', async () => {
		render(PersonEditForm, { props: { person: personPersonal } });

		const birthInput = (await page.getByLabelText(/^geburtsdatum$/i).element()) as HTMLInputElement;
		expect(birthInput.value).toBe('14.03.1899');
	});

	it('submits the ISO date via the hidden birthDate input', async () => {
		const { container } = render(PersonEditForm, { props: { person: personPersonal } });

		const hidden = container.querySelector('input[type="hidden"][name="birthDate"]');
		expect((hidden as HTMLInputElement).value).toBe('1899-03-14');
	});

	it('offers only DAY / MONTH / YEAR precisions for life dates', async () => {
		const { container } = render(PersonEditForm, { props: { person: personPersonal } });

		const select = container.querySelector(
			'select[name="birthDatePrecision"]'
		) as HTMLSelectElement;
		const values = Array.from(select.options).map((o) => o.value);
		expect(values).toEqual(['DAY', 'MONTH', 'YEAR']);
	});

	it('hydrates the precision selects from the person prop', async () => {
		const { container } = render(PersonEditForm, { props: { person: personPersonal } });

		const birth = container.querySelector('select[name="birthDatePrecision"]') as HTMLSelectElement;
		const death = container.querySelector('select[name="deathDatePrecision"]') as HTMLSelectElement;
		expect(birth.value).toBe('DAY');
		expect(death.value).toBe('YEAR');
	});

	it('seeds a non-form precision (APPROX legacy import) as YEAR instead of DAY', async () => {
		const { container } = render(PersonEditForm, {
			props: {
				person: { ...personPersonal, birthDate: '1899-01-01', birthDatePrecision: 'APPROX' }
			}
		});

		const birth = container.querySelector('select[name="birthDatePrecision"]') as HTMLSelectElement;
		expect(birth.value).toBe('YEAR');
	});

	it('stacks the date input and precision select without overflow at 320px', async () => {
		await page.viewport(320, 640);
		const { container } = render(PersonEditForm, { props: { person: personPersonal } });

		const input = container.querySelector('#birthDate') as HTMLElement;
		const select = container.querySelector('select[name="birthDatePrecision"]') as HTMLElement;
		expect(input.getBoundingClientRect().right).toBeLessThanOrEqual(320);
		expect(select.getBoundingClientRect().right).toBeLessThanOrEqual(320);
		// Stacked, not side by side: the select starts below the input.
		expect(select.getBoundingClientRect().top).toBeGreaterThanOrEqual(
			input.getBoundingClientRect().bottom
		);
		await page.viewport(1280, 720);
	});

	it('renders the notes textarea pre-filled with prior content', async () => {
		render(PersonEditForm, { props: { person: personPersonal } });

		const notes = (await page.getByLabelText(/notizen/i).element()) as HTMLTextAreaElement;
		expect(notes.value).toBe('Wohnte in Berlin.');
	});

	it('falls back to PERSON when an unknown personType is supplied', async () => {
		render(PersonEditForm, {
			props: { person: { ...personPersonal, personType: 'NOT_A_TYPE' } }
		});

		await expect.element(page.getByLabelText(/vorname/i)).toBeVisible();
	});

	it('renders empty inputs when nullable fields are null', async () => {
		render(PersonEditForm, {
			props: {
				person: {
					...personPersonal,
					title: null,
					alias: null,
					birthDate: null,
					birthDatePrecision: null
				}
			}
		});

		const title = (await page.getByLabelText(/^titel/i).element()) as HTMLInputElement;
		const alias = (await page.getByLabelText(/rufname/i).element()) as HTMLInputElement;
		const birthInput = (await page.getByLabelText(/^geburtsdatum$/i).element()) as HTMLInputElement;
		expect(title.value).toBe('');
		expect(alias.value).toBe('');
		expect(birthInput.value).toBe('');
	});

	// ─── generation dropdown (#689) ─────────────────────────────────────────────

	it('renders the generation select with G 0…G 6 options when personType is PERSON', async () => {
		render(PersonEditForm, { props: { person: personPersonal } });

		const select = (await page.getByLabelText(/^generation$/i).element()) as HTMLSelectElement;
		const labels = Array.from(select.options).map((o) => o.label.trim());
		expect(labels).toEqual(
			expect.arrayContaining(['G 0', 'G 1', 'G 2', 'G 3', 'G 4', 'G 5', 'G 6'])
		);
	});

	it('hides the generation select for INSTITUTION', async () => {
		render(PersonEditForm, { props: { person: personInstitution } });

		await expect.element(page.getByLabelText(/^generation$/i)).not.toBeInTheDocument();
	});

	it('hydrates the generation select from person.generation', async () => {
		render(PersonEditForm, {
			props: {
				person: { ...personPersonal, generation: 3 } as typeof personPersonal & {
					generation: number;
				}
			}
		});

		const select = (await page.getByLabelText(/^generation$/i).element()) as HTMLSelectElement;
		expect(select.value).toBe('3');
	});

	it('hydrates the generation select to "" when person.generation is null', async () => {
		render(PersonEditForm, {
			props: {
				person: { ...personPersonal, generation: null } as typeof personPersonal & {
					generation: number | null;
				}
			}
		});

		const select = (await page.getByLabelText(/^generation$/i).element()) as HTMLSelectElement;
		expect(select.value).toBe('');
	});

	// ─── partial-date guard (#812 review) ────────────────────────────────────────

	it('blocks submission while a stored birth date is partially edited (no silent clear)', async () => {
		render(PersonEditForm, { props: { person: personPersonal } });

		await userEvent.fill(page.getByLabelText(/^geburtsdatum$/i), '14.03.');

		const birthInput = (await page.getByLabelText(/^geburtsdatum$/i).element()) as HTMLInputElement;
		expect(birthInput.checkValidity()).toBe(false);
		await expect.element(page.getByText(/Bitte im Format TT\.MM\.JJJJ/)).toBeVisible();
	});
});
