import { test, expect, type Browser } from '@playwright/test';

/**
 * Admin panel E2E tests.
 *
 * Reads top-to-bottom as a complete admin journey:
 * 1. Admin opens the dashboard and sees all three management tabs.
 * 2. Admin creates a group for read-only access.
 * 3. Admin creates a new user in that group.
 * 4. Admin edits the user's profile.
 * 5. Admin resets the user's password without knowing their current password.
 * 6. The user can log in with the admin-set password.
 * 7. Admin deletes the user.
 * 8. Admin deletes the test group.
 * 9. Admin renames a tag and renames it back.
 *
 * Steps 2–8 form a self-contained lifecycle: everything created in this suite
 * is also deleted, leaving the database in its original state.
 */

// ── Dashboard ─────────────────────────────────────────────────────────────────

test.describe('Admin dashboard', () => {
	test('admin navigates to /admin and sees the three management tabs', async ({ page }) => {
		await page.goto('/admin');
		await page.waitForSelector('[data-hydrated]');

		await expect(page.getByRole('button', { name: 'Benutzer', exact: true })).toBeVisible();
		await expect(page.getByRole('button', { name: 'Gruppen', exact: true })).toBeVisible();
		await expect(page.getByRole('button', { name: 'Schlagworte', exact: true })).toBeVisible();
		await page.screenshot({ path: 'test-results/e2e/admin-dashboard.png' });
	});
});

// ── Group lifecycle ────────────────────────────────────────────────────────────

test.describe('Admin — group management', () => {
	test('admin creates a new group "E2E Leser" with READ_ALL permission', async ({ page }) => {
		await page.goto('/admin');
		await page.waitForSelector('[data-hydrated]');

		// Switch to the Groups tab
		await page.getByRole('button', { name: 'Gruppen', exact: true }).click();

		await page.getByPlaceholder('Gruppenname (z.B. Editoren)').fill('E2E Leser');

		// No permission checkboxes checked — READ_ALL is handled at application level
		// (a group with no permissions gets read-only access by default in the UI)

		await page.getByRole('button', { name: /Erstellen/i }).click();

		await expect(page.getByRole('cell', { name: 'E2E Leser', exact: true })).toBeVisible();
		await page.screenshot({ path: 'test-results/e2e/admin-group-created.png' });
	});
});

// ── User lifecycle ─────────────────────────────────────────────────────────────

