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

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

const PERSONS = [
	{
		id: '1',
		firstName: 'Max',
		lastName: 'Mustermann',
		displayName: 'Max Mustermann',
		personType: 'PERSON',
		familyMember: false
	},
	{
		id: '2',
		firstName: 'Anna',
		lastName: 'Musterfrau',
		displayName: 'Anna Musterfrau',
		personType: 'PERSON',
		familyMember: false
	},
	{
		id: '3',
		firstName: 'Karl',
		lastName: 'König',
		displayName: 'Karl König',
		personType: 'PERSON',
		familyMember: false
	}
];

function mockFetch(persons = PERSONS) {
	// /api/persons now returns a paged { items } envelope.
	vi.stubGlobal(
		'fetch',
		vi.fn().mockResolvedValue({
			ok: true,
			json: vi.fn().mockResolvedValue({ items: persons })
		})
	);
}

function receiverInputs() {
	return Array.from(
		document.querySelectorAll<HTMLInputElement>('input[type="hidden"][name="receiverIds"]')
	);
}

afterEach(() => {
	cleanup();
	vi.unstubAllGlobals();
});

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

describe('PersonMultiSelect – rendering', () => {
	it('renders the text input with placeholder when no persons selected', async () => {
		render(PersonMultiSelect, { selectedPersons: [] });
		await expect.element(page.getByPlaceholder('Namen tippen...')).toBeInTheDocument();
		await page.screenshot({ path: 'test-results/screenshots/person-multiselect-empty.png' });
	});

	it('renders pre-selected persons as chips', async () => {
		render(PersonMultiSelect, {
			selectedPersons: [
				{
					id: '1',
					firstName: 'Max',
					lastName: 'Mustermann',
					displayName: 'Max Mustermann',
					personType: 'PERSON',
					familyMember: false
				},
				{
					id: '2',
					firstName: 'Anna',
					lastName: 'Musterfrau',
					displayName: 'Anna Musterfrau',
					personType: 'PERSON',
					familyMember: false
				}
			]
		});
		await expect.element(page.getByText('Max Mustermann')).toBeInTheDocument();
		await expect.element(page.getByText('Anna Musterfrau')).toBeInTheDocument();
		await page.screenshot({ path: 'test-results/screenshots/person-multiselect-with-chips.png' });
	});

	it('renders hidden inputs for each selected person', async () => {
		render(PersonMultiSelect, {
			selectedPersons: [
				{
					id: '1',
					firstName: 'Max',
					lastName: 'Mustermann',
					displayName: 'Max Mustermann',
					personType: 'PERSON',
					familyMember: false
				},
				{
					id: '2',
					firstName: 'Anna',
					lastName: 'Musterfrau',
					displayName: 'Anna Musterfrau',
					personType: 'PERSON',
					familyMember: false
				}
			]
		});
		await tick();
		const inputs = receiverInputs();
		expect(inputs).toHaveLength(2);
		expect(inputs[0].value).toBe('1');
		expect(inputs[1].value).toBe('2');
	});

	it('hides the placeholder when persons are selected', async () => {
		render(PersonMultiSelect, {
			selectedPersons: [
				{
					id: '1',
					firstName: 'Max',
					lastName: 'Mustermann',
					displayName: 'Max Mustermann',
					personType: 'PERSON',
					familyMember: false
				}
			]
		});
		await expect.element(page.getByPlaceholder('Namen tippen...')).not.toBeInTheDocument();
	});
});

// ─── Selecting persons ────────────────────────────────────────────────────────

