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

vi.mock('$app/navigation', () => ({
	beforeNavigate: () => {},
	afterNavigate: () => {},
	goto: vi.fn(),
	invalidate: vi.fn(),
	invalidateAll: vi.fn(),
	preloadCode: vi.fn(),
	preloadData: vi.fn(),
	pushState: vi.fn(),
	replaceState: vi.fn(),
	disableScrollHandling: vi.fn(),
	onNavigate: () => () => {}
}));

afterEach(cleanup);

const baseNode = (overrides: Record<string, unknown> = {}) => ({
	id: 'p-1',
	displayName: 'Anna Schmidt',
	familyMember: true,
	...overrides
});

describe('StammbaumSidePanel', () => {
	let fetchSpy: ReturnType<typeof vi.spyOn>;

	beforeEach(() => {
		fetchSpy = vi.spyOn(globalThis, 'fetch').mockImplementation(async (url: RequestInfo | URL) => {
			const u = url.toString();
			if (u.includes('/relationships') && !u.includes('/inferred')) {
				return new Response('[]', { status: 200, headers: { 'Content-Type': 'application/json' } });
			}
			if (u.includes('/inferred-relationships')) {
				return new Response('[]', { status: 200, headers: { 'Content-Type': 'application/json' } });
			}
			return new Response('[]', { status: 200 });
		});
	});

	afterEach(() => {
		fetchSpy?.mockRestore();
	});

	it('renders the heading from node displayName', async () => {
		render(StammbaumSidePanel, {
			props: { node: baseNode(), onClose: () => {} }
		});

		await expect.element(page.getByRole('heading', { name: 'Anna Schmidt' })).toBeVisible();
	});

	it('shows birth/death years when set', async () => {
		render(StammbaumSidePanel, {
			props: {
				node: baseNode({ birthYear: 1899, deathYear: 1950 }),
				onClose: () => {}
			}
		});

		expect(document.body.textContent).toContain('1899');
		expect(document.body.textContent).toContain('1950');
	});

	it('renders ?– when birthYear is missing but deathYear is set', async () => {
		render(StammbaumSidePanel, {
			props: { node: baseNode({ deathYear: 1950 }), onClose: () => {} }
		});

		expect(document.body.textContent).toMatch(/\?–/);
	});

	it('does not render the years line when both are missing', async () => {
		render(StammbaumSidePanel, {
			props: { node: baseNode(), onClose: () => {} }
		});

		expect(document.body.textContent).not.toMatch(/\?–\?/);
	});

	it('calls onClose when the close button is clicked', async () => {
		const onClose = vi.fn();
		render(StammbaumSidePanel, {
			props: { node: baseNode(), onClose }
		});

		const closeBtn = document.querySelector('button[aria-label]') as HTMLButtonElement;
		closeBtn.click();
		expect(onClose).toHaveBeenCalledOnce();
	});

	it('calls onClose when Escape is pressed on window', async () => {
		const onClose = vi.fn();
		render(StammbaumSidePanel, {
			props: { node: baseNode(), onClose }
		});

		window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' }));
		expect(onClose).toHaveBeenCalledOnce();
	});

	it('does not call onClose for non-Escape keys', async () => {
		const onClose = vi.fn();
		render(StammbaumSidePanel, {
			props: { node: baseNode(), onClose }
		});

		window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' }));
		expect(onClose).not.toHaveBeenCalled();
	});

	it('renders the person detail link', async () => {
		render(StammbaumSidePanel, {
			props: { node: baseNode(), onClose: () => {} }
		});

		const link = document.querySelector('a[href="/persons/p-1"]');
		expect(link).not.toBeNull();
	});

	it('shows the empty placeholder when there are no direct relationships', async () => {
		render(StammbaumSidePanel, {
			props: { node: baseNode(), onClose: () => {} }
		});

		await expect.element(page.getByText(/keine beziehungen bekannt/i)).toBeVisible();
	});

	it('shows the error banner when both fetch calls fail', async () => {
		fetchSpy.mockImplementation(async () => new Response('error', { status: 500 }));

		render(StammbaumSidePanel, {
			props: { node: baseNode(), onClose: () => {} }
		});

		await vi.waitFor(() => {
			expect(document.querySelector('[role="alert"]')).not.toBeNull();
		});
	});

	it('shows the AddRelationshipForm only when canWrite is true', async () => {
		render(StammbaumSidePanel, {
			props: { node: baseNode(), onClose: () => {}, canWrite: true }
		});

		await vi.waitFor(() => {
			const addButtons = Array.from(document.querySelectorAll('button')).filter((b) =>
				b.textContent?.toLowerCase().includes('hinzufügen')
			);
			expect(addButtons.length).toBeGreaterThan(0);
		});
	});

	it('hides the AddRelationshipForm when canWrite is false', async () => {
		render(StammbaumSidePanel, {
			props: { node: baseNode(), onClose: () => {}, canWrite: false }
		});

		// canWrite=false hides the form — assert the toggle button is absent in the rendered DOM.
		const addButtons = Array.from(document.querySelectorAll('button')).filter((b) =>
			b.textContent?.toLowerCase().includes('hinzufügen')
		);
		expect(addButtons.length).toBe(0);
	});
});
