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-parent-import-admin@example.com';
const ADMIN_PASSWORD = 'ParentImportTest123';
const TENANT_ID = 'a1b2c3d4-e5f6-7890-abcd-ef1234567890';

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

const UNIQUE_SUFFIX = Date.now().toString().slice(-8);

// Student IDs — deterministic UUIDs for cleanup
const STUDENT1_ID = `e2e00001-0000-4000-8000-${UNIQUE_SUFFIX}0001`;
const STUDENT2_ID = `e2e00001-0000-4000-8000-${UNIQUE_SUFFIX}0002`;

// Unique student names to avoid collision with existing data
const STUDENT1_FIRST = `Alice${UNIQUE_SUFFIX}`;
const STUDENT1_LAST = `Dupont${UNIQUE_SUFFIX}`;
const STUDENT2_FIRST = `Bob${UNIQUE_SUFFIX}`;
const STUDENT2_LAST = `Martin${UNIQUE_SUFFIX}`;

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

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('Parent Invitation Import via CSV', () => {
	test.describe.configure({ mode: 'serial' });

	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' }
		);

		// Create 2 students with unique names for matching
		// Note: \\" produces \" in the string, which the shell interprets as literal " inside double quotes
		try {
			runCommand(
				`INSERT INTO users (id, tenant_id, email, first_name, last_name, roles, hashed_password, statut, school_name, date_naissance, created_at, activated_at, invited_at, blocked_at, blocked_reason, consentement_parent_id, consentement_eleve_id, consentement_date, consentement_ip, image_rights_status, image_rights_updated_at, image_rights_updated_by, student_number, updated_at) VALUES ('${STUDENT1_ID}', '${TENANT_ID}', NULL, '${STUDENT1_FIRST}', '${STUDENT1_LAST}', '[\\"ROLE_ELEVE\\"]', NULL, 'inscrit', 'E2E Test School', NULL, NOW(), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'not_specified', NULL, NULL, NULL, NOW()) ON CONFLICT (id) DO NOTHING`
			);
		} catch { /* may already exist */ }

		try {
			runCommand(
				`INSERT INTO users (id, tenant_id, email, first_name, last_name, roles, hashed_password, statut, school_name, date_naissance, created_at, activated_at, invited_at, blocked_at, blocked_reason, consentement_parent_id, consentement_eleve_id, consentement_date, consentement_ip, image_rights_status, image_rights_updated_at, image_rights_updated_by, student_number, updated_at) VALUES ('${STUDENT2_ID}', '${TENANT_ID}', NULL, '${STUDENT2_FIRST}', '${STUDENT2_LAST}', '[\\"ROLE_ELEVE\\"]', NULL, 'inscrit', 'E2E Test School', NULL, NOW(), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'not_specified', NULL, NULL, NULL, NOW()) ON CONFLICT (id) DO NOTHING`
			);
		} catch { /* may already exist */ }

		// Clear user cache to ensure students are visible
		try {
			execSync(
				`docker compose -f "${composeFile}" exec -T php php bin/console cache:pool:clear users.cache 2>&1`,
				{ encoding: 'utf-8' }
			);
		} catch { /* ignore */ }
	});

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

		await expect(page.getByRole('heading', { name: /import d'invitations parents/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 élève;Email parent 1;Email parent 2\n${STUDENT1_LAST} ${STUDENT1_FIRST};parent1@test.fr;parent2@test.fr\n`;
		const csvPath = createCsvFixture('e2e-parent-import-test.csv', csvContent);

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

		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-parent-import-test\.csv/i)).toBeVisible();
		await expect(page.getByText(/1 lignes/i)).toBeVisible();

		// Column names should appear in mapping
		await expect(page.locator('.column-name').filter({ hasText: /^Nom élève$/ })).toBeVisible();
		await expect(page.locator('.column-name').filter({ hasText: /^Email parent 1$/ })).toBeVisible();
		await expect(page.locator('.column-name').filter({ hasText: /^Email parent 2$/ })).toBeVisible();

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

	test('validates required fields in mapping', async ({ page }) => {
		const csvContent = `Nom élève;Email parent 1\n${STUDENT1_LAST} ${STUDENT1_FIRST};alice@test.fr\n`;
		const csvPath = createCsvFixture('e2e-parent-import-required.csv', csvContent);

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

		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();
		await expect(validateButton).toBeEnabled();

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

	test('navigates back from mapping to upload', async ({ page }) => {
		const csvContent = `Nom élève;Email parent 1\n${STUDENT1_LAST} ${STUDENT1_FIRST};alice@test.fr\n`;
		const csvPath = createCsvFixture('e2e-parent-import-back.csv', csvContent);

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

		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 pdfPath = createCsvFixture('e2e-parent-import-bad.pdf', 'not a csv file');

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

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

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

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

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

	test('shows preview step with valid/error counts', async ({ page }) => {
		// Use only one row with a clearly non-existent student to verify error display
		const csvContent =
			'Nom élève;Email parent 1\nZzznotfound99 Xxxxnomatch88;parent.err@test.fr\n';
		const csvPath = createCsvFixture('e2e-parent-import-preview.csv', csvContent);

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

		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 0 valid and 1 error
		await expect(page.locator('.summary-card.valid')).toBeVisible();
		await expect(page.locator('.summary-card.valid .summary-number')).toHaveText('0');
		await expect(page.locator('.summary-card.error')).toBeVisible();
		await expect(page.locator('.summary-card.error .summary-number')).toHaveText('1');

		// Error detail should mention the unknown student
		await expect(page.locator('.error-detail').first()).toContainText(/non trouvé/i);

		// Send button should be disabled (no valid rows)
		await expect(page.getByRole('button', { name: /envoyer/i })).toBeDisabled();

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

	test('[P0] completes full import flow', async ({ page }) => {
		const email1 = `parent.import.${UNIQUE_SUFFIX}@test.fr`;
		const csvContent = `Nom élève;Email parent 1\n${STUDENT1_LAST} ${STUDENT1_FIRST};${email1}\n`;
		const csvPath = createCsvFixture('e2e-parent-import-full-flow.csv', csvContent);

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

		// 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 expect(page.locator('.summary-card.valid .summary-number')).toHaveText('1');
		await page.getByRole('button', { name: /envoyer 1 invitation/i }).click();

		// Step 4: Result
		await expect(page.getByRole('heading', { name: /invitations envoyées/i })).toBeVisible({ timeout: 30000 });

		// Verify report stats
		const stats = page.locator('.report-stats .stat');
		const sentStat = stats.filter({ hasText: /envoyées/ });
		await expect(sentStat.locator('.stat-value')).toHaveText('1');
		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 invitations/i })).toBeVisible();

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

		// Cleanup: remove the created invitation
		try {
			runCommand(`DELETE FROM parent_invitations WHERE student_id = '${STUDENT1_ID}' AND parent_email = '${email1}'`);
		} catch { /* ignore */ }
	});

	test('[P1] handles multiple emails per student', async ({ page }) => {
		const email1 = `parent1.multi.${UNIQUE_SUFFIX}@test.fr`;
		const email2 = `parent2.multi.${UNIQUE_SUFFIX}@test.fr`;
		const csvContent = `Nom élève;Email parent 1;Email parent 2\n${STUDENT2_LAST} ${STUDENT2_FIRST};${email1};${email2}\n`;
		const csvPath = createCsvFixture('e2e-parent-import-multi-email.csv', csvContent);

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

		// 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 — should show 1 valid row
		await expect(page.locator('.preview-summary')).toBeVisible({ timeout: 15000 });
		await expect(page.locator('.summary-card.valid .summary-number')).toHaveText('1');

		// Send — 2 invitations (one per email)
		await page.getByRole('button', { name: /envoyer 1 invitation/i }).click();

		// Result
		await expect(page.locator('.report-stats')).toBeVisible({ timeout: 30000 });

		// Should have created 2 invitations (email1 + email2)
		const stats = page.locator('.report-stats .stat');
		const sentStat = stats.filter({ hasText: /envoyées/ });
		await expect(sentStat.locator('.stat-value')).toHaveText('2');

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

		// Cleanup
		try {
			runCommand(`DELETE FROM parent_invitations WHERE student_id = '${STUDENT2_ID}' AND parent_email IN ('${email1}', '${email2}')`);
		} catch { /* ignore */ }
	});

	test('[P1] shows invalid email errors in preview', async ({ page }) => {
		const csvContent = `Nom élève;Email parent 1\n${STUDENT1_LAST} ${STUDENT1_FIRST};not-an-email\n`;
		const csvPath = createCsvFixture('e2e-parent-import-invalid-email.csv', csvContent);

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

		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 — should show 0 valid, 1 error
		await expect(page.locator('.preview-summary')).toBeVisible({ timeout: 15000 });
		await expect(page.locator('.summary-card.valid .summary-number')).toHaveText('0');
		await expect(page.locator('.summary-card.error .summary-number')).toHaveText('1');

		// Error detail should mention invalid email
		await expect(page.locator('.error-detail').first()).toContainText(/invalide/i);

		// Send button should be disabled (0 valid rows)
		await expect(page.getByRole('button', { name: /envoyer/i })).toBeDisabled();

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

	test('[P2] shows preview of first 5 lines in mapping step', async ({ page }) => {
		const csvContent = [
			'Nom élève;Email parent 1',
			'Eleve Un;parent1@test.fr',
			'Eleve Deux;parent2@test.fr',
			'Eleve Trois;parent3@test.fr',
			'Eleve Quatre;parent4@test.fr',
			'Eleve Cinq;parent5@test.fr',
			'Eleve Six;parent6@test.fr',
			'Eleve Sept;parent7@test.fr'
		].join('\n') + '\n';
		const csvPath = createCsvFixture('e2e-parent-import-preview-5.csv', csvContent);

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

		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 exactly 5 rows in the preview table (not 7)
		await expect(page.locator('.preview-table tbody tr')).toHaveCount(5);

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

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

	test.afterAll(async () => {
		// Clean up test students
		try {
			runCommand(`DELETE FROM parent_invitations WHERE student_id IN ('${STUDENT1_ID}', '${STUDENT2_ID}')`);
			runCommand(`DELETE FROM users WHERE id IN ('${STUDENT1_ID}', '${STUDENT2_ID}')`);
		} catch { /* ignore */ }

		// Clear cache
		try {
			execSync(
				`docker compose -f "${composeFile}" exec -T php php bin/console cache:pool:clear users.cache 2>&1`,
				{ encoding: 'utf-8' }
			);
		} catch { /* ignore */ }
	});
});