describe('PersonMultiSelect – selecting persons', () => {
	it('adds a person chip on result click', async () => {
		mockFetch();
		render(PersonMultiSelect, { selectedPersons: [] });
		const input = page.getByRole('textbox');
		await input.fill('Mu');
		await waitForDebounce();
		((await page.getByRole('button', { name: 'Max Mustermann' }).element()) as HTMLElement).click();
		await expect.element(page.getByText('Max Mustermann')).toBeInTheDocument();
		await expect.element(input).toHaveValue('');
		await page.screenshot({
			path: 'test-results/screenshots/person-multiselect-one-selected.png'
		});
	});

	it('can select multiple persons sequentially', async () => {
		mockFetch();
		render(PersonMultiSelect, { selectedPersons: [] });
		const input = page.getByRole('textbox');

		await input.fill('Mu');
		await waitForDebounce();
		((await page.getByRole('button', { name: 'Max Mustermann' }).element()) as HTMLElement).click();

		await input.fill('Mu');
		await waitForDebounce();
		(
			(await page.getByRole('button', { name: 'Anna Musterfrau' }).element()) as HTMLElement
		).click();

		await expect.element(page.getByText('Max Mustermann')).toBeInTheDocument();
		await expect.element(page.getByText('Anna Musterfrau')).toBeInTheDocument();
		await page.screenshot({
			path: 'test-results/screenshots/person-multiselect-two-selected.png'
		});
	});

	it('filters already-selected persons from search results', async () => {
		mockFetch();
		render(PersonMultiSelect, {
			selectedPersons: [
				{
					id: '1',
					firstName: 'Max',
					lastName: 'Mustermann',
					displayName: 'Max Mustermann',
					personType: 'PERSON',
					familyMember: false
				}
			]
		});
		const input = page.getByRole('textbox');
		await input.fill('Mu');
		await waitForDebounce();
		// Chip still shows "Max Mustermann" but the dropdown item (role=button) must be filtered out
		await expect
			.element(page.getByRole('button', { name: 'Max Mustermann' }))
			.not.toBeInTheDocument();
		await expect.element(page.getByRole('button', { name: 'Anna Musterfrau' })).toBeInTheDocument();
	});

	it('selects a result with Enter key', async () => {
		mockFetch([
			{
				id: '1',
				firstName: 'Max',
				lastName: 'Mustermann',
				displayName: 'Max Mustermann',
				personType: 'PERSON',
				familyMember: false
			}
		]);
		render(PersonMultiSelect, { selectedPersons: [] });
		const input = page.getByRole('textbox');
		await input.fill('Ma');
		await waitForDebounce();
		const resultEl = (await page
			.getByRole('button', { name: 'Max Mustermann' })
			.element()) as HTMLElement;
		resultEl.dispatchEvent(
			new KeyboardEvent('keydown', { key: 'Enter', bubbles: true, cancelable: true })
		);
		await tick();
		await expect.element(page.getByText('Max Mustermann')).toBeInTheDocument();
	});
});

// ─── Removing persons ─────────────────────────────────────────────────────────

describe('PersonMultiSelect – removing persons', () => {
	it('removes a chip when its × button is clicked', async () => {
		render(PersonMultiSelect, {
			selectedPersons: [
				{
					id: '1',
					firstName: 'Max',
					lastName: 'Mustermann',
					displayName: 'Max Mustermann',
					personType: 'PERSON',
					familyMember: false
				},
				{
					id: '2',
					firstName: 'Anna',
					lastName: 'Musterfrau',
					displayName: 'Anna Musterfrau',
					personType: 'PERSON',
					familyMember: false
				}
			]
		});
		// Buttons have aria-label="Entfernen"
		const removeBtn = (await page
			.getByRole('button', { name: 'Entfernen' })
			.first()
			.element()) as HTMLElement;
		removeBtn.click();
		await expect.element(page.getByText('Max Mustermann')).not.toBeInTheDocument();
		await expect.element(page.getByText('Anna Musterfrau')).toBeInTheDocument();
	});

	// REQ-017 (#781): chip remove targets must be ≥44px for the 60+ audience.
	it('renders a ≥44px touch target on the chip remove button', async () => {
		render(PersonMultiSelect, {
			selectedPersons: [{ id: '1', displayName: 'Max Mustermann' }]
		});
		const removeBtn = (await page
			.getByRole('button', { name: 'Entfernen' })
			.first()
			.element()) as HTMLElement;
		expect(removeBtn.className).toContain('min-h-[44px]');
		expect(removeBtn.className).toContain('min-w-[44px]');
	});

	it('removes the corresponding hidden input when a chip is removed', async () => {
		render(PersonMultiSelect, {
			selectedPersons: [
				{
					id: '1',
					firstName: 'Max',
					lastName: 'Mustermann',
					displayName: 'Max Mustermann',
					personType: 'PERSON',
					familyMember: false
				},
				{
					id: '2',
					firstName: 'Anna',
					lastName: 'Musterfrau',
					displayName: 'Anna Musterfrau',
					personType: 'PERSON',
					familyMember: false
				}
			]
		});
		(
			(await page.getByRole('button', { name: 'Entfernen' }).first().element()) as HTMLElement
		).click();
		await tick();
		const inputs = receiverInputs();
		expect(inputs).toHaveLength(1);
		expect(inputs[0].value).toBe('2');
	});
});

// ─── Click outside ────────────────────────────────────────────────────────────

describe('PersonMultiSelect – click outside', () => {
	it('closes the dropdown when clicking outside', async () => {
		mockFetch();
		render(PersonMultiSelect, { selectedPersons: [] });
		const input = page.getByRole('textbox');
		await input.fill('Mu');
		await waitForDebounce();
		await expect.element(page.getByText('Max Mustermann')).toBeInTheDocument();
		document.body.click();
		await tick();
		await expect.element(page.getByText('Max Mustermann')).not.toBeInTheDocument();
	});
});
