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 TEACHER_EMAIL = 'e2e-grade-teacher@example.com';
const TEACHER_PASSWORD = 'GradeTest123';
const TENANT_ID = 'a1b2c3d4-e5f6-7890-abcd-ef1234567890';

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

// Deterministic IDs for test data
let evaluationId: string;
let classId: string;
let student1Id: string;
let student2Id: string;

test.describe('Grade Input Grid (Story 6.2)', () => {
	test.beforeAll(async () => {
		// Create teacher user
		createTestUser('ecole-alpha', TEACHER_EMAIL, TEACHER_PASSWORD, 'ROLE_PROF');

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

		// Create test class
		const classOutput = 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","grade-class-${TENANT_ID}")->toString();` +
				`' 2>&1`
		).trim();
		classId = classOutput;

		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-GRADE-6B', '6ème', 'active', NOW(), NOW()) ON CONFLICT DO NOTHING`
		);

		// Create test subject
		const subjectOutput = 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","grade-subject-${TENANT_ID}")->toString();` +
				`' 2>&1`
		).trim();
		const subjectId = subjectOutput;

		runSql(
			`INSERT INTO subjects (id, tenant_id, school_id, name, code, status, created_at, updated_at) ` +
				`VALUES ('${subjectId}', '${TENANT_ID}', '${schoolId}', 'E2E-GRADE-Sciences', 'E2GRDSCI', 'active', NOW(), NOW()) ON CONFLICT 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`
		);

		// Create 2 test students
		createTestUser('ecole-alpha', 'e2e-grade-student1@example.com', 'Student123', 'ROLE_ELEVE --firstName=Alice --lastName=Durand');
		createTestUser('ecole-alpha', 'e2e-grade-student2@example.com', 'Student123', 'ROLE_ELEVE --firstName=Bob --lastName=Martin');

		// Assign students to class
		const studentIds = execWithRetry(
			`docker compose -f "${composeFile}" exec -T php php bin/console dbal:run-sql "SELECT id FROM users WHERE email IN ('e2e-grade-student1@example.com','e2e-grade-student2@example.com') AND tenant_id='${TENANT_ID}' ORDER BY email" 2>&1`
		);
		const idMatches = studentIds.match(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/g);
		if (idMatches && idMatches.length >= 2) {
			student1Id = idMatches[0]!;
			student2Id = idMatches[1]!;
		}

		// Assign students 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}', '${student1Id}', '${classId}', '${academicYearId}', NOW(), NOW(), NOW()) ON CONFLICT (user_id, academic_year_id) DO NOTHING`
		);
		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}', '${student2Id}', '${classId}', '${academicYearId}', NOW(), NOW(), NOW()) ON CONFLICT (user_id, academic_year_id) DO NOTHING`
		);

		// Create test evaluation
		const evalOutput = 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","grade-eval-${TENANT_ID}")->toString();` +
				`' 2>&1`
		).trim();
		evaluationId = evalOutput;

		clearCache();
	});

	test.beforeEach(async () => {
		// Clean grades and recreate evaluation
		runSql(`DELETE FROM grade_events WHERE grade_id IN (SELECT id FROM grades WHERE evaluation_id = '${evaluationId}')`);
		runSql(`DELETE FROM grades WHERE evaluation_id = '${evaluationId}'`);
		runSql(`DELETE FROM evaluations WHERE id = '${evaluationId}'`);

		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 '${evaluationId}', '${TENANT_ID}', '${classId}', ` +
				`(SELECT id FROM subjects WHERE code='E2GRDSCI' AND tenant_id='${TENANT_ID}' LIMIT 1), ` +
				`u.id, 'E2E Contrôle Sciences', '2026-04-15', 20, 1.0, 'published', NULL, NOW(), NOW() ` +
				`FROM users u WHERE u.email='${TEACHER_EMAIL}' AND u.tenant_id='${TENANT_ID}' ` +
				`ON CONFLICT (id) DO UPDATE SET grades_published_at = NULL, updated_at = NOW()`
		);

		clearCache();
	});

	test.describe('Grade Grid Display', () => {
		test('shows grade input grid with students', async ({ page }) => {
			await loginAsTeacher(page);
			await page.goto(`${ALPHA_URL}/dashboard/teacher/evaluations/${evaluationId}/grades`);

			// Should display evaluation title
			await expect(page.getByRole('heading', { name: /E2E Contrôle Sciences/i })).toBeVisible({ timeout: 15000 });

			// Should show grade inputs
			const gradeInputs = page.locator('.grade-input');
			await expect(gradeInputs.first()).toBeVisible({ timeout: 10000 });
		});

		test('"Saisir les notes" button navigates to grades page', async ({ page }) => {
			await loginAsTeacher(page);
			await page.goto(`${ALPHA_URL}/dashboard/teacher/evaluations`);
			await expect(page.getByRole('heading', { name: /mes évaluations/i })).toBeVisible({ timeout: 15000 });

			// Wait for evaluation cards to load
			await expect(page.getByText('E2E Contrôle Sciences')).toBeVisible({ timeout: 10000 });
			await page.getByRole('link', { name: /saisir les notes/i }).first().click();

			await expect(page.getByRole('heading', { name: /E2E Contrôle Sciences/i })).toBeVisible({ timeout: 15000 });
		});
	});

	test.describe('Grade Input', () => {
		test('can enter a numeric grade', async ({ page }) => {
			await loginAsTeacher(page);
			await page.goto(`${ALPHA_URL}/dashboard/teacher/evaluations/${evaluationId}/grades`);
			await expect(page.locator('.grade-input').first()).toBeVisible({ timeout: 15000 });

			const firstInput = page.locator('.grade-input').first();
			await firstInput.fill('15.5');

			// Should show the grade in status column
			await expect(page.locator('.status-graded').first()).toBeVisible({ timeout: 5000 });
		});

		test('validates grade against scale maximum', async ({ page }) => {
			await loginAsTeacher(page);
			await page.goto(`${ALPHA_URL}/dashboard/teacher/evaluations/${evaluationId}/grades`);
			await expect(page.locator('.grade-input').first()).toBeVisible({ timeout: 15000 });

			const firstInput = page.locator('.grade-input').first();
			await firstInput.fill('25');

			// Should show error
			await expect(page.locator('.input-error-msg').first()).toBeVisible({ timeout: 5000 });
		});
	});

	test.describe('Slash Commands', () => {
		test('/abs marks student as absent', async ({ page }) => {
			await loginAsTeacher(page);
			await page.goto(`${ALPHA_URL}/dashboard/teacher/evaluations/${evaluationId}/grades`);
			await expect(page.locator('.grade-input').first()).toBeVisible({ timeout: 15000 });

			const firstInput = page.locator('.grade-input').first();
			await firstInput.clear();
			await firstInput.pressSequentially('/abs');

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

		test('/disp marks student as dispensed', async ({ page }) => {
			await loginAsTeacher(page);
			await page.goto(`${ALPHA_URL}/dashboard/teacher/evaluations/${evaluationId}/grades`);
			await expect(page.locator('.grade-input').first()).toBeVisible({ timeout: 15000 });

			const firstInput = page.locator('.grade-input').first();
			await firstInput.clear();
			await firstInput.pressSequentially('/disp');

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

	});

	test.describe('Keyboard Navigation', () => {
		test('Tab moves to next student', async ({ page }) => {
			await loginAsTeacher(page);
			await page.goto(`${ALPHA_URL}/dashboard/teacher/evaluations/${evaluationId}/grades`);
			await expect(page.locator('.grade-input').first()).toBeVisible({ timeout: 15000 });

			const firstInput = page.locator('.grade-input').first();
			await firstInput.focus();
			await firstInput.press('Tab');

			// Second input should be focused
			const secondInput = page.locator('.grade-input').nth(1);
			await expect(secondInput).toBeFocused({ timeout: 3000 });
		});

		test('Enter moves to next student', async ({ page }) => {
			await loginAsTeacher(page);
			await page.goto(`${ALPHA_URL}/dashboard/teacher/evaluations/${evaluationId}/grades`);
			await expect(page.locator('.grade-input').first()).toBeVisible({ timeout: 15000 });

			const firstInput = page.locator('.grade-input').first();
			await firstInput.fill('15');
			await firstInput.press('Enter');

			// Second input should be focused
			const secondInput = page.locator('.grade-input').nth(1);
			await expect(secondInput).toBeFocused({ timeout: 3000 });
		});

		test('Shift+Tab moves to previous student', async ({ page }) => {
			await loginAsTeacher(page);
			await page.goto(`${ALPHA_URL}/dashboard/teacher/evaluations/${evaluationId}/grades`);
			await expect(page.locator('.grade-input').first()).toBeVisible({ timeout: 15000 });

			const secondInput = page.locator('.grade-input').nth(1);
			await secondInput.focus();
			await secondInput.press('Shift+Tab');

			const firstInput = page.locator('.grade-input').first();
			await expect(firstInput).toBeFocused({ timeout: 3000 });
		});
	});

	test.describe('Publication', () => {
		test('publish requires confirmation via modal', async ({ page }) => {
			clearCache();
			await loginAsTeacher(page);
			await page.goto(`${ALPHA_URL}/dashboard/teacher/evaluations/${evaluationId}/grades`);
			await page.waitForLoadState('networkidle');
			await expect(page.locator('.grade-input').first()).toBeVisible({ timeout: 15000 });

			// Start listening for the PUT response BEFORE triggering the save
			const savePromise = page.waitForResponse(
				(resp) => resp.url().includes('/grades') && resp.request().method() === 'PUT',
				{ timeout: 30000 }
			);

			// Enter grades
			const firstInput = page.locator('.grade-input').first();
			await expect(firstInput).toBeVisible({ timeout: 5000 });
			await firstInput.fill('18');
			await expect(page.locator('.status-graded').first()).toContainText('18/20', { timeout: 10000 });

			// Wait for the PUT to complete
			await savePromise;

			// Click publish button — opens confirmation modal
			await page.getByRole('button', { name: /publier les notes/i }).click();

			// Confirmation dialog should appear
			const dialog = page.getByRole('alertdialog');
			await expect(dialog).toBeVisible({ timeout: 5000 });
			await expect(dialog.getByText(/cette action est irréversible/i)).toBeVisible();

			// Start listening for POST before clicking confirm
			const publishPromise = page.waitForResponse(
				(resp) => resp.url().includes('/publish') && resp.request().method() === 'POST',
				{ timeout: 30000 }
			);

			// Confirm publication
			await dialog.getByRole('button', { name: /confirmer la publication/i }).click();

			// Wait for the POST to complete
			await publishPromise;

			// Should show published badge
			await expect(page.getByText(/notes publiées/i)).toBeVisible({ timeout: 10000 });
		});

		test('publish modal can be cancelled', async ({ page }) => {
			clearCache();
			await loginAsTeacher(page);
			await page.goto(`${ALPHA_URL}/dashboard/teacher/evaluations/${evaluationId}/grades`);
			await page.waitForLoadState('networkidle');
			await expect(page.locator('.grade-input').first()).toBeVisible({ timeout: 15000 });

			// Start listening for PUT before triggering
			const savePromise = page.waitForResponse(
				(resp) => resp.url().includes('/grades') && resp.request().method() === 'PUT',
				{ timeout: 30000 }
			);

			// Enter a grade
			const firstInput = page.locator('.grade-input').first();
			await expect(firstInput).toBeVisible({ timeout: 5000 });
			await firstInput.fill('14');
			await expect(page.locator('.status-graded').first()).toContainText('14/20', { timeout: 10000 });

			// Wait for save to complete
			await savePromise;

			const publishBtn = page.getByRole('button', { name: /publier les notes/i });

			// Open modal then cancel
			await publishBtn.click();
			const dialog = page.getByRole('alertdialog');
			await expect(dialog).toBeVisible({ timeout: 5000 });
			await expect(dialog.getByText(/cette action est irréversible/i)).toBeVisible();
			await dialog.getByRole('button', { name: /annuler/i }).click();

			// Modal should close, publish button still visible
			await expect(dialog).not.toBeVisible({ timeout: 3000 });
			await expect(publishBtn).toBeVisible();
		});
	});
});
