import { afterEach, beforeEach, describe, expect, it, vi, type MockInstance } from 'vitest';
import { cleanup, render } from 'vitest-browser-svelte';
import { page } from 'vitest/browser';
import Page from './+page.svelte';

afterEach(cleanup);
afterEach(() => {
	vi.restoreAllMocks();
	vi.unstubAllGlobals();
});

describe('Admin system page', () => {
	it('renders the backfill versions heading', async () => {
		render(Page, {});
		await expect.element(page.getByText(/Verlaufsdaten auffüllen/i)).toBeInTheDocument();
	});

	it('renders the backfill versions button', async () => {
		render(Page, {});
		await expect
			.element(page.getByRole('button', { name: /jetzt auffüllen/i }))
			.toBeInTheDocument();
	});

	it('renders the backfill file hashes heading', async () => {
		render(Page, {});
		await expect
			.element(page.getByRole('heading', { name: /Datei-Hashes berechnen/i }))
			.toBeInTheDocument();
	});

	it('renders the file hashes button', async () => {
		render(Page, {});
		await expect
			.element(page.getByRole('button', { name: /Datei-Hashes berechnen/i }))
			.toBeInTheDocument();
	});
});

describe('Admin system page — mass import card', () => {
	beforeEach(() => {
		vi.stubGlobal(
			'fetch',
			vi.fn().mockResolvedValue({
				ok: true,
				json: async () => ({
					state: 'IDLE',
					message: 'Kein Import gestartet.',
					processed: 0,
					startedAt: null
				})
			})
		);
	});

	it('renders the mass import heading', async () => {
		render(Page, {});
		// getByText(/Massenimport/i) would match both the H2 heading AND the thumbnail
		// description paragraph that mentions "Massenimport" — use heading role instead.
		await expect.element(page.getByRole('heading', { name: /Massenimport/i })).toBeInTheDocument();
	});

	it('renders the start import button when idle', async () => {
		render(Page, {});
		await expect.element(page.getByRole('button', { name: /Import starten/i })).toBeInTheDocument();
	});

	it('shows idle status text', async () => {
		render(Page, {});
		await expect.element(page.getByText(/Kein Import gestartet/i)).toBeInTheDocument();
	});

	it('disables the start button and shows running state after click', async () => {
		// $effect calls fetchImportStatus() then fetchThumbnailStatus() on mount —
		// the mock must cover both before the POST trigger call.
		const fetchMock = vi
			.fn()
			// call 1: fetchImportStatus() on mount → IDLE
			.mockResolvedValueOnce({
				ok: true,
				json: async () => ({
					state: 'IDLE',
					message: 'Kein Import gestartet.',
					processed: 0,
					startedAt: null
				})
			})
			// call 2: fetchThumbnailStatus() on mount → IDLE (prevent thumbnail polling)
			.mockResolvedValueOnce({
				ok: true,
				json: async () => ({
					state: 'IDLE',
					message: '',
					total: 0,
					processed: 0,
					skipped: 0,
					failed: 0,
					startedAt: null
				})
			})
			// call 3: trigger POST → returns RUNNING
			.mockResolvedValueOnce({
				ok: true,
				json: async () => ({
					state: 'RUNNING',
					message: 'Import läuft...',
					processed: 0,
					startedAt: '2026-01-01T10:00:00'
				})
			});
		vi.stubGlobal('fetch', fetchMock);

		render(Page, {});
		await expect.element(page.getByRole('button', { name: /Import starten/i })).toBeInTheDocument();

		document.querySelector<HTMLButtonElement>('[data-import-trigger]')!.click();

		await expect.element(page.getByText(/Import läuft/i)).toBeInTheDocument();
	});

	it('shows done status and retry button after successful import', async () => {
		// Use mockResolvedValueOnce per call so thumbnail gets IDLE, not DONE.
		// Both cards in DONE state would render two "Erneut starten" buttons → strict mode.
		vi.stubGlobal(
			'fetch',
			vi
				.fn()
				.mockResolvedValueOnce({
					ok: true,
					json: async () => ({
						state: 'DONE',
						message: 'Import abgeschlossen.',
						processed: 42,
						startedAt: '2026-01-01T10:00:00'
					})
				})
				.mockResolvedValueOnce({
					ok: true,
					json: async () => ({
						state: 'IDLE',
						message: '',
						total: 0,
						processed: 0,
						skipped: 0,
						failed: 0,
						startedAt: null
					})
				})
		);
		render(Page, {});
		await expect.element(page.getByText(/42 Dokumente/i)).toBeInTheDocument();
		await expect.element(page.getByRole('button', { name: /Erneut starten/i })).toBeInTheDocument();
	});

	it('shows failed status and retry button on error', async () => {
		// Use mockResolvedValueOnce so thumbnail gets IDLE, not FAILED.
		// Both cards in FAILED state would render two identical error messages → strict mode.
		vi.stubGlobal(
			'fetch',
			vi
				.fn()
				.mockResolvedValueOnce({
					ok: true,
					json: async () => ({
						state: 'FAILED',
						statusCode: 'IMPORT_FAILED_NO_SPREADSHEET',
						processed: 0,
						startedAt: '2026-01-01T10:00:00'
					})
				})
				.mockResolvedValueOnce({
					ok: true,
					json: async () => ({
						state: 'IDLE',
						message: '',
						total: 0,
						processed: 0,
						skipped: 0,
						failed: 0,
						startedAt: null
					})
				})
		);
		render(Page, {});
		await expect.element(page.getByText(/Keine Tabellendatei gefunden/i)).toBeInTheDocument();
		await expect.element(page.getByRole('button', { name: /Erneut starten/i })).toBeInTheDocument();
	});
});

// ─── Polling lifecycle ────────────────────────────────────────────────────────

describe('Admin system page — polling lifecycle', () => {
	let setIntervalSpy: MockInstance;
	let clearIntervalSpy: MockInstance;

	beforeEach(() => {
		setIntervalSpy = vi.spyOn(globalThis, 'setInterval');
		clearIntervalSpy = vi.spyOn(globalThis, 'clearInterval');
	});

	afterEach(() => {
		setIntervalSpy.mockRestore();
		clearIntervalSpy.mockRestore();
	});

	it('starts polling when initial status is RUNNING', async () => {
		vi.stubGlobal(
			'fetch',
			vi.fn().mockResolvedValue({
				ok: true,
				json: async () => ({
					state: 'RUNNING',
					message: 'Import läuft...',
					processed: 0,
					startedAt: '2026-01-01T10:00:00'
				})
			})
		);
		render(Page, {});
		await expect.element(page.getByText(/Import läuft/i)).toBeInTheDocument();
		expect(setIntervalSpy).toHaveBeenCalled();
	});

	it('does not start polling when initial status is IDLE', async () => {
		vi.stubGlobal(
			'fetch',
			vi.fn().mockResolvedValue({
				ok: true,
				json: async () => ({
					state: 'IDLE',
					message: '',
					processed: 0,
					startedAt: null
				})
			})
		);
		render(Page, {});
		await expect.element(page.getByRole('button', { name: /Import starten/i })).toBeInTheDocument();
		expect(setIntervalSpy).not.toHaveBeenCalled();
	});
});
