import { test, expect } from '@playwright/test';
import { execSync } from 'child_process';
import { join, dirname } from 'path';
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-inv-admin@example.com';
const ADMIN_PASSWORD = 'ParentInvTest123';
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);
const STUDENT_ID = `e2e00002-0000-4000-8000-${UNIQUE_SUFFIX}0001`;
const PARENT_EMAIL = `e2e-parent-inv-${UNIQUE_SUFFIX}@test.fr`;

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()
	]);
}

test.describe('Parent Invitations', () => {
	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 a student with known name for invite tests
		// 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 ('${STUDENT_ID}', '${TENANT_ID}', NULL, 'Camille', 'Testinv', '[\\"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 */ }

		// Clean up invitations from previous runs
		try {
			runCommand(`DELETE FROM parent_invitations WHERE student_id = '${STUDENT_ID}' OR parent_email = '${PARENT_EMAIL}'`);
		} catch { /* ignore */ }

		// Clear user 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 */ }
	});

	test('admin can navigate to parent invitations page', async ({ page }) => {
		await loginAsAdmin(page);
		await page.goto(`${ALPHA_URL}/admin/parent-invitations`);

		// Page should load (empty state or table)
		await expect(
			page.locator('.data-table, .empty-state')
		).toBeVisible({ timeout: 10000 });

		// Title should be visible
		await expect(page.getByRole('heading', { name: /invitations parents/i })).toBeVisible();
	});

	test('admin sees empty state or data table', async ({ page }) => {
		await loginAsAdmin(page);
		await page.goto(`${ALPHA_URL}/admin/parent-invitations`);

		// Wait for page to load — either empty state or data table
		await expect(
			page.locator('.data-table, .empty-state')
		).toBeVisible({ timeout: 10000 });

		// Verify whichever state is shown has correct content
		const emptyState = page.locator('.empty-state');
		const dataTable = page.locator('.data-table');
		const isEmptyStateVisible = await emptyState.isVisible();

		if (isEmptyStateVisible) {
			await expect(emptyState.getByText(/aucune invitation/i)).toBeVisible();
		} else {
			await expect(dataTable).toBeVisible();
		}
	});

	test('admin can open the invite modal', async ({ page }) => {
		await loginAsAdmin(page);
		await page.goto(`${ALPHA_URL}/admin/parent-invitations`);

		await expect(
			page.locator('.data-table, .empty-state')
		).toBeVisible({ timeout: 10000 });

		// Click "Inviter les parents" button
		await page.getByRole('button', { name: /inviter les parents/i }).first().click();

		// Modal should appear
		await expect(page.locator('#invite-modal-title')).toBeVisible();
		await expect(page.locator('#invite-modal-title')).toHaveText('Inviter les parents');

		// Form fields should be visible
		await expect(page.locator('#invite-student')).toBeVisible();
		await expect(page.locator('#invite-email1')).toBeVisible();
		await expect(page.locator('#invite-email2')).toBeVisible();
	});

	test('admin can close the invite modal with Escape', async ({ page }) => {
		await loginAsAdmin(page);
		await page.goto(`${ALPHA_URL}/admin/parent-invitations`);

		await expect(
			page.locator('.data-table, .empty-state')
		).toBeVisible({ timeout: 10000 });

		// Open modal
		await page.getByRole('button', { name: /inviter les parents/i }).first().click();
		await expect(page.locator('#invite-modal-title')).toBeVisible();

		// Close with Escape
		await page.keyboard.press('Escape');
		await expect(page.locator('#invite-modal-title')).not.toBeVisible();
	});

	test('send invitation requires student and email', async ({ page }) => {
		await loginAsAdmin(page);
		await page.goto(`${ALPHA_URL}/admin/parent-invitations`);

		await expect(
			page.locator('.data-table, .empty-state')
		).toBeVisible({ timeout: 10000 });

		// Open modal
		await page.getByRole('button', { name: /inviter les parents/i }).first().click();
		await expect(page.locator('#invite-modal-title')).toBeVisible();

		// Submit button should be disabled when empty
		const submitBtn = page.locator('.modal').getByRole('button', { name: /envoyer l'invitation/i });
		await expect(submitBtn).toBeDisabled();
	});

	test('[P0] admin can create an invitation via modal', async ({ page }) => {
		await loginAsAdmin(page);
		await page.goto(`${ALPHA_URL}/admin/parent-invitations`);

		await expect(
			page.locator('.data-table, .empty-state')
		).toBeVisible({ timeout: 10000 });

		// Open modal
		await page.getByRole('button', { name: /inviter les parents/i }).first().click();
		await expect(page.locator('#invite-modal-title')).toBeVisible();

		// Wait for students to load in select (more than just the default empty option)
		await expect(page.locator('#invite-student option')).not.toHaveCount(1, { timeout: 10000 });

		// Select the first available student (not the placeholder)
		const firstStudentOption = page.locator('#invite-student option:not([value=""])').first();
		await expect(firstStudentOption).toBeAttached({ timeout: 10000 });
		const studentValue = await firstStudentOption.getAttribute('value');
		await page.locator('#invite-student').selectOption(studentValue!);

		// Fill parent email
		await page.locator('#invite-email1').fill(PARENT_EMAIL);

		// Submit button should be enabled
		const submitBtn = page.locator('.modal').getByRole('button', { name: /envoyer l'invitation/i });
		await expect(submitBtn).toBeEnabled();

		// Submit
		await submitBtn.click();

		// Modal should close and success message should appear
		await expect(page.locator('#invite-modal-title')).not.toBeVisible({ timeout: 10000 });
		await expect(page.locator('.alert-success')).toBeVisible({ timeout: 10000 });
		await expect(page.locator('.alert-success')).toContainText(/invitation.*envoyée/i);
	});

	test('[P0] invitation appears in the table after creation', async ({ page }) => {
		await loginAsAdmin(page);
		await page.goto(`${ALPHA_URL}/admin/parent-invitations`);

		// Wait for table to load (should no longer be empty state)
		await expect(page.locator('.data-table')).toBeVisible({ timeout: 10000 });

		// The invitation should appear with the parent email
		await expect(page.locator('.data-table').getByText(PARENT_EMAIL)).toBeVisible();

		// Student name should appear (any student name in the row)
		const invitationRow = page.locator('tr').filter({ hasText: PARENT_EMAIL });
		await expect(invitationRow).toBeVisible();

		// Status should be "Envoyée"
		await expect(page.locator('.data-table .status-badge').first()).toContainText(/envoyée/i);
	});

	test('[P1] admin can resend an invitation', async ({ page }) => {
		await loginAsAdmin(page);
		await page.goto(`${ALPHA_URL}/admin/parent-invitations`);

		await expect(page.locator('.data-table')).toBeVisible({ timeout: 10000 });

		// Find the row with our invitation and click "Renvoyer"
		const row = page.locator('tr').filter({ hasText: PARENT_EMAIL });
		await expect(row).toBeVisible();
		await row.getByRole('button', { name: /renvoyer/i }).click();

		// Success message should appear
		await expect(page.locator('.alert-success')).toBeVisible({ timeout: 10000 });
		await expect(page.locator('.alert-success')).toContainText(/renvoyée/i);
	});

	test('admin can navigate to file import page', async ({ page }) => {
		await loginAsAdmin(page);
		await page.goto(`${ALPHA_URL}/admin/parent-invitations`);

		await expect(
			page.locator('.data-table, .empty-state')
		).toBeVisible({ timeout: 10000 });

		// Click "Importer un fichier" link
		await page.getByRole('link', { name: /importer un fichier/i }).click();

		// Should navigate to the import wizard page
		await expect(page).toHaveURL(/\/admin\/import\/parents/);
		await expect(page.getByRole('heading', { name: /import d'invitations parents/i })).toBeVisible({
			timeout: 15000
		});
	});

	test('filter by status changes the URL', async ({ page }) => {
		await loginAsAdmin(page);
		await page.goto(`${ALPHA_URL}/admin/parent-invitations`);

		await expect(
			page.locator('.data-table, .empty-state')
		).toBeVisible({ timeout: 10000 });

		// Select a status filter
		await page.locator('#filter-status').selectOption('sent');
		await page.getByRole('button', { name: /filtrer/i }).click();

		// URL should have status param
		await expect(page).toHaveURL(/status=sent/);
	});

	test('reset filters clears URL params', async ({ page }) => {
		await loginAsAdmin(page);
		await page.goto(`${ALPHA_URL}/admin/parent-invitations?status=sent`);

		await expect(
			page.locator('.data-table, .empty-state')
		).toBeVisible({ timeout: 10000 });

		// Click reset (exact match to avoid ambiguity with "Réinitialiser les filtres" in empty state)
		await page.getByRole('button', { name: 'Réinitialiser', exact: true }).click();

		// URL should no longer contain status param
		await expect(page).not.toHaveURL(/status=/);
	});

	test('[P1] filter by sent status shows the created invitation', async ({ page }) => {
		await loginAsAdmin(page);
		await page.goto(`${ALPHA_URL}/admin/parent-invitations`);

		await expect(
			page.locator('.data-table, .empty-state')
		).toBeVisible({ timeout: 10000 });

		// Filter by "sent" status
		await page.locator('#filter-status').selectOption('sent');
		await page.getByRole('button', { name: /filtrer/i }).click();

		// Our invitation should still be visible
		await expect(page.locator('.data-table')).toBeVisible({ timeout: 10000 });
		await expect(page.locator('.data-table').getByText(PARENT_EMAIL)).toBeVisible();
	});

	test.afterAll(async () => {
		// Clean up invitations (by student or by email) and student
		try {
			runCommand(`DELETE FROM parent_invitations WHERE student_id = '${STUDENT_ID}' OR parent_email = '${PARENT_EMAIL}'`);
			runCommand(`DELETE FROM users WHERE id = '${STUDENT_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 */ }
	});
});

test.describe('Parent Activation Page', () => {
	test('displays form for parent activation page', async ({ page }) => {
		// Navigate to the parent activation page with a dummy code
		await page.goto('/parent-activate/test-code-that-does-not-exist');

		// Page should load
		await expect(page.getByRole('heading', { name: /activation.*parent/i })).toBeVisible();

		// Form fields should be visible
		await expect(page.locator('#firstName')).toBeVisible();
		await expect(page.locator('#lastName')).toBeVisible();
		await expect(page.locator('#password')).toBeVisible();
		await expect(page.locator('#passwordConfirmation')).toBeVisible();
	});

	test('validates password requirements in real-time', async ({ page }) => {
		await page.goto('/parent-activate/test-code');

		await expect(page.locator('#password')).toBeVisible({ timeout: 5000 });

		// Type a weak password
		await page.locator('#password').fill('abc');

		// Check that requirements are shown
		const requirements = page.locator('.password-requirements');
		await expect(requirements).toBeVisible();

		// Min length should NOT be valid
		const minLengthItem = requirements.locator('li').filter({ hasText: /8 caractères/ });
		await expect(minLengthItem).not.toHaveClass(/valid/);

		// Type a strong password
		await page.locator('#password').fill('StrongP@ss1');

		// All requirements should be valid
		const allItems = requirements.locator('li.valid');
		await expect(allItems).toHaveCount(5);
	});

	test('validates password confirmation match', async ({ page }) => {
		await page.goto('/parent-activate/test-code');

		await expect(page.locator('#password')).toBeVisible({ timeout: 5000 });

		await page.locator('#password').fill('StrongP@ss1');
		await page.locator('#passwordConfirmation').fill('DifferentPass');

		// Error should show
		await expect(page.getByText(/ne correspondent pas/i)).toBeVisible();
	});

	test('submit button is disabled until form is valid', async ({ page }) => {
		await page.goto('/parent-activate/test-code');

		await expect(page.locator('#password')).toBeVisible({ timeout: 5000 });

		// Submit should be disabled initially
		const submitBtn = page.getByRole('button', { name: /activer mon compte/i });
		await expect(submitBtn).toBeDisabled();

		// Fill all fields with valid data
		await page.locator('#firstName').fill('Jean');
		await page.locator('#lastName').fill('Parent');
		await page.locator('#password').fill('StrongP@ss1');
		await page.locator('#passwordConfirmation').fill('StrongP@ss1');

		// Submit should be enabled
		await expect(submitBtn).toBeEnabled();
	});
});
