/**
 * E2E regression tests for PersonTypeahead dropdown visibility.
 *
 * These tests verify that the dropdown list is never clipped by a parent
 * container's stacking context — the root cause of issue #343.
 *
 * The tests run at both desktop (1280×720) and tablet (768×1024) viewports
 * as required by the acceptance criteria.
 */
import { test, expect, type Page } from '@playwright/test';

/**
 * Find a document edit URL to use as the test page.
 * Falls back to /documents/new if no existing document is found.
 */
async function getDocumentEditUrl(page: Page): Promise<string> {
	await page.goto('/');
	await page.waitForLoadState('networkidle');
	const firstDocLink = page.locator('a[href^="/documents/"]').first();
	const href = await firstDocLink.getAttribute('href').catch(() => null);
	if (href) {
		return `${href}/edit`;
	}
	return '/documents/new';
}

/** Wait for the listbox to become visible after triggering a search. */
async function waitForListbox(page: Page): Promise<void> {
	await page.waitForSelector('[role="listbox"]', { state: 'visible', timeout: 2000 });
}

test.describe('PersonTypeahead — dropdown visibility (desktop)', () => {
	test.use({ viewport: { width: 1280, height: 720 } });

	test('sender dropdown items are visible and not clipped in document edit', async ({ page }) => {
		const editUrl = await getDocumentEditUrl(page);
		await page.goto(editUrl);
		await page.waitForLoadState('networkidle');

		// Find the sender typeahead input (the visible text input, not the hidden one)
		const senderInput = page.locator('#senderId-search');
		await expect(senderInput).toBeVisible();

		// Type to trigger the dropdown
		await senderInput.click();
		await senderInput.fill('a');

		// Wait for the dropdown to appear (handles debounce automatically)
		await waitForListbox(page);

		const dropdown = page.locator('[role="listbox"]').first();
		await expect(dropdown).toBeVisible();

		const firstOption = dropdown.locator('[role="option"]').first();
		await expect(firstOption).toBeVisible();

		// Verify the bounding box is within the viewport (not clipped)
		const box = await firstOption.boundingBox();
		expect(box).not.toBeNull();
		expect(box!.y).toBeGreaterThan(0);
		expect(box!.y + box!.height).toBeLessThan(720);

		await page.screenshot({ path: 'test-results/e2e/person-typeahead-dropdown-desktop.png' });
	});

	test('dropdown is positioned below the input field (not hidden behind parent)', async ({
		page
	}) => {
		const editUrl = await getDocumentEditUrl(page);
		await page.goto(editUrl);
		await page.waitForLoadState('networkidle');

		const senderInput = page.locator('#senderId-search');
		await expect(senderInput).toBeVisible();

		const inputBox = await senderInput.boundingBox();
		expect(inputBox).not.toBeNull();

		await senderInput.click();
		await senderInput.fill('a');
		await waitForListbox(page);

		const dropdown = page.locator('[role="listbox"]').first();
		await expect(dropdown).toBeVisible();

		const dropdownBox = await dropdown.boundingBox();
		expect(dropdownBox).not.toBeNull();

		// Dropdown must appear below the input, not on top or clipped behind it
		expect(dropdownBox!.y).toBeGreaterThanOrEqual(inputBox!.y + inputBox!.height - 5);

		await page.screenshot({ path: 'test-results/e2e/person-typeahead-dropdown-position.png' });
	});
});

test.describe('PersonTypeahead — dropdown visibility (tablet)', () => {
	test.use({ viewport: { width: 768, height: 1024 } });

	test('sender dropdown items are visible and not clipped on tablet viewport', async ({ page }) => {
		const editUrl = await getDocumentEditUrl(page);
		await page.goto(editUrl);
		await page.waitForLoadState('networkidle');

		const senderInput = page.locator('#senderId-search');
		await expect(senderInput).toBeVisible();

		await senderInput.click();
		await senderInput.fill('a');
		await waitForListbox(page);

		const dropdown = page.locator('[role="listbox"]').first();
		await expect(dropdown).toBeVisible();

		const firstOption = dropdown.locator('[role="option"]').first();
		await expect(firstOption).toBeVisible();

		const box = await firstOption.boundingBox();
		expect(box).not.toBeNull();
		expect(box!.y).toBeGreaterThan(0);
		expect(box!.y + box!.height).toBeLessThan(1024);

		await page.screenshot({ path: 'test-results/e2e/person-typeahead-dropdown-tablet.png' });
	});
});

test.describe('PersonTypeahead — keyboard navigation', () => {
	test.use({ viewport: { width: 1280, height: 720 } });

	test('ArrowDown moves focus to the first option', async ({ page }) => {
		const editUrl = await getDocumentEditUrl(page);
		await page.goto(editUrl);
		await page.waitForLoadState('networkidle');

		const senderInput = page.locator('#senderId-search');
		await senderInput.click();
		await senderInput.fill('a');
		await waitForListbox(page);

		await senderInput.press('ArrowDown');
		// First option should now be the active descendant
		const activeDescendant = await senderInput.getAttribute('aria-activedescendant');
		expect(activeDescendant).toBeTruthy();

		await page.screenshot({ path: 'test-results/e2e/person-typeahead-keyboard-nav.png' });
	});

	test('Escape key closes the dropdown', async ({ page }) => {
		const editUrl = await getDocumentEditUrl(page);
		await page.goto(editUrl);
		await page.waitForLoadState('networkidle');

		const senderInput = page.locator('#senderId-search');
		await senderInput.click();
		await senderInput.fill('a');
		await waitForListbox(page);

		const dropdown = page.locator('[role="listbox"]').first();
		await expect(dropdown).toBeVisible();
		await senderInput.press('Escape');
		await expect(dropdown).not.toBeVisible();
	});

	test('aria-expanded is true when dropdown is open', async ({ page }) => {
		const editUrl = await getDocumentEditUrl(page);
		await page.goto(editUrl);
		await page.waitForLoadState('networkidle');

		const senderInput = page.locator('#senderId-search');

		// Initially closed
		const initialExpanded = await senderInput.getAttribute('aria-expanded');
		expect(initialExpanded).toBe('false');

		await senderInput.click();
		await senderInput.fill('a');
		await waitForListbox(page);

		const expanded = await senderInput.getAttribute('aria-expanded');
		expect(expanded).toBe('true');
	});
});

test.describe('PersonTypeahead — click-outside dismiss (fixed position)', () => {
	test.use({ viewport: { width: 1280, height: 720 } });

	test('clicking outside a fixed-position dropdown closes it', async ({ page }) => {
		const editUrl = await getDocumentEditUrl(page);
		await page.goto(editUrl);
		await page.waitForLoadState('networkidle');

		const senderInput = page.locator('#senderId-search');
		await senderInput.click();
		await senderInput.fill('a');
		await waitForListbox(page);

		const dropdown = page.locator('[role="listbox"]').first();
		await expect(dropdown).toBeVisible();
		// Click somewhere else on the page
		await page.click('body', { position: { x: 10, y: 10 } });
		await expect(dropdown).not.toBeVisible();
	});
});
