import { test, expect } from '@playwright/test';
import { execWithRetry, runSql, clearCache, resolveDeterministicIds, createTestUser, composeFile } from './helpers';

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 STUDENT_EMAIL = 'e2e-student-grades@example.com';
const STUDENT_PASSWORD = 'StudentGrades123';
const TEACHER_EMAIL = 'e2e-sg-teacher@example.com';
const TEACHER_PASSWORD = 'TeacherGrades123';
const TENANT_ID = 'a1b2c3d4-e5f6-7890-abcd-ef1234567890';

let classId: string;
let subjectId: string;
let subject2Id: string;
let studentId: string;
let evalId1: string;
let evalId2: string;
let periodId: string;

function uuid5(name: string): string {
	return execWithRetry(
		`docker compose -f "${composeFile}" exec -T php php -r '` +
			`require "/app/vendor/autoload.php"; ` +
			`echo Ramsey\\Uuid\\Uuid::uuid5("6ba7b814-9dad-11d1-80b4-00c04fd430c8","${name}")->toString();` +
			`' 2>&1`
	).trim();
}

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

test.describe('Student Grade Consultation (Story 6.6)', () => {
	test.describe.configure({ mode: 'serial' });

	test.beforeAll(async () => {
		// Create users
		createTestUser('ecole-alpha', STUDENT_EMAIL, STUDENT_PASSWORD, 'ROLE_ELEVE --firstName=Émilie --lastName=Dubois');
		createTestUser('ecole-alpha', TEACHER_EMAIL, TEACHER_PASSWORD, 'ROLE_PROF');

		const { schoolId, academicYearId } = resolveDeterministicIds(TENANT_ID);

		// Resolve student ID
		const idOutput = execWithRetry(
			`docker compose -f "${composeFile}" exec -T php php bin/console dbal:run-sql "SELECT id FROM users WHERE email='${STUDENT_EMAIL}' AND tenant_id='${TENANT_ID}'" 2>&1`
		);
		const idMatch = idOutput.match(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/);
		studentId = idMatch![0]!;

		// Create deterministic IDs
		classId = uuid5(`sg-class-${TENANT_ID}`);
		subjectId = uuid5(`sg-subject1-${TENANT_ID}`);
		subject2Id = uuid5(`sg-subject2-${TENANT_ID}`);
		evalId1 = uuid5(`sg-eval1-${TENANT_ID}`);
		evalId2 = uuid5(`sg-eval2-${TENANT_ID}`);
		periodId = uuid5(`sg-period-${TENANT_ID}`);

		// Create class
		runSql(
			`INSERT INTO school_classes (id, tenant_id, school_id, academic_year_id, name, level, status, created_at, updated_at) ` +
				`VALUES ('${classId}', '${TENANT_ID}', '${schoolId}', '${academicYearId}', 'E2E-SG-4A', '4ème', 'active', NOW(), NOW()) ON CONFLICT DO NOTHING`
		);

		// Create subjects
		runSql(
			`INSERT INTO subjects (id, tenant_id, school_id, name, code, status, created_at, updated_at) ` +
				`VALUES ('${subjectId}', '${TENANT_ID}', '${schoolId}', 'E2E-SG-Mathématiques', 'E2ESGMATH', 'active', NOW(), NOW()) ON CONFLICT DO NOTHING`
		);
		runSql(
			`INSERT INTO subjects (id, tenant_id, school_id, name, code, status, created_at, updated_at) ` +
				`VALUES ('${subject2Id}', '${TENANT_ID}', '${schoolId}', 'E2E-SG-Français', 'E2ESGFRA', 'active', NOW(), NOW()) ON CONFLICT DO NOTHING`
		);

		// Assign student to class
		runSql(
			`INSERT INTO class_assignments (id, tenant_id, user_id, school_class_id, academic_year_id, assigned_at, created_at, updated_at) ` +
				`VALUES (gen_random_uuid(), '${TENANT_ID}', '${studentId}', '${classId}', '${academicYearId}', NOW(), NOW(), NOW()) ON CONFLICT (user_id, academic_year_id) DO NOTHING`
		);

		// Create teacher assignment
		runSql(
			`INSERT INTO teacher_assignments (id, tenant_id, teacher_id, school_class_id, subject_id, academic_year_id, status, start_date, created_at, updated_at) ` +
				`SELECT gen_random_uuid(), '${TENANT_ID}', u.id, '${classId}', '${subjectId}', '${academicYearId}', 'active', NOW(), NOW(), NOW() ` +
				`FROM users u WHERE u.email = '${TEACHER_EMAIL}' AND u.tenant_id = '${TENANT_ID}' ` +
				`ON CONFLICT DO NOTHING`
		);
		runSql(
			`INSERT INTO teacher_assignments (id, tenant_id, teacher_id, school_class_id, subject_id, academic_year_id, status, start_date, created_at, updated_at) ` +
				`SELECT gen_random_uuid(), '${TENANT_ID}', u.id, '${classId}', '${subject2Id}', '${academicYearId}', 'active', NOW(), NOW(), NOW() ` +
				`FROM users u WHERE u.email = '${TEACHER_EMAIL}' AND u.tenant_id = '${TENANT_ID}' ` +
				`ON CONFLICT DO NOTHING`
		);

		// Create published evaluation 1 (Maths - older)
		runSql(
			`INSERT INTO evaluations (id, tenant_id, class_id, subject_id, teacher_id, title, evaluation_date, grade_scale, coefficient, status, grades_published_at, created_at, updated_at) ` +
				`SELECT '${evalId1}', '${TENANT_ID}', '${classId}', '${subjectId}', u.id, 'DS Mathématiques', '2026-03-01', 20, 2.0, 'published', NOW(), NOW(), NOW() ` +
				`FROM users u WHERE u.email='${TEACHER_EMAIL}' AND u.tenant_id='${TENANT_ID}' ` +
				`ON CONFLICT (id) DO NOTHING`
		);

		// Create published evaluation 2 (Français - more recent)
		runSql(
			`INSERT INTO evaluations (id, tenant_id, class_id, subject_id, teacher_id, title, evaluation_date, grade_scale, coefficient, status, grades_published_at, created_at, updated_at) ` +
				`SELECT '${evalId2}', '${TENANT_ID}', '${classId}', '${subject2Id}', u.id, 'Dictée', '2026-03-15', 20, 1.0, 'published', NOW(), NOW(), NOW() ` +
				`FROM users u WHERE u.email='${TEACHER_EMAIL}' AND u.tenant_id='${TENANT_ID}' ` +
				`ON CONFLICT (id) DO NOTHING`
		);

		// Insert grades
		runSql(
			`INSERT INTO grades (id, tenant_id, evaluation_id, student_id, value, status, created_by, created_at, updated_at, appreciation) ` +
				`SELECT gen_random_uuid(), '${TENANT_ID}', '${evalId1}', '${studentId}', 16.5, 'graded', u.id, NOW(), NOW(), 'Très bon travail' ` +
				`FROM users u WHERE u.email='${TEACHER_EMAIL}' AND u.tenant_id='${TENANT_ID}' ` +
				`ON CONFLICT (evaluation_id, student_id) DO NOTHING`
		);
		runSql(
			`INSERT INTO grades (id, tenant_id, evaluation_id, student_id, value, status, created_by, created_at, updated_at) ` +
				`SELECT gen_random_uuid(), '${TENANT_ID}', '${evalId2}', '${studentId}', 14.0, 'graded', u.id, NOW(), NOW() ` +
				`FROM users u WHERE u.email='${TEACHER_EMAIL}' AND u.tenant_id='${TENANT_ID}' ` +
				`ON CONFLICT (evaluation_id, student_id) DO NOTHING`
		);

		// Insert class statistics
		runSql(
			`INSERT INTO evaluation_statistics (evaluation_id, average, min_grade, max_grade, median_grade, graded_count, updated_at) ` +
				`VALUES ('${evalId1}', 14.2, 8.0, 18.5, 14.5, 25, NOW()) ON CONFLICT (evaluation_id) DO NOTHING`
		);
		runSql(
			`INSERT INTO evaluation_statistics (evaluation_id, average, min_grade, max_grade, median_grade, graded_count, updated_at) ` +
				`VALUES ('${evalId2}', 12.8, 6.0, 17.0, 13.0, 25, NOW()) ON CONFLICT (evaluation_id) DO NOTHING`
		);

		// Find the academic period covering the current date (needed for /me/averages auto-detection)
		const periodOutput = execWithRetry(
			`docker compose -f "${composeFile}" exec -T php php bin/console dbal:run-sql "SELECT id FROM academic_periods WHERE tenant_id='${TENANT_ID}' AND start_date <= CURRENT_DATE AND end_date >= CURRENT_DATE LIMIT 1" 2>&1`
		);
		const periodMatch = periodOutput.match(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/);
		periodId = periodMatch ? periodMatch[0]! : uuid5(`sg-period-${TENANT_ID}`);

		// Insert student averages (subject + general)
		runSql(
			`INSERT INTO student_averages (id, tenant_id, student_id, subject_id, period_id, average, grade_count, updated_at) ` +
				`VALUES (gen_random_uuid(), '${TENANT_ID}', '${studentId}', '${subjectId}', '${periodId}', 16.5, 1, NOW()) ` +
				`ON CONFLICT (student_id, subject_id, period_id) DO NOTHING`
		);
		runSql(
			`INSERT INTO student_averages (id, tenant_id, student_id, subject_id, period_id, average, grade_count, updated_at) ` +
				`VALUES (gen_random_uuid(), '${TENANT_ID}', '${studentId}', '${subject2Id}', '${periodId}', 14.0, 1, NOW()) ` +
				`ON CONFLICT (student_id, subject_id, period_id) DO NOTHING`
		);
		runSql(
			`INSERT INTO student_general_averages (id, tenant_id, student_id, period_id, average, updated_at) ` +
				`VALUES (gen_random_uuid(), '${TENANT_ID}', '${studentId}', '${periodId}', 15.25, NOW()) ` +
				`ON CONFLICT (student_id, period_id) DO NOTHING`
		);

		clearCache();
	});

	// =========================================================================
	// AC2: Dashboard notes — grades and averages visible
	// =========================================================================

	test('AC2: student sees recent grades on dashboard', async ({ page }) => {
		await loginAsStudent(page);

		// Dashboard should show grades widget
		const gradesSection = page.locator('.grades-list');
		await expect(gradesSection).toBeVisible({ timeout: 15000 });

		// Should show at least one grade
		const gradeItems = page.locator('.grade-item');
		await expect(gradeItems.first()).toBeVisible({ timeout: 10000 });
	});

	test('AC2: student navigates to full grades page', async ({ page }) => {
		await loginAsStudent(page);
		await page.goto(`${ALPHA_URL}/dashboard/student-grades`);

		// Page title
		await expect(page.getByRole('heading', { name: 'Mes notes' })).toBeVisible({ timeout: 15000 });

		// Should show grade cards
		const gradeCards = page.locator('.grade-card');
		await expect(gradeCards.first()).toBeVisible({ timeout: 10000 });

		// Should show both grades (Dictée more recent first)
		await expect(gradeCards).toHaveCount(2);
	});

	test('AC2: grades show value, subject, and evaluation title', async ({ page }) => {
		await loginAsStudent(page);
		await page.goto(`${ALPHA_URL}/dashboard/student-grades`);

		await expect(page.locator('.grade-card').first()).toBeVisible({ timeout: 15000 });

		// Check first grade (Dictée - more recent)
		const firstCard = page.locator('.grade-card').first();
		await expect(firstCard.locator('.grade-subject-btn')).toContainText('E2E-SG-Français');
		await expect(firstCard.locator('.grade-eval-title')).toContainText('Dictée');
		await expect(firstCard.locator('.grade-value')).toContainText('14/20');

		// Check second grade (DS Maths)
		const secondCard = page.locator('.grade-card').nth(1);
		await expect(secondCard.locator('.grade-subject-btn')).toContainText('E2E-SG-Mathématiques');
		await expect(secondCard.locator('.grade-value')).toContainText('16.5/20');
	});

	test('AC2: class statistics visible on grades', async ({ page }) => {
		await loginAsStudent(page);
		await page.goto(`${ALPHA_URL}/dashboard/student-grades`);

		await expect(page.locator('.grade-card').first()).toBeVisible({ timeout: 15000 });

		const firstStats = page.locator('.grade-card').first().locator('.grade-card-stats');
		await expect(firstStats).toContainText('Moy. classe');
	});

	test('AC2: appreciation visible on grade', async ({ page }) => {
		await loginAsStudent(page);
		await page.goto(`${ALPHA_URL}/dashboard/student-grades`);

		await expect(page.locator('.grade-card').first()).toBeVisible({ timeout: 15000 });

		// The Maths grade has an appreciation
		await expect(page.locator('.grade-appreciation').first()).toContainText('Très bon travail');
	});

	// =========================================================================
	// AC3: Subject detail — click on subject shows all evaluations
	// =========================================================================

	test('AC3: click on subject shows detail modal', async ({ page }) => {
		await loginAsStudent(page);
		await page.goto(`${ALPHA_URL}/dashboard/student-grades`);

		// Wait for averages section
		const avgCard = page.locator('.average-card').first();
		await expect(avgCard).toBeVisible({ timeout: 15000 });

		// Click on first subject average card
		await avgCard.click();

		// Modal should appear
		const modal = page.getByRole('dialog');
		await expect(modal).toBeVisible({ timeout: 5000 });

		// Modal should show grade details
		await expect(modal.locator('.detail-item')).toHaveCount(1);
	});

	test('AC3: subject detail modal closes with Escape', async ({ page }) => {
		await loginAsStudent(page);
		await page.goto(`${ALPHA_URL}/dashboard/student-grades`);

		const avgCard = page.locator('.average-card').first();
		await expect(avgCard).toBeVisible({ timeout: 15000 });

		await avgCard.click();

		const modal = page.getByRole('dialog');
		await expect(modal).toBeVisible({ timeout: 5000 });

		await page.keyboard.press('Escape');
		await expect(modal).not.toBeVisible({ timeout: 5000 });
	});

	// =========================================================================
	// AC4: Discover mode — notes hidden by default, click to reveal
	// =========================================================================

	test('AC4: discover mode toggle exists', async ({ page }) => {
		await loginAsStudent(page);
		await page.goto(`${ALPHA_URL}/dashboard/student-grades`);

		await expect(page.locator('.discover-toggle')).toBeVisible({ timeout: 15000 });
		await expect(page.locator('.toggle-label')).toContainText('Mode découverte');
	});

	test('AC4: enabling discover mode hides grade values', async ({ page }) => {
		await loginAsStudent(page);
		await page.goto(`${ALPHA_URL}/dashboard/student-grades`);

		await expect(page.locator('.grade-card').first()).toBeVisible({ timeout: 15000 });

		// Enable discover mode
		const toggle = page.locator('.discover-toggle input');
		await toggle.check();

		// Grades should show reveal buttons instead of values
		await expect(page.locator('.reveal-btn').first()).toBeVisible({ timeout: 5000 });
		await expect(page.locator('.reveal-hint').first()).toContainText('Cliquer pour révéler');
	});

	test('AC4: clicking reveal shows the grade value', async ({ page }) => {
		await loginAsStudent(page);
		await page.goto(`${ALPHA_URL}/dashboard/student-grades`);

		await expect(page.locator('.grade-card').first()).toBeVisible({ timeout: 15000 });

		// Enable discover mode
		await page.locator('.discover-toggle input').check();

		await expect(page.locator('.reveal-btn').first()).toBeVisible({ timeout: 5000 });

		// Click to reveal first grade
		await page.locator('.reveal-btn').first().click();

		// Grade value should now be visible
		await expect(page.locator('.grade-card').first().locator('.grade-value')).toBeVisible({ timeout: 5000 });
	});

	// =========================================================================
	// AC5: Badge "Nouveau" on recent grades
	// =========================================================================

	test('AC5: new grades show Nouveau badge', async ({ page }) => {
		// Clear localStorage to simulate fresh session
		await page.goto(`${ALPHA_URL}/login`);
		await page.evaluate(() => {
			localStorage.removeItem('classeo_grades_seen');
			localStorage.removeItem('classeo_grade_preferences');
			localStorage.removeItem('classeo_grades_revealed');
		});

		await loginAsStudent(page);
		await page.goto(`${ALPHA_URL}/dashboard/student-grades`);

		await expect(page.locator('.grade-card').first()).toBeVisible({ timeout: 15000 });

		// Badges should be visible on new grades
		await expect(page.locator('.badge-new').first()).toBeVisible({ timeout: 5000 });
	});

	// =========================================================================
	// AC2: Averages section visible
	// =========================================================================

	test('AC2: subject averages section displays correctly', async ({ page }) => {
		await loginAsStudent(page);
		await page.goto(`${ALPHA_URL}/dashboard/student-grades`);

		// Wait for averages section
		const avgSection = page.locator('.averages-section');
		await expect(avgSection).toBeVisible({ timeout: 15000 });

		// Should show heading
		await expect(avgSection.getByRole('heading', { name: 'Moyennes par matière' })).toBeVisible();

		// Should show at least one average card
		const avgCards = page.locator('.average-card');
		await expect(avgCards.first()).toBeVisible({ timeout: 10000 });
	});

	test('AC2: general average visible on grades page', async ({ page }) => {
		await loginAsStudent(page);
		await page.goto(`${ALPHA_URL}/dashboard/student-grades`);

		const generalAvg = page.locator('.general-average');
		await expect(generalAvg).toBeVisible({ timeout: 15000 });
		await expect(generalAvg).toContainText('Moyenne générale');
		await expect(generalAvg.locator('.avg-value')).toContainText('/20');
	});

	// =========================================================================
	// AC4: Discover mode persistence
	// =========================================================================

	test('AC4: discover mode persists after page reload', async ({ page }) => {
		await loginAsStudent(page);
		await page.goto(`${ALPHA_URL}/dashboard/student-grades`);

		await expect(page.locator('.grade-card').first()).toBeVisible({ timeout: 15000 });

		// Enable discover mode
		await page.locator('.discover-toggle input').check();
		await expect(page.locator('.reveal-btn').first()).toBeVisible({ timeout: 5000 });

		// Reload the page
		await page.reload();

		// Discover mode should still be active after reload
		await expect(page.locator('.grade-card').first()).toBeVisible({ timeout: 15000 });
		await expect(page.locator('.reveal-btn').first()).toBeVisible({ timeout: 5000 });

		// Disable discover mode for cleanup
		await page.locator('.discover-toggle input').uncheck();
	});

	// =========================================================================
	// Navigation
	// =========================================================================

	test('student can navigate to grades page from nav bar', async ({ page }) => {
		await loginAsStudent(page);

		const navLink = page.getByRole('link', { name: /mes notes/i });
		await expect(navLink).toBeVisible({ timeout: 15000 });

		await navLink.click();
		await page.waitForURL(/student-grades/, { timeout: 10000 });

		await expect(page.getByRole('heading', { name: 'Mes notes' })).toBeVisible({ timeout: 10000 });
	});
});