test.describe('Admin — user lifecycle', () => {
	test('admin creates user "e2e-testuser" and they appear in the user list', async ({ page }) => {
		await page.goto('/admin/users/new');
		await page.waitForSelector('[data-hydrated]');

		await page.locator('input[name="username"]').fill('e2e-testuser');
		await page.locator('input[name="password"]').fill('InitPass123!');

		// Assign to the group we just created
		const groupLabel = page.locator('label').filter({ hasText: 'E2E Leser' });
		if ((await groupLabel.count()) > 0) {
			await groupLabel.locator('input[type="checkbox"]').check();
		}

		await page.getByRole('button', { name: /Erstellen/i }).click();

		// Redirected back to /admin — user appears in the table
		await expect(page).toHaveURL('/admin');
		await expect(page.getByRole('cell', { name: 'e2e-testuser', exact: true })).toBeVisible();
		await page.screenshot({ path: 'test-results/e2e/admin-user-created.png' });
	});

	test('admin opens the edit page and updates the user first name', async ({ page }) => {
		await page.goto('/admin');
		await page.waitForSelector('[data-hydrated]');

		// Click the edit link for the test user
		const userRow = page.locator('tr').filter({ hasText: 'e2e-testuser' });
		await userRow.getByRole('link', { name: /Bearbeiten/i }).click();

		await expect(page).toHaveURL(/\/admin\/users\/.+/);
		await expect(
			page.getByRole('heading', { name: /Benutzer bearbeiten: e2e-testuser/i })
		).toBeVisible();

		await page.locator('input[name="firstName"]').fill('E2E');
		await page.locator('input[name="lastName"]').fill('Testuser');

		await page.getByRole('button', { name: /Speichern/i }).click();

		await expect(page.getByText('Änderungen gespeichert.')).toBeVisible();
		await page.screenshot({ path: 'test-results/e2e/admin-user-edited.png' });
	});

	test('admin sets a new password without entering the current password', async ({ page }) => {
		await page.goto('/admin');
		await page.waitForSelector('[data-hydrated]');

		const userRow = page.locator('tr').filter({ hasText: 'e2e-testuser' });
		await userRow.getByRole('link', { name: /Bearbeiten/i }).click();

		// Password fields — no current password field on the admin edit form
		await page.locator('input[name="newPassword"]').fill('AdminSet456!');
		await page.locator('input[name="confirmPassword"]').fill('AdminSet456!');

		await page.getByRole('button', { name: /Speichern/i }).click();

		await expect(page.getByText('Änderungen gespeichert.')).toBeVisible();
		await page.screenshot({ path: 'test-results/e2e/admin-user-password-reset.png' });
	});

	test('the user can log in with the admin-set password', async ({ browser }) => {
		// Open a completely separate browser context — no shared session cookies
		const freshCtx = await (browser as Browser).newContext({
			storageState: { cookies: [], origins: [] }
		});
		const freshPage = await freshCtx.newPage();

		await freshPage.goto('/login');
		await freshPage.getByLabel('Benutzername').fill('e2e-testuser');
		await freshPage.getByLabel('Passwort').fill('AdminSet456!');
		await freshPage.getByRole('button', { name: 'Anmelden' }).click();

		await expect(freshPage).toHaveURL('/');
		await freshPage.screenshot({ path: 'test-results/e2e/admin-user-login-new-password.png' });

		await freshCtx.close();
	});

	test('admin deletes the test user and they disappear from the list', async ({ page }) => {
		await page.goto('/admin');
		await page.waitForSelector('[data-hydrated]');

		const userRow = page.locator('tr').filter({ hasText: 'e2e-testuser' });

		// The delete button triggers a window.confirm() dialog
		page.once('dialog', (dialog) => dialog.accept());
		await userRow.getByTitle('Benutzer löschen').click();

		await expect(page.getByRole('cell', { name: 'e2e-testuser', exact: true })).not.toBeVisible();
		await page.screenshot({ path: 'test-results/e2e/admin-user-deleted.png' });
	});
});

// ── Group cleanup ──────────────────────────────────────────────────────────────

test.describe('Admin — group cleanup', () => {
	test('admin deletes the "E2E Leser" group', async ({ page }) => {
		await page.goto('/admin');
		await page.waitForSelector('[data-hydrated]');

		await page.getByRole('button', { name: 'Gruppen' }).click();

		const groupRow = page.locator('tr').filter({ hasText: 'E2E Leser' });

		page.once('dialog', (dialog) => dialog.accept());
		await groupRow.getByTitle('Löschen').click();

		await expect(page.getByRole('cell', { name: 'E2E Leser', exact: true })).not.toBeVisible();
		await page.screenshot({ path: 'test-results/e2e/admin-group-deleted.png' });
	});
});

// ── Tag management ─────────────────────────────────────────────────────────────

