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

vi.mock('$app/forms', () => ({ enhance: () => () => {} }));
vi.mock('$lib/person/PersonTypeahead.svelte', () => ({ default: () => null }));

afterEach(cleanup);

const PID = 'person-1';
const OTHER = 'person-2';

const editRel = () => ({
	id: 'rel-9',
	personId: PID,
	relatedPersonId: OTHER,
	personDisplayName: 'Anna Müller',
	relatedPersonDisplayName: 'Hans Müller',
	relationType: 'SPOUSE_OF' as const,
	fromDate: '1923-05-12',
	fromDatePrecision: 'DAY' as const,
	toDatePrecision: 'UNKNOWN' as const,
	notes: 'Hochzeit in Berlin'
});

describe('AddRelationshipForm — create mode', () => {
	it('shows the add-relationship toggle initially and no form', async () => {
		render(AddRelationshipForm, { personId: PID });
		await expect.element(page.getByRole('button')).toBeInTheDocument();
		expect(document.querySelector('select[name="relationType"]')).toBeNull();
	});

	it('shows the relationType select when the add toggle is clicked', async () => {
		render(AddRelationshipForm, { personId: PID });
		document.querySelector<HTMLButtonElement>('button')!.click();
		await expect.element(page.getByRole('combobox', { name: 'Typ' })).toBeInTheDocument();
	});

	it('hides the form and shows the toggle again on cancel', async () => {
		render(AddRelationshipForm, { personId: PID });
		document.querySelector<HTMLButtonElement>('button')!.click();
		await expect.element(page.getByRole('combobox', { name: 'Typ' })).toBeInTheDocument();
		const cancelBtn = [...document.querySelectorAll<HTMLButtonElement>('button')].find(
			(b) => b.type === 'button' && /abbrechen/i.test(b.textContent ?? '')
		);
		cancelBtn!.click();
		await vi.waitFor(() =>
			expect(document.querySelector('select[name="relationType"]')).toBeNull()
		);
	});

	it('disables submit when no person is selected', async () => {
		render(AddRelationshipForm, { personId: PID });
		document.querySelector<HTMLButtonElement>('button')!.click();
		await expect.element(page.getByRole('button', { name: /^Hinzufügen$/i })).toBeDisabled();
	});

	it('has no server action when an onSubmit prop is provided', async () => {
		const onSubmit = vi.fn().mockResolvedValue(undefined);
		render(AddRelationshipForm, { personId: PID, onSubmit });
		document.querySelector<HTMLButtonElement>('button')!.click();
		await expect.element(page.getByRole('combobox', { name: 'Typ' })).toBeInTheDocument();
		expect(document.querySelector('form')?.hasAttribute('action')).toBe(false);
	});
});

describe('AddRelationshipForm — edit mode', () => {
	it('opens pre-filled and labels the submit "Speichern"', async () => {
		render(AddRelationshipForm, { personId: PID, relationship: editRel() });
		await expect.element(page.getByRole('button', { name: /^Speichern$/i })).toBeInTheDocument();
	});

	it('pre-fills the from-date as dd.mm.yyyy', async () => {
		render(AddRelationshipForm, { personId: PID, relationship: editRel() });
		await expect.element(page.getByRole('combobox', { name: 'Typ' })).toBeInTheDocument();
		const fromInput = document.querySelector<HTMLInputElement>('#fromDate')!;
		await vi.waitFor(() => expect(fromInput.value).toBe('12.05.1923'));
	});

	it('round-trips the notes into the textarea', async () => {
		render(AddRelationshipForm, { personId: PID, relationship: editRel() });
		await expect.element(page.getByRole('combobox', { name: 'Typ' })).toBeInTheDocument();
		const notes = document.querySelector<HTMLTextAreaElement>('textarea[name="notes"]')!;
		await vi.waitFor(() => expect(notes.value).toBe('Hochzeit in Berlin'));
	});

	it('offers only DAY/MONTH/YEAR in each precision select', async () => {
		render(AddRelationshipForm, { personId: PID, relationship: editRel() });
		await expect.element(page.getByRole('combobox', { name: 'Typ' })).toBeInTheDocument();
		const options = [
			...document.querySelectorAll<HTMLOptionElement>('#fromDatePrecision option')
		].map((o) => o.value);
		expect(options).toEqual(['DAY', 'MONTH', 'YEAR']);
	});

	it('gives each date input an associated label (accessible name)', async () => {
		render(AddRelationshipForm, { personId: PID, relationship: editRel() });
		await expect.element(page.getByRole('combobox', { name: 'Typ' })).toBeInTheDocument();
		expect(document.querySelector('#fromDate')?.getAttribute('aria-label')).toBe('Beginn (Datum)');
		expect(document.querySelector('#toDate')?.getAttribute('aria-label')).toBe('Ende (Datum)');
	});

	it('disables the submit and shows a progress spinner while a submit is in flight', async () => {
		let resolve: () => void = () => {};
		const onSubmit = vi.fn(() => new Promise<void>((r) => (resolve = r)));
		render(AddRelationshipForm, { personId: PID, relationship: editRel(), onSubmit });

		const submit = await vi.waitFor(() => {
			const b = [...document.querySelectorAll<HTMLButtonElement>('button')].find(
				(x) => x.type === 'submit'
			);
			if (!b) throw new Error('submit not ready');
			return b;
		});
		submit.click();

		await expect.element(page.getByTestId('submit-spinner')).toBeInTheDocument();
		await vi.waitFor(() => expect(submit.disabled).toBe(true));
		expect(onSubmit).toHaveBeenCalledOnce();
		resolve();
	});
});
