import { test, expect, type Page } from '@playwright/test';
import path from 'path';
import { fileURLToPath } from 'url';
import fs from 'fs';
import { AxeBuilder } from '@axe-core/playwright';

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const PDF_FIXTURE = path.resolve(__dirname, 'fixtures/minimal.pdf');

/**
 * E2E tests for the transcribe keyboard shortcuts + cheatsheet overlay — #327.
 *
 * Strategy mirrors annotations.spec: seed a document with two transcription
 * blocks via API in beforeAll (no OCR, no manual drawing), then drive the
 * keyboard. j/k navigation is exercised in read mode so no editable can trap
 * focus — the active region's resize overlay renders regardless of read/edit.
 */

const RESIZE_AREA_LABEL = 'Annotationsgröße und -position ändern';

let docHref: string;
let docId: string;
let annotAId: string;
let annotBId: string;

test.describe('Transcribe keyboard shortcuts', () => {
	test.beforeAll(async ({ request }) => {
		const baseURL = process.env.E2E_BASE_URL ?? 'http://localhost:3000';

		const createRes = await request.post('/api/documents', {
			multipart: { title: 'E2E Shortcuts Test', documentDate: '1945-05-08' }
		});
		if (!createRes.ok()) throw new Error(`Create document failed: ${createRes.status()}`);
		const doc = await createRes.json();
		docId = doc.id;
		docHref = `${baseURL}/documents/${docId}`;

		const uploadRes = await request.put(`/api/documents/${docId}`, {
			multipart: {
				title: doc.title,
				documentDate: '1945-05-08',
				file: {
					name: 'minimal.pdf',
					mimeType: 'application/pdf',
					buffer: fs.readFileSync(PDF_FIXTURE)
				}
			}
		});
		if (!uploadRes.ok()) throw new Error(`Upload PDF failed: ${uploadRes.status()}`);

		const blockARes = await request.post(`/api/documents/${docId}/transcription-blocks`, {
			data: {
				pageNumber: 1,
				x: 0.1,
				y: 0.1,
				width: 0.3,
				height: 0.1,
				text: 'Erste Zeile.',
				label: 'Anrede'
			}
		});
		if (!blockARes.ok()) throw new Error(`Create block A failed: ${blockARes.status()}`);
		annotAId = (await blockARes.json()).annotationId;

		const blockBRes = await request.post(`/api/documents/${docId}/transcription-blocks`, {
			data: {
				pageNumber: 1,
				x: 0.1,
				y: 0.35,
				width: 0.3,
				height: 0.1,
				text: 'Zweite Zeile.',
				label: null
			}
		});
		if (!blockBRes.ok()) throw new Error(`Create block B failed: ${blockBRes.status()}`);
		annotBId = (await blockBRes.json()).annotationId;
	});

	async function openTranscribe(page: Page) {
		await page.goto(docHref);
		await page.waitForSelector('[data-hydrated]');
		await page.getByRole('button', { name: 'Transkribieren' }).click();
		await page.locator('.tabular-nums').waitFor({ timeout: 15_000 });
		await page.locator(`[data-testid="annotation-${annotAId}"]`).waitFor({ timeout: 10_000 });
	}

	function activeRegionOverlay(page: Page, annotationId: string) {
		return page.locator(`[data-testid="annotation-${annotationId}"]`).getByLabel(RESIZE_AREA_LABEL);
	}

	test('? opens the cheatsheet; Esc closes it, then a second Esc closes the panel', async ({
		page
	}) => {
		test.setTimeout(30_000);
		await openTranscribe(page);

		await page.keyboard.press('?');
		const dialog = page.getByRole('dialog');
		await expect(dialog).toBeVisible();
		await expect(dialog.getByRole('heading', { name: 'Tastaturkürzel' })).toBeVisible();

		await page.keyboard.press('Escape');
		await expect(dialog).not.toBeVisible();

		// Panel still open after closing only the cheatsheet (Esc ladder rung 1).
		await expect(page.getByRole('button', { name: 'Fertig' })).toBeVisible();

		await page.keyboard.press('Escape');
		await expect(page.getByRole('button', { name: 'Transkribieren' })).toBeVisible();
	});

	test('e toggles between read and edit mode', async ({ page }) => {
		test.setTimeout(30_000);
		await openTranscribe(page);

		// The "mark for training" section renders only in the edit view.
		const editMarker = page.getByText('Für Training vormerken');

		// Default for a writer with existing blocks is read mode.
		await expect(editMarker).toHaveCount(0);

		await page.keyboard.press('e');
		await expect(editMarker).toBeVisible();

		await page.keyboard.press('e');
		await expect(editMarker).toHaveCount(0);
	});

	test('j and k walk forward and back through the regions', async ({ page }) => {
		test.setTimeout(30_000);
		await openTranscribe(page);

		await page.keyboard.press('j');
		await expect(activeRegionOverlay(page, annotAId)).toBeVisible();

		await page.keyboard.press('j');
		await expect(activeRegionOverlay(page, annotBId)).toBeVisible();
		await expect(activeRegionOverlay(page, annotAId)).toHaveCount(0);

		await page.keyboard.press('k');
		await expect(activeRegionOverlay(page, annotAId)).toBeVisible();
	});

	test('the open cheatsheet has no critical accessibility violations', async ({ page }) => {
		test.setTimeout(30_000);
		await openTranscribe(page);

		await page.keyboard.press('?');
		await expect(page.getByRole('dialog')).toBeVisible();

		const results = await new AxeBuilder({ page })
			.include('dialog')
			.withTags(['wcag2a', 'wcag2aa'])
			.analyze();
		const critical = results.violations.filter((v) => v.impact === 'critical');
		expect(critical).toEqual([]);

		// The dialog exposes a modal role with an accessible name (labelled heading).
		const dialog = page.getByRole('dialog');
		await expect(dialog).toHaveAttribute('aria-modal', 'true');
	});
});