test.describe('Admin — tag management', () => {
	test('admin renames a tag and sees the change in the list', async ({ page }) => {
		await page.goto('/admin');
		await page.waitForSelector('[data-hydrated]');

		await page.getByRole('button', { name: 'Schlagworte', exact: true }).click();
		// Wait for the tags list to render after the tab switch
		await page.waitForSelector('ul > li');

		// Hover over the "Familie" row to reveal the opacity-0 action buttons
		const familieRow = page
			.locator('ul > li')
			.filter({ has: page.locator('span', { hasText: /^Familie$/ }) });
		await familieRow.hover();
		await familieRow.getByRole('button', { name: 'Schlagwort bearbeiten' }).click();

		// After clicking edit, {#if editingTagId} replaces the span with a form —
		// the familieRow filter no longer matches, so we find the input directly.
		await page.locator('input[name="name"]').fill('Familie (E2E)');
		await page.getByRole('button', { name: 'Speichern' }).click();

		await expect(page.getByText('Familie (E2E)')).toBeVisible();
		await page.screenshot({ path: 'test-results/e2e/admin-tag-renamed.png' });
	});

	test('admin renames it back to restore the original name', async ({ page }) => {
		await page.goto('/admin');
		await page.waitForSelector('[data-hydrated]');

		await page.getByRole('button', { name: 'Schlagworte', exact: true }).click();
		await page.waitForSelector('ul > li');

		const renamedRow = page
			.locator('ul > li')
			.filter({ has: page.locator('span', { hasText: /^Familie \(E2E\)$/ }) });
		await renamedRow.hover();
		await renamedRow.getByRole('button', { name: 'Schlagwort bearbeiten' }).click();

		await page.locator('input[name="name"]').fill('Familie');
		await page.getByRole('button', { name: 'Speichern' }).click();

		await expect(page.getByText('Familie')).toBeVisible();
		await page.screenshot({ path: 'test-results/e2e/admin-tag-restored.png' });
	});
});

// ─── System tab — mass import trigger (J12) ───────────────────────────────────

test.describe('Admin system tab — mass import trigger', () => {
	test('admin triggers mass import and sees a status response', async ({ page }) => {
		test.setTimeout(30_000);

		await page.goto('/admin');
		await page.waitForSelector('[data-hydrated]');

		await page.getByRole('button', { name: /system/i }).click();

		// The import button is rendered as [data-import-trigger] in all states.
		const importBtn = page.locator('[data-import-trigger]');
		await expect(importBtn.first()).toBeVisible({ timeout: 10_000 });
		await importBtn.first().click();

		// After triggering, a status message specific to the import operation appears.
		await expect(
			page.locator('text=/Import läuft|Import abgeschlossen|Fehler:/').first()
		).toBeVisible({ timeout: 15_000 });

		await page.screenshot({ path: 'test-results/e2e/admin-mass-import-triggered.png' });
	});
});

// ─── System tab — backfill file hashes ────────────────────────────────────────

test.describe('Admin system tab — backfill file hashes', () => {
	test('admin triggers file hash backfill and sees success message', async ({ request, page }) => {
		test.setTimeout(60_000);

		// Create a document via API so there is at least one without a hash
		const createRes = await request.post('/api/documents', {
			multipart: { title: 'E2E Backfill Hash Test' }
		});
		if (!createRes.ok()) throw new Error(`Create failed: ${createRes.status()}`);

		await page.goto('/admin');
		await page.waitForSelector('[data-hydrated]');

		// Navigate to System tab
		await page.getByRole('button', { name: /system/i }).click();

		// Click the backfill hashes button
		const btn = page.getByRole('button', { name: /datei-hashes berechnen/i });
		await expect(btn).toBeVisible();
		await btn.click();

		// Success message must appear (count >= 0)
		await expect(page.locator('text=/\\d+ Dokumente wurden aktualisiert/i')).toBeVisible({
			timeout: 15000
		});

		await page.screenshot({ path: 'test-results/e2e/admin-backfill-hashes.png' });
	});
});

// ─── System tab — generate thumbnails ─────────────────────────────────────────

test.describe('Admin system tab — generate thumbnails', () => {
	test('admin triggers thumbnail generation and sees DONE within 30s', async ({ page }) => {
		test.setTimeout(45_000);

		await page.goto('/admin');
		await page.waitForSelector('[data-hydrated]');

		// Navigate to System tab
		await page.getByRole('button', { name: /system/i }).click();

		const btn = page
			.locator('[data-thumbnails-trigger]')
			.or(page.getByRole('button', { name: /thumbnails erzeugen/i }));
		await expect(btn.first()).toBeVisible();
		await btn.first().click();

		// Status transitions RUNNING → DONE; poll message shows the final summary
		await expect(page.getByTestId('thumbnails-status-done')).toBeVisible({ timeout: 30_000 });

		await page.screenshot({ path: 'test-results/e2e/admin-generate-thumbnails.png' });
	});
});
