import { test, expect } from '@playwright/test';
import { execSync } from 'child_process';
import { join, dirname } from 'path';
import { writeFileSync, mkdirSync, unlinkSync } from 'fs';
import { fileURLToPath } from 'url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

const baseUrl = process.env.PLAYWRIGHT_BASE_URL || 'http://localhost:4173';
const urlMatch = baseUrl.match(/:(\d+)$/);
const PORT = urlMatch ? urlMatch[1] : '4173';
const ALPHA_URL = `http://ecole-alpha.classeo.local:${PORT}`;

const ADMIN_EMAIL = 'e2e-teacher-import-admin@example.com';
const ADMIN_PASSWORD = 'TeacherImportTest123';
const TENANT_ID = 'a1b2c3d4-e5f6-7890-abcd-ef1234567890';

const projectRoot = join(__dirname, '../..');
const composeFile = join(projectRoot, 'compose.yaml');

function runCommand(sql: string) {
	execSync(
		`docker compose -f "${composeFile}" exec -T php php bin/console dbal:run-sql "${sql}" 2>&1`,
		{ encoding: 'utf-8' }
	);
}

function resolveDeterministicIds(): { schoolId: string; academicYearId: string } {
	const output = execSync(
		`docker compose -f "${composeFile}" exec -T php php -r '` +
			`require "/app/vendor/autoload.php"; ` +
			`$t="${TENANT_ID}"; $ns="6ba7b814-9dad-11d1-80b4-00c04fd430c8"; ` +
			`echo Ramsey\\Uuid\\Uuid::uuid5($ns,"school-$t")->toString()."\\n"; ` +
			`$m=(int)date("n"); $s=$m>=9?(int)date("Y"):(int)date("Y")-1; $e=$s+1; ` +
			`echo Ramsey\\Uuid\\Uuid::uuid5($ns,"$t:$s-$e")->toString();` +
			`' 2>&1`,
		{ encoding: 'utf-8' }
	).trim();
	const [schoolId, academicYearId] = output.split('\n');
	return { schoolId, academicYearId };
}

async function loginAsAdmin(page: import('@playwright/test').Page) {
	await page.goto(`${ALPHA_URL}/login`);
	await page.locator('#email').fill(ADMIN_EMAIL);
	await page.locator('#password').fill(ADMIN_PASSWORD);
	await Promise.all([
		page.waitForURL(/\/dashboard/, { timeout: 30000 }),
		page.getByRole('button', { name: /se connecter/i }).click()
	]);
}

function createCsvFixture(filename: string, content: string): string {
	const tmpDir = join(__dirname, 'fixtures');
	mkdirSync(tmpDir, { recursive: true });
	const filePath = join(tmpDir, filename);
	writeFileSync(filePath, content, 'utf-8');
	return filePath;
}

