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

afterEach(cleanup);

function renderDialog() {
	const service = createConfirmService();
	const result = render(ConfirmDialog, {
		context: new Map([[CONFIRM_KEY, service]])
	});
	return { ...result, service };
}

describe('ConfirmDialog', () => {
	it('renders the title when options are set', async () => {
		const { service } = renderDialog();
		service.confirm({ title: 'Delete this item?' });

		await expect.element(page.getByText('Delete this item?')).toBeInTheDocument();
		service.settle(false);
	});

	it('renders the body when provided', async () => {
		const { service } = renderDialog();
		service.confirm({ title: 'Delete?', body: 'This cannot be undone.' });

		await expect.element(page.getByText('This cannot be undone.')).toBeInTheDocument();
		service.settle(false);
	});

	it('does not render body element when body is omitted', async () => {
		const { service } = renderDialog();
		service.confirm({ title: 'Delete?' });

		await expect.element(page.getByText('Delete?')).toBeInTheDocument();
		const body = document.querySelector('p');
		expect(body).toBeNull();
		service.settle(false);
	});

	it('applies bg-danger class on confirm button when destructive', async () => {
		const { service } = renderDialog();
		service.confirm({ title: 'Delete?', destructive: true });

		await expect.element(page.getByText('Delete?')).toBeInTheDocument();
		const confirmBtn = document.querySelector('button[class*="bg-danger"]');
		expect(confirmBtn).not.toBeNull();
		service.settle(false);
	});

	it('applies bg-primary class on confirm button when not destructive', async () => {
		const { service } = renderDialog();
		service.confirm({ title: 'Confirm action?' });

		await expect.element(page.getByText('Confirm action?')).toBeInTheDocument();
		const confirmBtn = document.querySelector('button[class*="bg-primary"]');
		expect(confirmBtn).not.toBeNull();
		service.settle(false);
	});

	it('renders custom confirmLabel when provided', async () => {
		const { service } = renderDialog();
		service.confirm({ title: 'Remove?', confirmLabel: 'Yes, remove it' });

		await expect.element(page.getByRole('button', { name: 'Yes, remove it' })).toBeInTheDocument();
		service.settle(false);
	});

	it('renders custom cancelLabel when provided', async () => {
		const { service } = renderDialog();
		service.confirm({ title: 'Remove?', cancelLabel: 'No, keep it' });

		await expect.element(page.getByRole('button', { name: 'No, keep it' })).toBeInTheDocument();
		service.settle(false);
	});

	it('settles true when confirm button is clicked', async () => {
		const { service } = renderDialog();
		const resultPromise = service.confirm({ title: 'Do it?' });

		await expect.element(page.getByText('Do it?')).toBeInTheDocument();
		const confirmBtn = document.querySelectorAll<HTMLButtonElement>('button[type="button"]')[1];
		confirmBtn.click();

		expect(await resultPromise).toBe(true);
	});

	it('settles false when cancel button is clicked', async () => {
		const { service } = renderDialog();
		const resultPromise = service.confirm({ title: 'Do it?' });

		await expect.element(page.getByText('Do it?')).toBeInTheDocument();
		const cancelBtn = document.querySelectorAll<HTMLButtonElement>('button[type="button"]')[0];
		cancelBtn.click();

		expect(await resultPromise).toBe(false);
	});

	it('hides content when no options are set', () => {
		renderDialog();
		const heading = document.querySelector('#confirm-title');
		expect(heading).toBeNull();
	});

	it('has aria-labelledby pointing to the title element', async () => {
		const { service } = renderDialog();
		service.confirm({ title: 'Accessible title' });

		await expect.element(page.getByText('Accessible title')).toBeInTheDocument();
		const dialog = document.querySelector('dialog');
		expect(dialog?.getAttribute('aria-labelledby')).toBe('confirm-title');
		const title = document.getElementById('confirm-title');
		expect(title).not.toBeNull();
		service.settle(false);
	});

	it('does not show content after settling', async () => {
		const { service } = renderDialog();
		service.confirm({ title: 'Gone soon?' });
		await expect.element(page.getByText('Gone soon?')).toBeInTheDocument();
		service.settle(false);

		await vi.waitFor(() => {
			expect(document.querySelector('#confirm-title')).toBeNull();
		});
	});
});
