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

afterEach(cleanup);

describe('admin/system page', () => {
	let fetchSpy: ReturnType<typeof vi.spyOn>;

	beforeEach(() => {
		// mockImplementation (not mockResolvedValue) returns a fresh Response per call so the
		// body stream isn't already-consumed after the first read.
		fetchSpy = vi.spyOn(globalThis, 'fetch').mockImplementation(
			async () =>
				new Response(
					JSON.stringify({
						state: 'IDLE',
						message: '',
						total: 0,
						processed: 0,
						skipped: 0,
						failed: 0
					}),
					{ status: 200, headers: { 'Content-Type': 'application/json' } }
				)
		);
	});

	afterEach(() => {
		fetchSpy?.mockRestore();
	});

	it('renders the backfill versions card', async () => {
		render(AdminSystemPage, { props: {} });

		await expect
			.element(page.getByRole('heading', { name: /verlaufsdaten auffüllen/i }))
			.toBeVisible();
	});

	it('renders the backfill versions button enabled by default', async () => {
		render(AdminSystemPage, { props: {} });

		const btn = (await page
			.getByRole('button', { name: /jetzt auffüllen/i })
			.element()) as HTMLButtonElement;
		expect(btn.disabled).toBe(false);
	});

	it('renders the backfill file-hashes card', async () => {
		render(AdminSystemPage, { props: {} });

		await expect
			.element(page.getByRole('heading', { name: /datei-hashes berechnen/i }))
			.toBeVisible();
	});

	it('renders the backfill file-hashes button enabled by default', async () => {
		render(AdminSystemPage, { props: {} });

		const btn = (await page
			.getByRole('button', { name: /datei-hashes berechnen/i })
			.element()) as HTMLButtonElement;
		expect(btn.disabled).toBe(false);
	});

	it('does not render the backfill success banner before any action', async () => {
		render(AdminSystemPage, { props: {} });

		const banners = document.querySelectorAll('.bg-green-50');
		expect(banners.length).toBe(0);
	});

	it('triggers backfill versions when its button is clicked', async () => {
		fetchSpy.mockResolvedValueOnce(
			new Response(JSON.stringify({ count: 7 }), {
				status: 200,
				headers: { 'Content-Type': 'application/json' }
			})
		);

		render(AdminSystemPage, { props: {} });

		await page.getByRole('button', { name: /jetzt auffüllen/i }).click();

		await vi.waitFor(() => {
			const calls = fetchSpy.mock.calls.map((c) => c[0].toString());
			expect(calls.some((c) => c.includes('backfill-versions'))).toBe(true);
		});
	});

	it('triggers file-hashes backfill when its button is clicked', async () => {
		render(AdminSystemPage, { props: {} });

		await page.getByRole('button', { name: /datei-hashes berechnen/i }).click();

		await vi.waitFor(() => {
			const calls = fetchSpy.mock.calls.map((c) => c[0].toString());
			expect(calls.some((c) => c.includes('backfill-file-hashes'))).toBe(true);
		});
	});

	it('initial fetch loads import-status and thumbnail-status', async () => {
		render(AdminSystemPage, { props: {} });

		await vi.waitFor(() => {
			const calls = fetchSpy.mock.calls.map((c) => c[0].toString());
			expect(calls.some((c) => c.includes('import-status'))).toBe(true);
			expect(calls.some((c) => c.includes('thumbnail-status'))).toBe(true);
		});
	});

	it('shows the success banner after backfill versions completes', async () => {
		fetchSpy.mockImplementation(async (url: RequestInfo | URL) => {
			const u = url.toString();
			if (u.includes('backfill-versions')) {
				return new Response(JSON.stringify({ count: 12 }), {
					status: 200,
					headers: { 'Content-Type': 'application/json' }
				});
			}
			return new Response(JSON.stringify({ state: 'IDLE' }), {
				status: 200,
				headers: { 'Content-Type': 'application/json' }
			});
		});

		render(AdminSystemPage, { props: {} });

		await page.getByRole('button', { name: /jetzt auffüllen/i }).click();

		await vi.waitFor(() => {
			expect(document.querySelector('.bg-green-50')).not.toBeNull();
		});
	});

	it('renders the running state for import-status', async () => {
		fetchSpy.mockImplementation(async (url: RequestInfo | URL) => {
			const u = url.toString();
			if (u.includes('import-status')) {
				return new Response(
					JSON.stringify({ state: 'RUNNING', message: '', processed: 0, startedAt: null }),
					{ status: 200, headers: { 'Content-Type': 'application/json' } }
				);
			}
			return new Response(JSON.stringify({ state: 'IDLE' }), {
				status: 200,
				headers: { 'Content-Type': 'application/json' }
			});
		});

		render(AdminSystemPage, { props: {} });

		await vi.waitFor(() => {
			expect(document.body.textContent).toMatch(/läuft|wird ausgeführt/i);
		});
	});

	it('renders the DONE state with processed count for import-status', async () => {
		fetchSpy.mockImplementation(async (url: RequestInfo | URL) => {
			const u = url.toString();
			if (u.includes('import-status')) {
				return new Response(
					JSON.stringify({ state: 'DONE', message: '', processed: 99, startedAt: null }),
					{ status: 200, headers: { 'Content-Type': 'application/json' } }
				);
			}
			return new Response(JSON.stringify({ state: 'IDLE' }), {
				status: 200,
				headers: { 'Content-Type': 'application/json' }
			});
		});

		render(AdminSystemPage, { props: {} });

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

	it('renders the FAILED state with the error message for thumbnail-status', async () => {
		fetchSpy.mockImplementation(async (url: RequestInfo | URL) => {
			const u = url.toString();
			if (u.includes('thumbnail-status')) {
				return new Response(
					JSON.stringify({
						state: 'FAILED',
						message: 'connection refused',
						total: 0,
						processed: 0,
						skipped: 0,
						failed: 0,
						startedAt: null
					}),
					{ status: 200, headers: { 'Content-Type': 'application/json' } }
				);
			}
			return new Response(JSON.stringify({ state: 'IDLE' }), {
				status: 200,
				headers: { 'Content-Type': 'application/json' }
			});
		});

		render(AdminSystemPage, { props: {} });

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

	it('renders the DONE state for thumbnail-status with retry button', async () => {
		fetchSpy.mockImplementation(async (url: RequestInfo | URL) => {
			const u = url.toString();
			if (u.includes('thumbnail-status')) {
				return new Response(
					JSON.stringify({
						state: 'DONE',
						message: '',
						total: 100,
						processed: 95,
						skipped: 3,
						failed: 2,
						startedAt: null
					}),
					{ status: 200, headers: { 'Content-Type': 'application/json' } }
				);
			}
			return new Response(JSON.stringify({ state: 'IDLE' }), {
				status: 200,
				headers: { 'Content-Type': 'application/json' }
			});
		});

		render(AdminSystemPage, { props: {} });

		await vi.waitFor(() => {
			expect(document.querySelector('[data-testid="thumbnails-status-done"]')).not.toBeNull();
		});
	});

	it('renders the FAILED state for import-status with retry button', async () => {
		fetchSpy.mockImplementation(async (url: RequestInfo | URL) => {
			const u = url.toString();
			if (u.includes('import-status')) {
				return new Response(
					JSON.stringify({
						state: 'FAILED',
						statusCode: 'IMPORT_FAILED_INTERNAL',
						processed: 0,
						startedAt: null
					}),
					{ status: 200, headers: { 'Content-Type': 'application/json' } }
				);
			}
			return new Response(JSON.stringify({ state: 'IDLE' }), {
				status: 200,
				headers: { 'Content-Type': 'application/json' }
			});
		});

		render(AdminSystemPage, { props: {} });

		await vi.waitFor(() => {
			expect(document.body.textContent).toContain('Interner Fehler beim Import');
		});
	});

	it('renders the running thumbnail status with progress count', async () => {
		fetchSpy.mockImplementation(async (url: RequestInfo | URL) => {
			const u = url.toString();
			if (u.includes('thumbnail-status')) {
				return new Response(
					JSON.stringify({
						state: 'RUNNING',
						message: '',
						total: 100,
						processed: 30,
						skipped: 5,
						failed: 1,
						startedAt: null
					}),
					{ status: 200, headers: { 'Content-Type': 'application/json' } }
				);
			}
			return new Response(JSON.stringify({ state: 'IDLE' }), {
				status: 200,
				headers: { 'Content-Type': 'application/json' }
			});
		});

		render(AdminSystemPage, { props: {} });

		await vi.waitFor(() => {
			// Total 100, processed+skipped+failed = 36 — at least one of these surfaces.
			expect(document.body.textContent).toMatch(/36|100/);
		});
	});

	it('triggers thumbnail backfill when its button is clicked', async () => {
		render(AdminSystemPage, { props: {} });

		await vi.waitFor(() => {
			expect(document.querySelector('[data-thumbnails-trigger]')).not.toBeNull();
		});
		const btns = Array.from(document.querySelectorAll('[data-thumbnails-trigger]'));
		(btns[0] as HTMLButtonElement).click();

		await vi.waitFor(() => {
			const calls = fetchSpy.mock.calls.map((c) => c[0].toString());
			expect(calls.some((c) => c.includes('generate-thumbnails'))).toBe(true);
		});
	});

	it('triggers import when import button is clicked from idle state', async () => {
		render(AdminSystemPage, { props: {} });

		await vi.waitFor(() => {
			expect(document.querySelector('[data-import-trigger]')).not.toBeNull();
		});
		const btns = Array.from(document.querySelectorAll('[data-import-trigger]'));
		(btns[0] as HTMLButtonElement).click();

		await vi.waitFor(() => {
			const calls = fetchSpy.mock.calls.map((c) => c[0].toString());
			expect(calls.some((c) => c.includes('trigger-import'))).toBe(true);
		});
	});

	it('renders the running thumbnail status without progress when total is 0', async () => {
		fetchSpy.mockImplementation(async (url: RequestInfo | URL) => {
			const u = url.toString();
			if (u.includes('thumbnail-status')) {
				return new Response(
					JSON.stringify({
						state: 'RUNNING',
						message: '',
						total: 0,
						processed: 0,
						skipped: 0,
						failed: 0,
						startedAt: null
					}),
					{ status: 200, headers: { 'Content-Type': 'application/json' } }
				);
			}
			return new Response(JSON.stringify({ state: 'IDLE' }), {
				status: 200,
				headers: { 'Content-Type': 'application/json' }
			});
		});

		render(AdminSystemPage, { props: {} });

		await vi.waitFor(() => {
			expect(document.body.textContent).toMatch(/läuft|wird|generier/i);
		});
	});
});
