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

// Extract port from PLAYWRIGHT_BASE_URL or use default (4173 matches playwright.config.ts)
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}`;

// Test credentials for authenticated tests
const ADMIN_EMAIL = 'e2e-dashboard-admin@example.com';
const ADMIN_PASSWORD = 'DashboardTest123';

test.describe('Dashboard', () => {
	/**
	 * Navigate to the dashboard and wait for SvelteKit hydration.
	 * SSR renders the HTML immediately, but event handlers are only
	 * attached after client-side hydration completes.
	 */
	async function goToDashboard(page: import('@playwright/test').Page) {
		await page.goto('/dashboard', { waitUntil: 'networkidle' });
		await expect(page.locator('.demo-controls')).toBeVisible({ timeout: 5000 });
	}

	/**
	 * Switch to a demo role with retry logic to handle hydration timing.
	 * Retries the click until the button's active class confirms the switch.
	 */
	async function switchToDemoRole(
		page: import('@playwright/test').Page,
		roleName: string | RegExp
	) {
		const button = page.locator('.demo-controls button', { hasText: roleName });
		await expect(async () => {
			await button.click();
			await expect(button).toHaveClass(/active/, { timeout: 1000 });
		}).toPass({ timeout: 10000 });
	}

	// ============================================================================
	// Demo Mode (unauthenticated) - Role Switcher
	// ============================================================================
	test.describe('Demo Mode', () => {
		test('shows demo role switcher when not authenticated', async ({ page }) => {
			await goToDashboard(page);

			await expect(page).toHaveURL(/\/dashboard/);
			await expect(page.getByText(/Démo - Changer de rôle/i)).toBeVisible();
		});

		test('page title is set correctly', async ({ page }) => {
			await goToDashboard(page);

			await expect(page).toHaveTitle(/tableau de bord/i);
		});

		test('demo role switcher has all 4 role buttons', async ({ page }) => {
			await goToDashboard(page);

			const demoControls = page.locator('.demo-controls');
			await expect(demoControls).toBeVisible();

			await expect(demoControls.getByRole('button', { name: 'Parent' })).toBeVisible();
			await expect(demoControls.getByRole('button', { name: 'Enseignant' })).toBeVisible();
			await expect(demoControls.getByRole('button', { name: /Élève/i })).toBeVisible();
			await expect(demoControls.getByRole('button', { name: 'Admin' })).toBeVisible();
		});

		test('Parent role is selected by default', async ({ page }) => {
			await goToDashboard(page);

			const parentButton = page.locator('.demo-controls button', { hasText: 'Parent' });
			await expect(parentButton).toHaveClass(/active/);
		});
	});

	// ============================================================================
	// Parent Dashboard View
	// ============================================================================
	test.describe('Parent Dashboard', () => {
		test('shows Score Serenite card', async ({ page }) => {
			await goToDashboard(page);

			// Parent is the default demo role
			await expect(page.getByText(/score sérénité/i).first()).toBeVisible();
		});

		test('shows serenity score with numeric value', async ({ page }) => {
			await goToDashboard(page);

			// The score card should display a number value
			const scoreCard = page.locator('.serenity-card');
			await expect(scoreCard).toBeVisible();

			// Should have a numeric value followed by /100
			await expect(scoreCard.locator('.value')).toBeVisible();
			await expect(scoreCard.getByText('/100')).toBeVisible();
		});

		test('serenity score shows demo badge', async ({ page }) => {
			await goToDashboard(page);

			await expect(page.getByText(/données de démonstration/i)).toBeVisible();
		});

		test('shows placeholder sections for schedule, notes, and homework', async ({ page }) => {
			await goToDashboard(page);

			// These sections show as placeholders since hasRealData is false
			await expect(page.getByRole('heading', { name: /emploi du temps/i })).toBeVisible();
			await expect(page.getByRole('heading', { name: /notes récentes/i })).toBeVisible();
			await expect(page.getByRole('heading', { name: /devoirs à venir/i })).toBeVisible();
		});

		test('placeholder sections show informative messages', async ({ page }) => {
			await goToDashboard(page);

			await expect(page.getByText(/l'emploi du temps sera disponible/i)).toBeVisible();
			await expect(page.getByText(/les notes apparaîtront ici/i)).toBeVisible();
			await expect(page.getByText(/les devoirs seront affichés ici/i)).toBeVisible();
		});

		test('onboarding banner is visible on first login', async ({ page }) => {
			await goToDashboard(page);

			// The onboarding banner should be visible (isFirstLogin=true initially)
			await expect(page.getByText(/bienvenue sur classeo/i)).toBeVisible();
			await expect(page.getByText(/score sérénité/i).first()).toBeVisible();
		});

		test('clicking serenity score opens explainer', async ({ page }) => {
			await goToDashboard(page);

			// Click the serenity score card
			const scoreCard = page.locator('.serenity-card');
			await expect(scoreCard).toBeVisible();
			await scoreCard.click();

			// The explainer modal/overlay should appear
			// SerenityScoreExplainer should be visible after click
			await expect(page.getByText(/cliquez pour en savoir plus/i)).toBeVisible();
		});
	});

	// ============================================================================
	// Teacher Dashboard View
	// ============================================================================
	test.describe('Teacher Dashboard', () => {
		test('shows teacher dashboard header', async ({ page }) => {
			await goToDashboard(page);

			// Switch to teacher
			await switchToDemoRole(page, 'Enseignant');

			await expect(page.getByRole('heading', { name: /tableau de bord enseignant/i })).toBeVisible();
			await expect(page.getByText(/bienvenue.*voici vos outils du jour/i)).toBeVisible();
		});

		test('shows quick action cards', async ({ page }) => {
			await goToDashboard(page);
			await switchToDemoRole(page, 'Enseignant');

			await expect(page.getByText(/faire l'appel/i)).toBeVisible();
			await expect(page.getByText(/saisir des notes/i)).toBeVisible();
			await expect(page.getByText(/créer un devoir/i)).toBeVisible();
		});

		test('quick action cards are disabled in demo mode', async ({ page }) => {
			await goToDashboard(page);
			await switchToDemoRole(page, 'Enseignant');

			// Action cards should be disabled since hasRealData=false
			const actionCards = page.locator('.action-card');
			const count = await actionCards.count();
			expect(count).toBeGreaterThanOrEqual(3);

			for (let i = 0; i < count; i++) {
				await expect(actionCards.nth(i)).toBeDisabled();
			}
		});

		test('shows placeholder sections for teacher data', async ({ page }) => {
			await goToDashboard(page);
			await switchToDemoRole(page, 'Enseignant');

			await expect(page.getByRole('heading', { name: /mes classes aujourd'hui/i })).toBeVisible();
			await expect(page.getByRole('heading', { name: /notes à saisir/i })).toBeVisible();
			await expect(page.getByRole('heading', { name: /appels du jour/i })).toBeVisible();
			await expect(page.getByRole('heading', { name: /statistiques rapides/i })).toBeVisible();
		});

		test('placeholder sections have informative messages', async ({ page }) => {
			await goToDashboard(page);
			await switchToDemoRole(page, 'Enseignant');

			await expect(page.getByText(/vos classes apparaîtront ici/i)).toBeVisible();
			await expect(page.getByText(/évaluations en attente de notation/i)).toBeVisible();
			await expect(page.getByText(/les appels à effectuer/i)).toBeVisible();
			await expect(page.getByText(/les statistiques de vos classes/i)).toBeVisible();
		});
	});

	// ============================================================================
	// Student Dashboard View
	// ============================================================================
	test.describe('Student Dashboard', () => {
		test('shows student dashboard header', async ({ page }) => {
			await goToDashboard(page);

			// Switch to student
			await switchToDemoRole(page, /Élève/i);

			await expect(page.getByRole('heading', { name: /mon espace/i })).toBeVisible();
			// Student is minor by default, so "ton" instead of "votre"
			await expect(page.getByText(/bienvenue.*voici ton tableau de bord/i)).toBeVisible();
		});

		test('shows info banner for student in demo mode', async ({ page }) => {
			await goToDashboard(page);
			await switchToDemoRole(page, /Élève/i);

			await expect(page.getByText(/ton emploi du temps, tes notes et tes devoirs/i)).toBeVisible();
		});

		test('shows placeholder sections for student data', async ({ page }) => {
			await goToDashboard(page);
			await switchToDemoRole(page, /Élève/i);

			await expect(page.getByRole('heading', { name: /mon emploi du temps/i })).toBeVisible();
			await expect(page.getByRole('heading', { name: /mes notes/i })).toBeVisible();
			await expect(page.getByRole('heading', { name: /mes devoirs/i })).toBeVisible();
		});

		test('placeholder sections show minor-appropriate messages', async ({ page }) => {
			await goToDashboard(page);
			await switchToDemoRole(page, /Élève/i);

			// Uses "ton/tes" for minors
			await expect(page.getByText(/ton emploi du temps sera bientôt disponible/i)).toBeVisible();
			await expect(page.getByText(/tes notes apparaîtront ici/i)).toBeVisible();
			await expect(page.getByText(/tes devoirs s'afficheront ici/i)).toBeVisible();
		});
	});

	// ============================================================================
	// Admin Dashboard View
	// ============================================================================
	test.describe('Admin Dashboard', () => {
		test('shows admin dashboard header', async ({ page }) => {
			await goToDashboard(page);

			// Switch to admin
			await switchToDemoRole(page, 'Admin');

			await expect(page.getByRole('heading', { name: /administration/i })).toBeVisible();
		});

		test('shows establishment name', async ({ page }) => {
			await goToDashboard(page);
			await switchToDemoRole(page, 'Admin');

			// Demo data uses "École Alpha" as establishment name
			await expect(page.getByText(/école alpha/i)).toBeVisible();
		});

		test('shows quick action links for admin', async ({ page }) => {
			await goToDashboard(page);
			await switchToDemoRole(page, 'Admin');

			await expect(page.getByText(/gérer les utilisateurs/i)).toBeVisible();
			await expect(page.getByText(/configurer les classes/i)).toBeVisible();
			await expect(page.getByText(/gérer les matières/i)).toBeVisible();
			await expect(page.getByText(/périodes scolaires/i)).toBeVisible();
			await expect(page.getByText(/pédagogie/i)).toBeVisible();
		});

		test('admin quick action links have correct hrefs', async ({ page }) => {
			await goToDashboard(page);
			await switchToDemoRole(page, 'Admin');

			// Verify action cards link to correct pages
			const usersLink = page.locator('.action-card[href="/admin/users"]');
			await expect(usersLink).toBeVisible();

			const classesLink = page.locator('.action-card[href="/admin/classes"]');
			await expect(classesLink).toBeVisible();

			const subjectsLink = page.locator('.action-card[href="/admin/subjects"]');
			await expect(subjectsLink).toBeVisible();

			const periodsLink = page.locator('.action-card[href="/admin/academic-year/periods"]');
			await expect(periodsLink).toBeVisible();

			const pedagogyLink = page.locator('.action-card[href="/admin/pedagogy"]');
			await expect(pedagogyLink).toBeVisible();
		});

		test('shows import action cards for students and teachers', async ({ page }) => {
			await goToDashboard(page);
			await switchToDemoRole(page, 'Admin');

			const studentImport = page.getByRole('link', { name: /importer des élèves/i });
			await expect(studentImport).toBeVisible();
			await expect(studentImport).toHaveAttribute('href', '/admin/import/students');

			const teacherImport = page.getByRole('link', { name: /importer des enseignants/i });
			await expect(teacherImport).toBeVisible();
			await expect(teacherImport).toHaveAttribute('href', '/admin/import/teachers');
		});

		test('shows placeholder sections for admin stats', async ({ page }) => {
			await goToDashboard(page);
			await switchToDemoRole(page, 'Admin');

			await expect(page.getByRole('heading', { name: /utilisateurs/i })).toBeVisible();
			await expect(page.getByRole('heading', { name: 'Configuration', exact: true })).toBeVisible();
			await expect(page.getByRole('heading', { name: /activité récente/i })).toBeVisible();
		});
	});

	// ============================================================================
	// Role Switching
	// ============================================================================
	test.describe('Role Switching', () => {
		test('switching from parent to teacher changes dashboard content', async ({ page }) => {
			await goToDashboard(page);

			// Verify parent view
			await expect(page.getByText(/score sérénité/i).first()).toBeVisible();

			// Switch to teacher
			await switchToDemoRole(page, 'Enseignant');

			// Parent content should be gone
			await expect(page.locator('.serenity-card')).not.toBeVisible();

			// Teacher content should appear
			await expect(page.getByRole('heading', { name: /tableau de bord enseignant/i })).toBeVisible();
		});

		test('switching from teacher to student changes dashboard content', async ({ page }) => {
			await goToDashboard(page);

			// Switch to teacher first
			await switchToDemoRole(page, 'Enseignant');
			await expect(page.getByRole('heading', { name: /tableau de bord enseignant/i })).toBeVisible();

			// Switch to student
			await switchToDemoRole(page, /Élève/i);

			// Teacher content should be gone
			await expect(page.getByRole('heading', { name: /tableau de bord enseignant/i })).not.toBeVisible();

			// Student content should appear
			await expect(page.getByRole('heading', { name: /mon espace/i })).toBeVisible();
		});

		test('switching from student to admin changes dashboard content', async ({ page }) => {
			await goToDashboard(page);

			// Switch to student first
			await switchToDemoRole(page, /Élève/i);
			await expect(page.getByRole('heading', { name: /mon espace/i })).toBeVisible();

			// Switch to admin
			await switchToDemoRole(page, 'Admin');

			// Student content should be gone
			await expect(page.getByRole('heading', { name: /mon espace/i })).not.toBeVisible();

			// Admin content should appear
			await expect(page.getByRole('heading', { name: /administration/i })).toBeVisible();
		});

		test('active role button changes visual state', async ({ page }) => {
			await goToDashboard(page);

			// Parent should be active initially
			const parentBtn = page.locator('.demo-controls button', { hasText: 'Parent' });
			await expect(parentBtn).toHaveClass(/active/);

			// Switch to teacher
			await switchToDemoRole(page, 'Enseignant');

			// Teacher should now be active, parent should not
			const teacherBtn = page.locator('.demo-controls button', { hasText: 'Enseignant' });
			await expect(teacherBtn).toHaveClass(/active/);
			await expect(parentBtn).not.toHaveClass(/active/);
		});

		test('onboarding banner disappears after switching roles', async ({ page }) => {
			await goToDashboard(page);

			// Onboarding banner is visible initially (isFirstLogin=true)
			await expect(page.getByText(/bienvenue sur classeo/i)).toBeVisible();

			// Switch role - this calls switchDemoRole which sets isFirstLogin=false
			await switchToDemoRole(page, 'Enseignant');

			// Switch back to parent
			await switchToDemoRole(page, 'Parent');

			// Onboarding banner should no longer be visible
			await expect(page.getByText(/bienvenue sur classeo/i)).not.toBeVisible();
		});
	});

	// ============================================================================
	// Admin Dashboard - Navigation from Quick Actions
	// ============================================================================
	test.describe('Admin Quick Action Navigation', () => {
		test.beforeAll(async () => {
			const projectRoot = join(__dirname, '../..');
			const composeFile = join(projectRoot, 'compose.yaml');

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

		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('clicking "Gerer les utilisateurs" navigates to users page', async ({ page }) => {
			await loginAsAdmin(page);

			// Admin dashboard should show after login (ROLE_ADMIN maps to admin view)
			await expect(page.getByRole('heading', { name: /administration/i })).toBeVisible({ timeout: 10000 });

			// Click users link
			await page.locator('.action-card[href="/admin/users"]').click();
			await expect(page).toHaveURL(/\/admin\/users/);
		});

		test('clicking "Configurer les classes" navigates to classes page', async ({ page }) => {
			await loginAsAdmin(page);
			await expect(page.getByRole('heading', { name: /administration/i })).toBeVisible({ timeout: 10000 });

			await page.locator('.action-card[href="/admin/classes"]').click();
			await expect(page).toHaveURL(/\/admin\/classes/);
		});

		test('clicking "Gerer les matieres" navigates to subjects page', async ({ page }) => {
			await loginAsAdmin(page);
			await expect(page.getByRole('heading', { name: /administration/i })).toBeVisible({ timeout: 10000 });

			await page.locator('.action-card[href="/admin/subjects"]').click();
			await expect(page).toHaveURL(/\/admin\/subjects/);
		});

		test('clicking "Periodes scolaires" navigates to periods page', async ({ page }) => {
			await loginAsAdmin(page);
			await expect(page.getByRole('heading', { name: /administration/i })).toBeVisible({ timeout: 10000 });

			await page.locator('.action-card[href="/admin/academic-year/periods"]').click();
			await expect(page).toHaveURL(/\/admin\/academic-year\/periods/);
		});

		test('clicking "Pedagogie" navigates to pedagogy page', async ({ page }) => {
			await loginAsAdmin(page);
			await expect(page.getByRole('heading', { name: /administration/i })).toBeVisible({ timeout: 10000 });

			await page.locator('.action-card[href="/admin/pedagogy"]').click();
			await expect(page).toHaveURL(/\/admin\/pedagogy/);
		});
	});

	// ============================================================================
	// Accessibility
	// ============================================================================
	test.describe('Accessibility', () => {
		test('serenity score card has accessible label', async ({ page }) => {
			await goToDashboard(page);

			const scoreCard = page.locator('[aria-label*="Score Sérénité"]');
			await expect(scoreCard).toBeVisible();
		});

		test('teacher quick actions have a visually hidden heading', async ({ page }) => {
			await goToDashboard(page);
			await switchToDemoRole(page, 'Enseignant');

			// The "Actions rapides" heading exists but is sr-only
			const actionsHeading = page.getByRole('heading', { name: /actions rapides/i });
			await expect(actionsHeading).toBeAttached();
		});

		test('admin configuration actions have a visually hidden heading', async ({ page }) => {
			await goToDashboard(page);
			await switchToDemoRole(page, 'Admin');

			const configHeading = page.getByRole('heading', { name: /actions de configuration/i });
			await expect(configHeading).toBeAttached();
		});
	});
});

test.describe('Dashboard Components', () => {
	test('demo data JSON is valid and accessible', async ({ page }) => {
		// This tests that the demo data file is bundled correctly
		await page.goto('/');

		// The app should load without errors
		await expect(page.locator('body')).toBeVisible();
	});
});
