import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { cleanup, render } from 'vitest-browser-svelte';
import { page } from 'vitest/browser';
import Page from './+page.svelte';
import { createConfirmService, CONFIRM_KEY } from '$lib/shared/services/confirm.svelte.js';

vi.mock('$app/forms', () => ({ enhance: () => () => {} }));
vi.mock('$app/navigation', () => ({
	beforeNavigate: vi.fn(),
	goto: vi.fn(),
	replaceState: vi.fn()
}));
vi.mock('$app/state', () => ({
	get page() {
		return { url: new URL('http://localhost/admin/tags/t1') };
	}
}));

import { beforeNavigate, goto } from '$app/navigation';

const baseTag = { id: 't1', name: 'Familie', documentCount: 0 };
const baseData = {
	tag: baseTag,
	tags: [] as {
		id: string;
		name: string;
		parentId?: string;
		color?: string;
		documentCount: number;
	}[]
};

// eslint-disable-next-line @typescript-eslint/no-explicit-any
function renderPage(props: { data: any; form: null }) {
	const service = createConfirmService();
	return render(Page, { props, context: new Map([[CONFIRM_KEY, service]]) });
}

afterEach(cleanup);

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

describe('Admin edit tag page – rendering', () => {
	it('renders the heading with tag name', async () => {
		renderPage({ data: baseData, form: null });
		await expect.element(page.getByText(/Schlagwort: Familie/i)).toBeInTheDocument();
	});

	it('pre-fills the name input', async () => {
		renderPage({ data: baseData, form: null });
		const input = document.querySelector<HTMLInputElement>('input[name="name"]');
		expect(input?.value).toBe('Familie');
	});

	it('renders the cancel link pointing to /admin/tags', async () => {
		renderPage({ data: baseData, form: null });
		await expect
			.element(page.getByRole('link', { name: /Abbrechen/i }))
			.toHaveAttribute('href', '/admin/tags');
	});
});

// ─── Unsaved-changes guard ────────────────────────────────────────────────────

describe('Admin edit tag page – unsaved-changes guard', () => {
	beforeEach(() => vi.clearAllMocks());

	it('does not show unsaved warning initially', async () => {
		renderPage({ data: baseData, form: null });
		await expect.element(page.getByText(/ungespeicherte Änderungen/i)).not.toBeInTheDocument();
	});

	it('cancels navigation and shows warning when rename form is dirty', async () => {
		renderPage({ data: baseData, form: null });
		const [callback] = vi.mocked(beforeNavigate).mock.calls[0];

		document
			.querySelector<HTMLInputElement>('input[name="name"]')!
			.dispatchEvent(new InputEvent('input', { bubbles: true }));

		const cancel = vi.fn();
		callback({ cancel, to: { url: new URL('http://localhost/admin/tags/t2') } });

		expect(cancel).toHaveBeenCalled();
		await expect.element(page.getByText(/ungespeicherte Änderungen/i)).toBeInTheDocument();
	});

	it('does not cancel navigation when form is clean', async () => {
		renderPage({ data: baseData, form: null });
		const [callback] = vi.mocked(beforeNavigate).mock.calls[0];

		const cancel = vi.fn();
		callback({ cancel, to: { url: new URL('http://localhost/admin/tags/t2') } });

		expect(cancel).not.toHaveBeenCalled();
	});

	it('discard button calls goto with the target URL', async () => {
		renderPage({ data: baseData, form: null });
		const [callback] = vi.mocked(beforeNavigate).mock.calls[0];

		document
			.querySelector<HTMLInputElement>('input[name="name"]')!
			.dispatchEvent(new InputEvent('input', { bubbles: true }));

		callback({ cancel: vi.fn(), to: { url: new URL('http://localhost/admin/tags/t2') } });

		await page.getByRole('button', { name: /verwerfen/i }).click();

		expect(vi.mocked(goto)).toHaveBeenCalledWith('http://localhost/admin/tags/t2');
	});
});

// ─── Parent selector (TagParentPicker combobox) ───────────────────────────────

describe('Admin edit tag page – parent selector', () => {
	it('renders a TagParentPicker combobox', async () => {
		renderPage({ data: baseData, form: null });
		await expect
			.element(page.getByRole('combobox', { name: /Übergeordnetes Schlagwort/i }))
			.toBeInTheDocument();
	});
});

// ─── Color picker ─────────────────────────────────────────────────────────────

describe('Admin edit tag page – color picker', () => {
	it('renders color picker when tag has no parent', async () => {
		renderPage({
			data: { tag: { id: 't1', name: 'Familie', parentId: undefined, documentCount: 0 }, tags: [] },
			form: null
		});
		await expect.element(page.getByTestId('color-picker')).toBeInTheDocument();
	});

	it('hides color picker when tag already has a parent', async () => {
		renderPage({
			data: {
				tag: { id: 't1', name: 'Familie', parentId: 't2', documentCount: 0 },
				tags: [{ id: 't2', name: 'Reise', documentCount: 0 }]
			},
			form: null
		});
		await expect.element(page.getByTestId('color-picker')).not.toBeInTheDocument();
	});

	it('pre-selects the current tag color in the color picker', async () => {
		renderPage({
			data: { tag: { id: 't1', name: 'Familie', color: 'sage', documentCount: 0 }, tags: [] },
			form: null
		});
		const selected = page.getByTestId('color-swatch-sage');
		await expect.element(selected).toHaveAttribute('aria-pressed', 'true');
	});
});

// ─── Merge success banner ─────────────────────────────────────────────────────

describe('Admin edit tag page – merge success banner', () => {
	it('shows merge success banner when data.mergeSuccess is true', async () => {
		renderPage({ data: { ...baseData, mergeSuccess: true }, form: null });
		await expect.element(page.getByText(/Erfolgreich zusammengeführt/i)).toBeInTheDocument();
	});

	it('does not show merge success banner when data.mergeSuccess is false', async () => {
		renderPage({ data: { ...baseData, mergeSuccess: false }, form: null });
		await expect.element(page.getByText(/Erfolgreich zusammengeführt/i)).not.toBeInTheDocument();
	});
});

// ─── New components present ───────────────────────────────────────────────────

describe('Admin edit tag page – new components', () => {
	it('renders TagAncestry nav when tag has a parent', async () => {
		const { container } = renderPage({
			data: {
				tag: { id: 't2', name: 'Kind', parentId: 't1', documentCount: 0 },
				tags: [
					{ id: 't1', name: 'Eltern', documentCount: 0 },
					{ id: 't2', name: 'Kind', parentId: 't1', documentCount: 0 }
				]
			},
			form: null
		});
		expect(container.querySelector('nav')).toBeTruthy();
	});

	it('does not render TagAncestry nav for root tag', async () => {
		const { container } = renderPage({ data: baseData, form: null });
		expect(container.querySelector('nav')).toBeFalsy();
	});

	it('renders TagMergeZone with merge heading', async () => {
		renderPage({ data: baseData, form: null });
		await expect.element(page.getByText(/Zusammenführen/i)).toBeInTheDocument();
	});

	it('renders TagDeleteGuard with two radio options', async () => {
		renderPage({ data: baseData, form: null });
		const radios = document.querySelectorAll<HTMLInputElement>('input[type="radio"]');
		expect(radios.length).toBe(2);
	});
});