test.describe('Teacher Import via CSV', () => {
	test.describe.configure({ mode: 'serial' });

	let subjectId: string;

	test.beforeAll(async () => {
		// Create admin user
		execSync(
			`docker compose -f "${composeFile}" exec -T php php bin/console app:dev:create-test-user --tenant=ecole-alpha --email=${ADMIN_EMAIL} --password=${ADMIN_PASSWORD} --role=ROLE_ADMIN 2>&1`,
			{ encoding: 'utf-8' }
		);

		const { schoolId } = resolveDeterministicIds();
		const suffix = Date.now().toString().slice(-8);
		subjectId = `00000200-e2e0-4000-8000-${suffix}0001`;

		// Create a subject for valid import rows
		try {
			runCommand(
				`INSERT INTO subjects (id, tenant_id, school_id, name, code, color, status, created_at, updated_at) VALUES ('${subjectId}', '${TENANT_ID}', '${schoolId}', 'E2E Maths', 'MATH', NULL, 'active', NOW(), NOW()) ON CONFLICT (id) DO NOTHING`
			);
		} catch {
			// Subject may already exist
		}

		// Clean up auto-created subjects from previous runs
		try {
			runCommand(`DELETE FROM teacher_assignments WHERE subject_id IN (SELECT id FROM subjects WHERE tenant_id = '${TENANT_ID}' AND name LIKE 'E2E AutoSubject%')`);
			runCommand(`DELETE FROM subjects WHERE tenant_id = '${TENANT_ID}' AND name LIKE 'E2E AutoSubject%'`);
		} catch { /* ignore */ }

		// Clean up saved column mappings from previous runs to avoid stale mapping suggestions
		try {
			runCommand(`DELETE FROM saved_teacher_column_mappings WHERE tenant_id = '${TENANT_ID}'`);
		} catch { /* ignore */ }
	});

	test('displays the import wizard page', async ({ page }) => {
		await loginAsAdmin(page);
		await page.goto(`${ALPHA_URL}/admin/import/teachers`);

		await expect(page.getByRole('heading', { name: /import d'enseignants/i })).toBeVisible({
			timeout: 15000
		});

		// Verify stepper is visible with 4 steps
		await expect(page.locator('.stepper .step')).toHaveCount(4);

		// Verify dropzone is visible
		await expect(page.locator('.dropzone')).toBeVisible();
		await expect(page.getByText(/glissez votre fichier/i)).toBeVisible();
	});

	test('uploads a CSV file and shows mapping step', async ({ page }) => {
		const csvContent = 'Nom;Prénom;Email\nDupont;Jean;jean.dupont@ecole.fr\nMartin;Marie;marie.martin@ecole.fr\n';
		const csvPath = createCsvFixture('e2e-teacher-import-test.csv', csvContent);

		await loginAsAdmin(page);
		await page.goto(`${ALPHA_URL}/admin/import/teachers`);

		await expect(page.locator('.dropzone')).toBeVisible({ timeout: 15000 });

		const fileInput = page.locator('input[type="file"]');
		await fileInput.setInputFiles(csvPath);

		// Should transition to mapping step
		await expect(page.getByText(/association des colonnes/i)).toBeVisible({ timeout: 15000 });

		// File info should be visible
		await expect(page.getByText(/e2e-teacher-import-test\.csv/i)).toBeVisible();
		await expect(page.getByText(/2 lignes/i)).toBeVisible();

		// Column names should appear in mapping
		await expect(page.locator('.column-name').filter({ hasText: /^Nom$/ })).toBeVisible();
		await expect(page.locator('.column-name').filter({ hasText: /^Prénom$/ })).toBeVisible();
		await expect(page.locator('.column-name').filter({ hasText: /^Email$/ })).toBeVisible();

		try { unlinkSync(csvPath); } catch { /* ignore */ }
	});

	test('validates required fields in mapping', async ({ page }) => {
		const csvContent = 'Nom;Prénom;Email\nDupont;Jean;jean@ecole.fr\n';
		const csvPath = createCsvFixture('e2e-teacher-import-required.csv', csvContent);

		await loginAsAdmin(page);
		await page.goto(`${ALPHA_URL}/admin/import/teachers`);

		await expect(page.locator('.dropzone')).toBeVisible({ timeout: 15000 });

		const fileInput = page.locator('input[type="file"]');
		await fileInput.setInputFiles(csvPath);

		await expect(page.getByText(/association des colonnes/i)).toBeVisible({ timeout: 15000 });

		// The mapping should be auto-suggested, so the "Valider le mapping" button should be enabled
		const validateButton = page.getByRole('button', { name: /valider le mapping/i });
		await expect(validateButton).toBeVisible();

		try { unlinkSync(csvPath); } catch { /* ignore */ }
	});

	test('navigates back from mapping to upload', async ({ page }) => {
		const csvContent = 'Nom;Prénom;Email\nDupont;Jean;jean@ecole.fr\n';
		const csvPath = createCsvFixture('e2e-teacher-import-back.csv', csvContent);

		await loginAsAdmin(page);
		await page.goto(`${ALPHA_URL}/admin/import/teachers`);

		await expect(page.locator('.dropzone')).toBeVisible({ timeout: 15000 });

		const fileInput = page.locator('input[type="file"]');
		await fileInput.setInputFiles(csvPath);

		await expect(page.getByText(/association des colonnes/i)).toBeVisible({ timeout: 15000 });

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

		// Should be back on upload step
		await expect(page.locator('.dropzone')).toBeVisible({ timeout: 10000 });

		try { unlinkSync(csvPath); } catch { /* ignore */ }
	});

	test('rejects non-CSV files', async ({ page }) => {
		const txtPath = createCsvFixture('e2e-teacher-import-bad.pdf', 'not a csv file');

		await loginAsAdmin(page);
		await page.goto(`${ALPHA_URL}/admin/import/teachers`);

		await expect(page.locator('.dropzone')).toBeVisible({ timeout: 15000 });

		const fileInput = page.locator('input[type="file"]');
		await fileInput.setInputFiles(txtPath);

		// Should show error
		await expect(page.locator('.alert-error')).toBeVisible({ timeout: 10000 });

		try { unlinkSync(txtPath); } catch { /* ignore */ }
	});

	test('shows preview step with valid/error counts', async ({ page }) => {
		const csvContent =
			'Nom;Prénom;Email\nDupont;Jean;jean.preview@ecole.fr\n;Marie;marie@ecole.fr\nMartin;;martin@ecole.fr\n';
		const csvPath = createCsvFixture('e2e-teacher-import-preview.csv', csvContent);

		await loginAsAdmin(page);
		await page.goto(`${ALPHA_URL}/admin/import/teachers`);

		await expect(page.locator('.dropzone')).toBeVisible({ timeout: 15000 });

		const fileInput = page.locator('input[type="file"]');
		await fileInput.setInputFiles(csvPath);

		// Wait for mapping step
		await expect(page.getByText(/association des colonnes/i)).toBeVisible({ timeout: 15000 });

		// Submit mapping
		await page.getByRole('button', { name: /valider le mapping/i }).click();

		// Wait for preview step
		await expect(page.locator('.preview-summary')).toBeVisible({ timeout: 15000 });

		// Should show valid and error counts
		await expect(page.locator('.summary-card.valid')).toBeVisible();
		await expect(page.locator('.summary-card.error')).toBeVisible();

		try { unlinkSync(csvPath); } catch { /* ignore */ }
	});

	test('[P0] completes full import flow with progress and report', async ({ page }) => {
		const suffix = Date.now().toString().slice(-6);
		const email1 = `alice.prof.${suffix}@ecole.fr`;
		const email2 = `bob.prof.${suffix}@ecole.fr`;
		const csvContent = `Nom;Prénom;Email\nTestProf${suffix};Alice;${email1}\nTestProf${suffix};Bob;${email2}\n`;
		const csvPath = createCsvFixture('e2e-teacher-import-full-flow.csv', csvContent);

		await loginAsAdmin(page);
		await page.goto(`${ALPHA_URL}/admin/import/teachers`);

		// Step 1: Upload
		await expect(page.locator('.dropzone')).toBeVisible({ timeout: 15000 });
		await page.locator('input[type="file"]').setInputFiles(csvPath);

		// Step 2: Mapping
		await expect(page.getByText(/association des colonnes/i)).toBeVisible({ timeout: 15000 });
		await page.getByRole('button', { name: /valider le mapping/i }).click();

		// Step 3: Preview
		await expect(page.locator('.preview-summary')).toBeVisible({ timeout: 15000 });
		await page.getByRole('button', { name: /lancer l'import/i }).click();

		// Step 4: Confirmation — wait for completion
		await expect(page.getByRole('heading', { name: /import terminé/i })).toBeVisible({ timeout: 60000 });

		// Verify report stats
		const stats = page.locator('.report-stats .stat');
		const importedStat = stats.filter({ hasText: /importés/ });
		await expect(importedStat.locator('.stat-value')).toHaveText('2');
		const errorStat = stats.filter({ hasText: /erreurs/ });
		await expect(errorStat.locator('.stat-value')).toHaveText('0');

		// Verify action buttons
		await expect(page.getByRole('button', { name: /voir les utilisateurs/i })).toBeVisible();

		try { unlinkSync(csvPath); } catch { /* ignore */ }

		// Cleanup
		try {
			runCommand(`DELETE FROM teacher_assignments WHERE teacher_id IN (SELECT id FROM users WHERE tenant_id = '${TENANT_ID}' AND email IN ('${email1}', '${email2}'))`);
			runCommand(`DELETE FROM users WHERE tenant_id = '${TENANT_ID}' AND email IN ('${email1}', '${email2}')`);
		} catch { /* ignore */ }
	});

	test('[P1] imports only valid rows when errors exist', async ({ page }) => {
		const suffix = Date.now().toString().slice(-6);
		const validEmail = `sophie.durand.${suffix}@ecole.fr`;
		const csvContent = `Nom;Prénom;Email\nDurand${suffix};Sophie;${validEmail}\n;Marie;marie.err@ecole.fr\nMartin;;martin.err@ecole.fr\n`;
		const csvPath = createCsvFixture('e2e-teacher-import-valid-only.csv', csvContent);

		await loginAsAdmin(page);
		await page.goto(`${ALPHA_URL}/admin/import/teachers`);

		// Upload
		await expect(page.locator('.dropzone')).toBeVisible({ timeout: 15000 });
		await page.locator('input[type="file"]').setInputFiles(csvPath);

		// Mapping
		await expect(page.getByText(/association des colonnes/i)).toBeVisible({ timeout: 15000 });
		await page.getByRole('button', { name: /valider le mapping/i }).click();

		// Preview
		await expect(page.locator('.preview-summary')).toBeVisible({ timeout: 15000 });

		// Verify error count
		await expect(page.locator('.summary-card.error .summary-number')).toHaveText('2');

		// Verify error detail rows are visible
		await expect(page.locator('.error-detail').first()).toBeVisible();

		// "Import valid only" radio should be selected by default
		const validOnlyRadio = page.locator('input[type="radio"][name="importMode"][value="true"]');
		await expect(validOnlyRadio).toBeChecked();

		// Launch import (should only import 1 valid row)
		await page.getByRole('button', { name: /lancer l'import/i }).click();

		// Wait for completion
		await expect(page.getByRole('heading', { name: /import terminé/i })).toBeVisible({ timeout: 60000 });

		// Verify only 1 teacher imported
		const stats = page.locator('.report-stats .stat');
		const importedStat = stats.filter({ hasText: /importés/ });
		await expect(importedStat.locator('.stat-value')).toHaveText('1');

		try { unlinkSync(csvPath); } catch { /* ignore */ }

		// Cleanup
		try {
			runCommand(`DELETE FROM teacher_assignments WHERE teacher_id IN (SELECT id FROM users WHERE tenant_id = '${TENANT_ID}' AND email = '${validEmail}')`);
			runCommand(`DELETE FROM users WHERE tenant_id = '${TENANT_ID}' AND email = '${validEmail}'`);
		} catch { /* ignore */ }
	});

	test('[P1] shows unknown subjects and allows auto-creation', async ({ page }) => {
		const suffix = Date.now().toString().slice(-6);
		const subjectName = `E2E AutoSubject${suffix}`;
		const email = `paul.lemaire.${suffix}@ecole.fr`;
		const csvContent = `Nom;Prénom;Email;Matières\nLemaire${suffix};Paul;${email};${subjectName}\n`;
		const csvPath = createCsvFixture('e2e-teacher-import-auto-subject.csv', csvContent);

		await loginAsAdmin(page);
		await page.goto(`${ALPHA_URL}/admin/import/teachers`);

		// Upload
		await expect(page.locator('.dropzone')).toBeVisible({ timeout: 15000 });
		await page.locator('input[type="file"]').setInputFiles(csvPath);

		// Mapping
		await expect(page.getByText(/association des colonnes/i)).toBeVisible({ timeout: 15000 });
		await page.getByRole('button', { name: /valider le mapping/i }).click();

		// Preview
		await expect(page.locator('.preview-summary')).toBeVisible({ timeout: 15000 });

		// Verify unknown subjects section
		await expect(page.locator('.unknown-items').first()).toBeVisible({ timeout: 10000 });
		await expect(page.locator('.item-tag').first()).toContainText(subjectName);

		// Check auto-create checkbox
		await page.locator('.unknown-items input[type="checkbox"]').check();

		// Launch import
		await page.getByRole('button', { name: /lancer l'import/i }).click();

		// Wait for completion
		await expect(page.getByRole('heading', { name: /import terminé/i })).toBeVisible({ timeout: 60000 });

		// Verify teacher imported
		const stats = page.locator('.report-stats .stat');
		const importedStat = stats.filter({ hasText: /importés/ });
		await expect(importedStat.locator('.stat-value')).toHaveText('1');

		try { unlinkSync(csvPath); } catch { /* ignore */ }

		// Cleanup
		try {
			runCommand(`DELETE FROM teacher_assignments WHERE teacher_id IN (SELECT id FROM users WHERE tenant_id = '${TENANT_ID}' AND email = '${email}')`);
			runCommand(`DELETE FROM subjects WHERE tenant_id = '${TENANT_ID}' AND name = '${subjectName}'`);
			runCommand(`DELETE FROM users WHERE tenant_id = '${TENANT_ID}' AND email = '${email}'`);
		} catch { /* ignore */ }
	});

	test('[P2] shows preview of first 5 lines in mapping step', async ({ page }) => {
		const csvContent = [
			'Nom;Prénom;Email',
			'Alpha;Un;alpha.un@ecole.fr',
			'Bravo;Deux;bravo.deux@ecole.fr',
			'Charlie;Trois;charlie.trois@ecole.fr',
			'Delta;Quatre;delta.quatre@ecole.fr',
			'Echo;Cinq;echo.cinq@ecole.fr',
			'Foxtrot;Six;foxtrot.six@ecole.fr',
			'Golf;Sept;golf.sept@ecole.fr',
			'Hotel;Huit;hotel.huit@ecole.fr'
		].join('\n') + '\n';
		const csvPath = createCsvFixture('e2e-teacher-import-preview-5.csv', csvContent);

		await loginAsAdmin(page);
		await page.goto(`${ALPHA_URL}/admin/import/teachers`);

		await expect(page.locator('.dropzone')).toBeVisible({ timeout: 15000 });
		await page.locator('input[type="file"]').setInputFiles(csvPath);

		// Wait for mapping step
		await expect(page.getByText(/association des colonnes/i)).toBeVisible({ timeout: 15000 });

		// Verify preview section exists
		await expect(page.locator('.preview-section')).toBeVisible();

		// Verify heading shows 5 premières lignes
		await expect(page.locator('.preview-section h3')).toContainText('5 premières lignes');

		// Verify exactly 5 rows in the preview table (not 8)
		await expect(page.locator('.preview-table tbody tr')).toHaveCount(5);

		// Verify total row count in file info
		await expect(page.getByText(/8 lignes/i)).toBeVisible();

		try { unlinkSync(csvPath); } catch { /* ignore */ }
	});

	test('[P1] skips teachers with duplicate emails and shows duplicate card', async ({ page }) => {
		const DUPLICATE_EMAIL = 'e2e-duplicate-teacher@ecole.fr';
		const UNIQUE_EMAIL = `e2e-unique-teacher-${Date.now()}@ecole.fr`;

		// Clean up any stale user from previous runs (DB + cache) to avoid cache/DB desync
		try {
			runCommand(`DELETE FROM teacher_assignments WHERE teacher_id IN (SELECT id FROM users WHERE tenant_id = '${TENANT_ID}' AND email = '${DUPLICATE_EMAIL}')`);
			runCommand(`DELETE FROM users WHERE tenant_id = '${TENANT_ID}' AND email = '${DUPLICATE_EMAIL}'`);
		} catch { /* ignore */ }
		execSync(
			`docker compose -f "${composeFile}" exec -T php php bin/console cache:pool:clear users.cache 2>&1`,
			{ encoding: 'utf-8' }
		);

		// Create a pre-existing user with the duplicate email
		execSync(
			`docker compose -f "${composeFile}" exec -T php php bin/console app:dev:create-test-user --tenant=ecole-alpha --email=${DUPLICATE_EMAIL} --password=Unused123 --role=ROLE_PROF 2>&1`,
			{ encoding: 'utf-8' }
		);

		const csvContent = `Nom;Prénom;Email\nExistant;Dupli;${DUPLICATE_EMAIL}\nNouveau;Unique;${UNIQUE_EMAIL}\n`;
		const csvPath = createCsvFixture('e2e-teacher-import-duplicate-email.csv', csvContent);

		try {
			await loginAsAdmin(page);
			await page.goto(`${ALPHA_URL}/admin/import/teachers`);

			// Step 1: Upload
			await expect(page.locator('.dropzone')).toBeVisible({ timeout: 15000 });
			await page.locator('input[type="file"]').setInputFiles(csvPath);

			// Step 2: Mapping
			await expect(page.getByText(/association des colonnes/i)).toBeVisible({ timeout: 15000 });
			await page.getByRole('button', { name: /valider le mapping/i }).click();

			// Step 3: Preview — duplicates are detected and shown in the summary
			await expect(page.locator('.preview-summary')).toBeVisible({ timeout: 15000 });

			// Verify duplicate card is visible
			await expect(page.locator('.summary-card.duplicate')).toBeVisible({ timeout: 10000 });
			await expect(page.locator('.summary-card.duplicate .summary-number')).toHaveText('1');

			// "Ignorer les doublons" radio should be default
			const ignoreRadio = page.locator('input[type="radio"][name="duplicateMode"][value="false"]');
			await expect(ignoreRadio).toBeChecked();

			// Launch import with "ignore" mode (default)
			await page.getByRole('button', { name: /lancer l'import/i }).click();

			// Step 4: Report — wait for completion
			await expect(page.getByRole('heading', { name: /import terminé/i })).toBeVisible({ timeout: 60000 });

			// Verify report: 1 imported (unique email), 0 errors (duplicate was filtered by "import valid only")
			const stats = page.locator('.report-stats .stat');
			const importedStat = stats.filter({ hasText: /importés/ });
			await expect(importedStat.locator('.stat-value')).toHaveText('1');
			const errorStat = stats.filter({ hasText: /erreurs/ });
			await expect(errorStat.locator('.stat-value')).toHaveText('0');
		} finally {
			try { unlinkSync(csvPath); } catch { /* ignore */ }

			// Cleanup: remove both the pre-existing and imported users (DB + cache)
			try {
				runCommand(`DELETE FROM teacher_assignments WHERE teacher_id IN (SELECT id FROM users WHERE tenant_id = '${TENANT_ID}' AND email IN ('${DUPLICATE_EMAIL}', '${UNIQUE_EMAIL}'))`);
				runCommand(`DELETE FROM users WHERE tenant_id = '${TENANT_ID}' AND email IN ('${DUPLICATE_EMAIL}', '${UNIQUE_EMAIL}')`);
			} catch { /* ignore */ }
			try {
				execSync(
					`docker compose -f "${composeFile}" exec -T php php bin/console cache:pool:clear users.cache 2>&1`,
					{ encoding: 'utf-8' }
				);
			} catch { /* ignore */ }
		}
	});
});
