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-appr-teacher@example.com';
const TEACHER_PASSWORD = 'ApprTest123';
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()
	]);
}

/** Navigate to grades page and verify grade is loaded (pre-seeded via SQL). */
async function waitForGradeLoaded(page: import('@playwright/test').Page) {
	await page.goto(`${ALPHA_URL}/dashboard/teacher/evaluations/${evaluationId}/grades`);
	await expect(page.locator('.grade-input').first()).toBeVisible({ timeout: 15000 });
	// Grade was pre-inserted in beforeEach, should show as 15/20
	await expect(page.locator('.status-graded').first()).toContainText('15/20', { timeout: 10000 });
}

let evaluationId: string;
let classId: string;
let student1Id: string;

test.describe('Appreciations (Story 6.4)', () => {
	test.beforeAll(async () => {
		createTestUser('ecole-alpha', TEACHER_EMAIL, TEACHER_PASSWORD, 'ROLE_PROF');

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

		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","appr-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-APPR-4A', '4ème', 'active', NOW(), NOW()) ON CONFLICT DO NOTHING`
		);

		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","appr-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-APPR-Français', 'E2APRFR', 'active', NOW(), NOW()) 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}', '${subjectId}', '${academicYearId}', 'active', NOW(), NOW(), NOW() ` +
				`FROM users u WHERE u.email = '${TEACHER_EMAIL}' AND u.tenant_id = '${TENANT_ID}' ` +
				`ON CONFLICT DO NOTHING`
		);

		createTestUser('ecole-alpha', 'e2e-appr-student1@example.com', 'Student123', 'ROLE_ELEVE --firstName=Claire --lastName=Petit');

		const studentIds = execWithRetry(
			`docker compose -f "${composeFile}" exec -T php php bin/console dbal:run-sql "SELECT id FROM users WHERE email = 'e2e-appr-student1@example.com' AND tenant_id='${TENANT_ID}'" 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 >= 1) {
			student1Id = idMatches[0]!;
		}

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

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

		clearCache();
	});

	test.beforeEach(async () => {
		// Clean appreciation templates for the teacher
		runSql(`DELETE FROM appreciation_templates WHERE tenant_id = '${TENANT_ID}' AND teacher_id IN (SELECT id FROM users WHERE email = '${TEACHER_EMAIL}' AND tenant_id = '${TENANT_ID}')`);

		// 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='E2APRFR' AND tenant_id='${TENANT_ID}' LIMIT 1), ` +
				`u.id, 'E2E Contrôle Français', '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()`
		);

		// Pre-insert a grade for the student so appreciation tests don't depend on auto-save
		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}', '${evaluationId}', '${student1Id}', 15, 'graded', ` +
				`(SELECT id FROM users WHERE email = '${TEACHER_EMAIL}' AND tenant_id = '${TENANT_ID}'), NOW(), NOW() ` +
				`ON CONFLICT DO NOTHING`
		);

		clearCache();
	});

	test.describe('Appreciation Input', () => {
		test('clicking appreciation icon opens text area', async ({ page }) => {
			await loginAsTeacher(page);
			await waitForGradeLoaded(page);

			// Click appreciation button
			const apprBtn = page.locator('.btn-appreciation').first();
			await apprBtn.click();

			// Appreciation panel should open with textarea
			await expect(page.locator('.appreciation-panel')).toBeVisible({ timeout: 5000 });
			await expect(page.locator('.appreciation-textarea')).toBeVisible();
		});

		test('typing appreciation shows character counter', async ({ page }) => {
			await loginAsTeacher(page);
			await waitForGradeLoaded(page);

			// Open appreciation panel
			await page.locator('.btn-appreciation').first().click();
			await expect(page.locator('.appreciation-textarea')).toBeVisible({ timeout: 5000 });

			// Type appreciation
			await page.locator('.appreciation-textarea').fill('Bon travail');

			// Should show character count
			await expect(page.locator('.char-counter')).toContainText('11/500');
		});

		// Firefox: auto-save debounce (setTimeout) doesn't trigger reliably with Playwright fill()
		test('appreciation auto-saves after typing', async ({ page, browserName }) => {
			test.skip(browserName === 'firefox', 'Firefox auto-save timing unreliable with Playwright');
			await loginAsTeacher(page);
			await waitForGradeLoaded(page);

			// Open appreciation panel
			await page.locator('.btn-appreciation').first().click();
			await expect(page.locator('.appreciation-textarea')).toBeVisible({ timeout: 5000 });

			// Type appreciation text
			await page.locator('.appreciation-textarea').fill('Très bon travail ce trimestre');

			// Wait for auto-save by checking the UI status indicator
			await expect(page.getByText('Sauvegardé')).toBeVisible({ timeout: 15000 });
		});

		test('appreciation icon changes when appreciation exists', async ({ page }) => {
			// Pre-insert appreciation via SQL
			runSql(
				`UPDATE grades SET appreciation = 'Excellent' WHERE evaluation_id = '${evaluationId}' AND student_id = '${student1Id}' AND tenant_id = '${TENANT_ID}'`
			);
			clearCache();

			await loginAsTeacher(page);
			await waitForGradeLoaded(page);

			// Button should have "has-appreciation" class since appreciation was pre-inserted
			await expect(page.locator('.btn-appreciation.has-appreciation').first()).toBeVisible({ timeout: 5000 });
		});
	});

	test.describe('Appreciation Templates', () => {
		test('can open template manager and create a template', async ({ page }) => {
			await loginAsTeacher(page);
			await waitForGradeLoaded(page);

			// Open appreciation panel
			await page.locator('.btn-appreciation').first().click();
			await expect(page.locator('.appreciation-panel')).toBeVisible({ timeout: 5000 });

			// Click "Gérer" to open template manager
			await page.locator('.btn-template-manage').click();

			// Template manager modal should be visible
			const modal = page.getByRole('dialog');
			await expect(modal).toBeVisible({ timeout: 5000 });
			await expect(modal.getByText('Gérer les modèles')).toBeVisible();

			// Fill the new template form
			await modal.locator('.template-input').fill('Très bon travail');
			await modal.locator('.template-textarea').fill('Très bon travail, continuez ainsi !');
			await modal.getByLabel('Positive').check();

			// Listen for POST
			const createPromise = page.waitForResponse(
				(resp) => resp.url().includes('/appreciation-templates') && resp.request().method() === 'POST',
				{ timeout: 30000 }
			);

			// Create template
			await modal.getByRole('button', { name: 'Créer' }).click();
			await createPromise;

			// Template should appear in list
			await expect(modal.getByText('Très bon travail, continuez')).toBeVisible({ timeout: 5000 });
		});

		test('can apply template to appreciation', async ({ page }) => {
			await loginAsTeacher(page);
			await waitForGradeLoaded(page);

			// Open appreciation panel
			await page.locator('.btn-appreciation').first().click();
			await expect(page.locator('.appreciation-panel')).toBeVisible({ timeout: 5000 });

			// Create a template first via the manager
			await page.locator('.btn-template-manage').click();
			const modal = page.getByRole('dialog');
			await expect(modal).toBeVisible({ timeout: 5000 });

			await modal.locator('.template-input').fill('Progrès encourageants');
			await modal.locator('.template-textarea').fill('Progrès encourageants ce trimestre, poursuivez vos efforts.');

			const createPromise = page.waitForResponse(
				(resp) => resp.url().includes('/appreciation-templates') && resp.request().method() === 'POST',
				{ timeout: 30000 }
			);
			await modal.getByRole('button', { name: 'Créer' }).click();
			await createPromise;

			// Close manager
			await modal.getByRole('button', { name: 'Fermer' }).click();
			await expect(modal).not.toBeVisible({ timeout: 5000 });

			// The appreciation panel may still be open from before the modal,
			// or it may have closed. Toggle if needed.
			const panel = page.locator('.appreciation-panel');
			if (!(await panel.isVisible())) {
				await page.locator('.btn-appreciation').first().click();
			}
			await expect(panel).toBeVisible({ timeout: 10000 });

			// Click "Modèles" to show template dropdown
			await page.locator('.btn-template-select').click();
			await expect(page.locator('.template-dropdown')).toBeVisible({ timeout: 5000 });

			// Listen for appreciation auto-save
			const apprSavePromise = page.waitForResponse(
				(resp) => resp.url().includes('/appreciation') && resp.request().method() === 'PUT',
				{ timeout: 30000 }
			);

			// Click the template
			await page.locator('.template-item').first().click();

			// Textarea should contain the template content
			await expect(page.locator('.appreciation-textarea')).toHaveValue('Progrès encourageants ce trimestre, poursuivez vos efforts.', { timeout: 5000 });

			// Wait for auto-save
			await apprSavePromise;
		});

		test('can edit an existing template', async ({ page }) => {
			await loginAsTeacher(page);
			await waitForGradeLoaded(page);

			// Open appreciation panel then template manager
			await page.locator('.btn-appreciation').first().click();
			await page.locator('.btn-template-manage').click();

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

			// Create a template to edit
			await modal.locator('.template-input').fill('Avant modification');
			await modal.locator('.template-textarea').fill('Contenu avant modification');
			const createPromise = page.waitForResponse(
				(resp) => resp.url().includes('/appreciation-templates') && resp.request().method() === 'POST',
				{ timeout: 30000 }
			);
			await modal.getByRole('button', { name: 'Créer' }).click();
			await createPromise;

			// Click "Modifier" on the template
			await modal.getByRole('button', { name: 'Modifier' }).first().click();

			// Form should show "Modifier le modèle" and be pre-filled
			await expect(modal.getByText('Modifier le modèle')).toBeVisible({ timeout: 5000 });

			// Clear and fill with new values
			await modal.locator('.template-input').fill('Après modification');
			await modal.locator('.template-textarea').fill('Contenu après modification');

			// Submit the edit
			const updatePromise = page.waitForResponse(
				(resp) => resp.url().includes('/appreciation-templates/') && resp.request().method() === 'PUT',
				{ timeout: 30000 }
			);
			await modal.getByRole('button', { name: 'Modifier' }).first().click();
			await updatePromise;

			// Verify updated template is displayed
			await expect(modal.getByText('Après modification', { exact: true })).toBeVisible({ timeout: 5000 });
			await expect(modal.getByText('Avant modification', { exact: true })).not.toBeVisible({ timeout: 5000 });
		});

		test('can delete a template', async ({ page }) => {
			await loginAsTeacher(page);
			await waitForGradeLoaded(page);

			// Open appreciation panel then template manager
			await page.locator('.btn-appreciation').first().click();
			await page.locator('.btn-template-manage').click();

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

			// Create a template
			await modal.locator('.template-input').fill('À supprimer');
			await modal.locator('.template-textarea').fill('Contenu test');
			const createPromise = page.waitForResponse(
				(resp) => resp.url().includes('/appreciation-templates') && resp.request().method() === 'POST',
				{ timeout: 30000 }
			);
			await modal.getByRole('button', { name: 'Créer' }).click();
			await createPromise;

			// Template should be visible
			await expect(modal.getByText('À supprimer')).toBeVisible({ timeout: 5000 });

			// Delete it
			const deletePromise = page.waitForResponse(
				(resp) => resp.url().includes('/appreciation-templates/') && resp.request().method() === 'DELETE',
				{ timeout: 30000 }
			);
			await modal.getByRole('button', { name: 'Supprimer' }).click();
			await deletePromise;

			// Template should disappear
			await expect(modal.getByText('À supprimer')).not.toBeVisible({ timeout: 5000 });
		});
	});
});
