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

vi.mock('$app/forms', () => ({ enhance: () => () => {} }));

afterEach(cleanup);

function renderWithConfirm(props = { tag, allTags }) {
	const service = createConfirmService();
	const result = render(TagDeleteGuard, {
		props,
		context: new Map([[CONFIRM_KEY, service]])
	});
	return { ...result, service };
}

const tag = { id: 't1', name: 'Familie', documentCount: 3 };
const allTags = [
	{ id: 't1', name: 'Familie', documentCount: 3 },
	{ id: 't2', name: 'Eltern', parentId: 't1', documentCount: 1 },
	{ id: 't3', name: 'Kinder', parentId: 't1', documentCount: 0 }
];

describe('TagDeleteGuard', () => {
	it('renders two radio options (single and subtree)', async () => {
		renderWithConfirm();
		await expect.element(page.getByRole('radio', { name: /Nur dieses/i })).toBeInTheDocument();
		await expect
			.element(page.getByRole('radio', { name: /Gesamten Teilbaum/i }))
			.toBeInTheDocument();
	});

	it('delete button is disabled initially', async () => {
		renderWithConfirm();
		await expect.element(page.getByTestId('delete-submit-btn')).toBeDisabled();
	});

	it('delete button is enabled after selecting single radio', async () => {
		renderWithConfirm();
		await page.getByRole('radio', { name: /Nur dieses/i }).click();
		await expect.element(page.getByTestId('delete-submit-btn')).not.toBeDisabled();
	});

	it('delete button is enabled after selecting subtree radio', async () => {
		renderWithConfirm();
		await page.getByRole('radio', { name: /Gesamten Teilbaum/i }).click();
		await expect.element(page.getByTestId('delete-submit-btn')).not.toBeDisabled();
	});

	it('delete button shows subtree-specific text when subtree mode is selected', async () => {
		renderWithConfirm();
		await page.getByRole('radio', { name: /Gesamten Teilbaum/i }).click();
		await expect
			.element(page.getByTestId('delete-submit-btn'))
			.toHaveTextContent(/Teilbaum löschen/i);
	});

	it('shows descendant count in impact summary', async () => {
		renderWithConfirm();
		// tag has 2 descendants (t2 and t3)
		await expect.element(page.getByText(/2 Untergeordnete/)).toBeInTheDocument();
	});

	it('shows document count in impact summary', async () => {
		renderWithConfirm();
		await expect.element(page.getByText(/3 Dokument/)).toBeInTheDocument();
	});

	// Characterization (#698): the delete-impact warning describes a destructive single-tag delete,
	// which removes only the tag's DIRECT document_tags rows — so it must show documentCount, never
	// a subtree rollup. Pinned so a future change can't silently desync this warning.
	it('shows the direct documentCount in the impact summary, not a subtree rollup', async () => {
		const tagWithStraySubtree = {
			id: 't1',
			name: 'Familie',
			documentCount: 3,
			subtreeDocumentCount: 99
		};
		renderWithConfirm({ tag: tagWithStraySubtree, allTags });
		await expect.element(page.getByText(/3 Dokument/)).toBeInTheDocument();
		expect(document.body.textContent).not.toContain('99');
	});
});

describe('TagDeleteGuard – confirmation dialog', () => {
	it('opens confirm dialog when delete button is clicked', async () => {
		const { service } = renderWithConfirm();
		await page.getByRole('radio', { name: /Nur dieses/i }).click();
		await page.getByTestId('delete-submit-btn').click();
		await vi.waitFor(() => expect(service.options).not.toBeNull());
		expect(service.options?.destructive).toBe(true);
		service.settle(false);
	});

	it('submits the form when user confirms', async () => {
		const { service, container } = renderWithConfirm();
		const form = container.querySelector('form')!;
		const requestSubmit = vi.spyOn(form, 'requestSubmit').mockImplementation(() => {});

		await page.getByRole('radio', { name: /Nur dieses/i }).click();
		await page.getByTestId('delete-submit-btn').click();
		await vi.waitFor(() => expect(service.options).not.toBeNull());
		service.settle(true);
		await vi.waitFor(() => expect(service.options).toBeNull());

		expect(requestSubmit).toHaveBeenCalledOnce();
	});

	it('does not submit when user cancels the dialog', async () => {
		const { service, container } = renderWithConfirm();
		const form = container.querySelector('form')!;
		const requestSubmit = vi.spyOn(form, 'requestSubmit').mockImplementation(() => {});

		await page.getByRole('radio', { name: /Nur dieses/i }).click();
		await page.getByTestId('delete-submit-btn').click();
		await vi.waitFor(() => expect(service.options).not.toBeNull());
		service.settle(false);
		await vi.waitFor(() => expect(service.options).toBeNull());

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