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

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

const makeNode = () => ({
	id: 'person-1',
	displayName: 'Alice Müller',
	birthYear: 1900,
	deathYear: undefined,
	familyMember: true
});

function stubFetch(directRels: unknown[] = [], inferredRels: unknown[] = []) {
	let callCount = 0;
	vi.stubGlobal(
		'fetch',
		vi.fn().mockImplementation(() => {
			callCount++;
			const body = callCount % 2 === 1 ? directRels : inferredRels;
			return Promise.resolve({ ok: true, json: () => Promise.resolve(body) });
		})
	);
}

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

describe('StammbaumSidePanel', () => {
	it('calls onClose when dismiss button is clicked', async () => {
		const onClose = vi.fn();
		render(StammbaumSidePanel, { node: makeNode(), onClose, canWrite: false });
		await expect.element(page.getByRole('button', { name: 'Schließen' })).toBeInTheDocument();
		const btn = [...document.querySelectorAll<HTMLButtonElement>('button')].find(
			(b) => b.getAttribute('aria-label') === 'Schließen'
		);
		btn!.click();
		expect(onClose).toHaveBeenCalledOnce();
	});

	it('renders the person displayName as heading', async () => {
		render(StammbaumSidePanel, { node: makeNode(), onClose: vi.fn(), canWrite: false });
		await expect.element(page.getByText('Alice Müller')).toBeInTheDocument();
	});

	it('hides the centre control when onCentre is not provided', async () => {
		render(StammbaumSidePanel, { node: makeNode(), onClose: vi.fn(), canWrite: false });
		await expect
			.element(page.getByRole('button', { name: 'Auf diese Person zentrieren' }))
			.not.toBeInTheDocument();
	});

	it('calls onCentre when the centre control is clicked (US-PAN-005)', async () => {
		const onCentre = vi.fn();
		render(StammbaumSidePanel, { node: makeNode(), onClose: vi.fn(), onCentre, canWrite: false });
		const btn = [...document.querySelectorAll<HTMLButtonElement>('button')].find(
			(b) => b.getAttribute('aria-label') === 'Auf diese Person zentrieren'
		);
		btn!.click();
		expect(onCentre).toHaveBeenCalledOnce();
	});

	it('shows empty-relationships message when no direct relationships are loaded', async () => {
		render(StammbaumSidePanel, { node: makeNode(), onClose: vi.fn(), canWrite: false });
		await expect.element(page.getByText('Noch keine Beziehungen bekannt.')).toBeInTheDocument();
	});

	it('date inputs inside the add form have accessible labels (canWrite=true)', async () => {
		render(StammbaumSidePanel, { node: makeNode(), onClose: vi.fn(), canWrite: true });
		await expect.element(page.getByText('Noch keine Beziehungen bekannt.')).toBeInTheDocument();
		const addBtn = [...document.querySelectorAll<HTMLButtonElement>('button')].find((b) =>
			/Beziehung hinzufügen/i.test(b.textContent ?? '')
		);
		addBtn!.click();
		await expect.element(page.getByRole('combobox', { name: 'Typ' })).toBeInTheDocument();
		const dateInputs = [...document.querySelectorAll('input')].filter(
			(i) => i.inputMode === 'numeric'
		);
		expect(dateInputs.length).toBeGreaterThan(0);
		for (const input of dateInputs) {
			expect(input.getAttribute('aria-label')).toBeTruthy();
		}
	});

	it('shows loading indicator while fetching', async () => {
		let resolveFirst: (v: unknown) => void;
		vi.stubGlobal(
			'fetch',
			vi.fn().mockImplementation(
				() =>
					new Promise((resolve) => {
						resolveFirst = resolve;
					})
			)
		);
		render(StammbaumSidePanel, { node: makeNode(), onClose: vi.fn(), canWrite: false });
		await expect.element(page.getByText('…')).toBeInTheDocument();
		resolveFirst!({ ok: true, json: () => Promise.resolve([]) });
	});
});
