import { describe, it, expect, vi, afterEach } from 'vitest';
import { cleanup, render } from 'vitest-browser-svelte';
import { page } from 'vitest/browser';

import { createConfirmService, CONFIRM_KEY } from '$lib/shared/services/confirm.svelte.js';

const { default: TranscriptionEditView } = await import('./TranscriptionEditView.svelte');
import type { TranscriptionBlockData } from '$lib/shared/types';

afterEach(cleanup);

const baseBlock = (overrides: Partial<TranscriptionBlockData> = {}): TranscriptionBlockData =>
	({
		id: 'b-1',
		annotationId: 'ann-1',
		text: 'Hello',
		sortOrder: 1,
		reviewed: false,
		mentionedPersons: [],
		label: null,
		...overrides
	}) as TranscriptionBlockData;

const baseProps = (overrides: Record<string, unknown> = {}) => ({
	documentId: 'doc-1',
	blocks: [] as TranscriptionBlockData[],
	canComment: false,
	currentUserId: null,
	onBlockFocus: () => {},
	onSaveBlock: async () => {},
	onDeleteBlock: async () => {},
	onReviewToggle: async () => {},
	...overrides
});

describe('TranscriptionEditView', () => {
	it('renders the empty-state coach when there are no blocks', async () => {
		render(TranscriptionEditView, {
			context: new Map([[CONFIRM_KEY, createConfirmService()]]),
			props: baseProps()
		});

		// TranscribeCoachEmptyState renders some German text
		expect(document.body.textContent).toMatch(/markier|block|transkrip/i);
	});

	it('renders the review progress counter when there are blocks', async () => {
		render(TranscriptionEditView, {
			context: new Map([[CONFIRM_KEY, createConfirmService()]]),
			props: baseProps({
				blocks: [baseBlock({ id: 'b1', reviewed: false }), baseBlock({ id: 'b2', reviewed: true })]
			})
		});

		expect(document.body.textContent).toMatch(/1\s*\/\s*2/);
	});

	it('shows the "alle als fertig markieren" button when onMarkAllReviewed is provided', async () => {
		render(TranscriptionEditView, {
			context: new Map([[CONFIRM_KEY, createConfirmService()]]),
			props: baseProps({
				blocks: [baseBlock()],
				onMarkAllReviewed: async () => {}
			})
		});

		await expect.element(page.getByRole('button', { name: /alle als fertig/i })).toBeVisible();
	});

	it('disables the mark-all-reviewed button when all blocks are reviewed', async () => {
		render(TranscriptionEditView, {
			context: new Map([[CONFIRM_KEY, createConfirmService()]]),
			props: baseProps({
				blocks: [baseBlock({ reviewed: true })],
				onMarkAllReviewed: async () => {}
			})
		});

		const btn = (await page
			.getByRole('button', { name: /alle als fertig/i })
			.element()) as HTMLButtonElement;
		expect(btn.disabled).toBe(true);
	});

	it('enables the mark-all-reviewed button when not all blocks are reviewed', async () => {
		render(TranscriptionEditView, {
			context: new Map([[CONFIRM_KEY, createConfirmService()]]),
			props: baseProps({
				blocks: [baseBlock({ reviewed: false })],
				onMarkAllReviewed: async () => {}
			})
		});

		const btn = (await page
			.getByRole('button', { name: /alle als fertig/i })
			.element()) as HTMLButtonElement;
		expect(btn.disabled).toBe(false);
	});

	it('hides the mark-all-reviewed button when onMarkAllReviewed is not provided', async () => {
		render(TranscriptionEditView, {
			context: new Map([[CONFIRM_KEY, createConfirmService()]]),
			props: baseProps({ blocks: [baseBlock()] })
		});

		await expect
			.element(page.getByRole('button', { name: /alle als fertig/i }))
			.not.toBeInTheDocument();
	});

	it('renders the OcrTrigger only when canRunOcr is true and onTriggerOcr is provided', async () => {
		render(TranscriptionEditView, {
			context: new Map([[CONFIRM_KEY, createConfirmService()]]),
			props: baseProps({
				blocks: [baseBlock()],
				canRunOcr: true,
				onTriggerOcr: () => {}
			})
		});

		// OcrTrigger renders a select with script-type options
		const select = document.querySelector('select');
		expect(select).not.toBeNull();
	});

	it('hides the OcrTrigger when canRunOcr is false', async () => {
		render(TranscriptionEditView, {
			context: new Map([[CONFIRM_KEY, createConfirmService()]]),
			props: baseProps({
				blocks: [baseBlock()],
				canRunOcr: false,
				onTriggerOcr: () => {}
			})
		});

		const select = document.querySelector('select');
		expect(select).toBeNull();
	});

	it('renders the training-label chips when canWrite=true and there are blocks', async () => {
		render(TranscriptionEditView, {
			context: new Map([[CONFIRM_KEY, createConfirmService()]]),
			props: baseProps({
				blocks: [baseBlock()],
				canWrite: true,
				trainingLabels: [],
				onToggleTrainingLabel: async () => {}
			})
		});

		// Training-label section caption
		expect(document.body.textContent).toMatch(/training/i);
	});

	it('hides the training-label section when canWrite is false', async () => {
		render(TranscriptionEditView, {
			context: new Map([[CONFIRM_KEY, createConfirmService()]]),
			props: baseProps({
				blocks: [baseBlock()],
				canWrite: false
			})
		});

		expect(document.body.textContent).not.toMatch(/Für Training vormerken/i);
	});

	it('toggles the training label chip when clicked', async () => {
		const onToggleTrainingLabel = vi.fn().mockResolvedValue(undefined);
		render(TranscriptionEditView, {
			context: new Map([[CONFIRM_KEY, createConfirmService()]]),
			props: baseProps({
				blocks: [baseBlock()],
				canWrite: true,
				trainingLabels: [],
				onToggleTrainingLabel
			})
		});

		const chip = Array.from(document.querySelectorAll('button')).find((b) =>
			/kurrent|segmentier/i.test(b.textContent ?? '')
		);
		expect(chip).toBeDefined();
		chip?.click();

		await vi.waitFor(() => expect(onToggleTrainingLabel).toHaveBeenCalled());
	});

	it('renders blocks sorted by sortOrder', async () => {
		render(TranscriptionEditView, {
			context: new Map([[CONFIRM_KEY, createConfirmService()]]),
			props: baseProps({
				blocks: [
					baseBlock({ id: 'b3', sortOrder: 3, text: 'Third' }),
					baseBlock({ id: 'b1', sortOrder: 1, text: 'First' }),
					baseBlock({ id: 'b2', sortOrder: 2, text: 'Second' })
				]
			})
		});

		const text = document.body.textContent ?? '';
		const idxFirst = text.indexOf('First');
		const idxSecond = text.indexOf('Second');
		const idxThird = text.indexOf('Third');
		expect(idxFirst).toBeLessThan(idxSecond);
		expect(idxSecond).toBeLessThan(idxThird);
	});

	it('renders both blocks with their text after rerender with a new activeAnnotationId', async () => {
		const { rerender } = render(TranscriptionEditView, {
			context: new Map([[CONFIRM_KEY, createConfirmService()]]),
			props: baseProps({
				blocks: [
					baseBlock({ id: 'b1', annotationId: 'ann-1', sortOrder: 1, text: 'First' }),
					baseBlock({ id: 'b2', annotationId: 'ann-2', sortOrder: 2, text: 'Second' })
				],
				activeAnnotationId: null
			})
		});

		// re-render with activeAnnotationId set to ann-2 — the activeBlockId $effect re-runs
		// and both blocks must still be present in the rendered list.
		await rerender({
			...baseProps({
				blocks: [
					baseBlock({ id: 'b1', annotationId: 'ann-1', sortOrder: 1, text: 'First' }),
					baseBlock({ id: 'b2', annotationId: 'ann-2', sortOrder: 2, text: 'Second' })
				],
				activeAnnotationId: 'ann-2'
			})
		});

		await vi.waitFor(() => {
			expect(document.body.textContent).toContain('First');
			expect(document.body.textContent).toContain('Second');
		});
	});

	it('handleMarkAllReviewed calls onMarkAllReviewed when clicked', async () => {
		const onMarkAllReviewed = vi.fn().mockResolvedValue(undefined);
		render(TranscriptionEditView, {
			context: new Map([[CONFIRM_KEY, createConfirmService()]]),
			props: baseProps({
				blocks: [baseBlock({ reviewed: false })],
				onMarkAllReviewed
			})
		});

		const btn = (await page
			.getByRole('button', { name: /alle als fertig/i })
			.element()) as HTMLButtonElement;
		btn.click();
		await vi.waitFor(() => expect(onMarkAllReviewed).toHaveBeenCalledOnce());
	});

	it('renders all blocks with their text', async () => {
		render(TranscriptionEditView, {
			context: new Map([[CONFIRM_KEY, createConfirmService()]]),
			props: baseProps({
				blocks: [
					baseBlock({ id: 'b1', text: 'Erster Block' }),
					baseBlock({ id: 'b2', text: 'Zweiter Block' })
				]
			})
		});

		expect(document.body.textContent).toContain('Erster Block');
		expect(document.body.textContent).toContain('Zweiter Block');
	});

	it('shows the next-block CTA when there are blocks', async () => {
		render(TranscriptionEditView, {
			context: new Map([[CONFIRM_KEY, createConfirmService()]]),
			props: baseProps({
				blocks: [baseBlock()]
			})
		});

		// CTA shows the number of the next block ("Nächster Block 2")
		expect(document.body.textContent).toMatch(/2/);
	});

	it('shows the active training label highlighted when included in trainingLabels', async () => {
		render(TranscriptionEditView, {
			context: new Map([[CONFIRM_KEY, createConfirmService()]]),
			props: baseProps({
				blocks: [baseBlock()],
				canWrite: true,
				trainingLabels: ['KURRENT_RECOGNITION'],
				onToggleTrainingLabel: async () => {}
			})
		});

		// The chip for KURRENT_RECOGNITION should have the active class
		const chips = document.querySelectorAll('button');
		const activeChip = Array.from(chips).find(
			(c) => c.className.includes('border-brand-mint') && c.className.includes('bg-brand-mint')
		);
		expect(activeChip).toBeDefined();
	});

	it('renders the inactive training-label chip class when not in trainingLabels', async () => {
		render(TranscriptionEditView, {
			context: new Map([[CONFIRM_KEY, createConfirmService()]]),
			props: baseProps({
				blocks: [baseBlock()],
				canWrite: true,
				trainingLabels: [],
				onToggleTrainingLabel: async () => {}
			})
		});

		// Inactive chip has border-line class, not bg-brand-mint
		const chips = Array.from(document.querySelectorAll('button')).filter((b) =>
			/kurrent|segmentier/i.test(b.textContent ?? '')
		);
		expect(chips.length).toBeGreaterThan(0);
		expect(chips[0].className).not.toContain('bg-brand-mint');
	});
});
