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-assignments-admin@example.com';
const ADMIN_PASSWORD = 'AssignmentsTest123';
const TENANT_ID = 'a1b2c3d4-e5f6-7890-abcd-ef1234567890';

const projectRoot = join(__dirname, '../..');
const composeFile = join(projectRoot, 'compose.yaml');

function runCommand(sql: string) {
	execSync(
		`docker compose -f "${composeFile}" exec -T php php bin/console dbal:run-sql "${sql}" 2>&1`,
		{ encoding: 'utf-8' }
	);
}

function clearCache() {
	try {
		execSync(
			`docker compose -f "${composeFile}" exec -T php php bin/console cache:pool:clear paginated_queries.cache 2>&1`,
			{ encoding: 'utf-8' }
		);
	} catch {
		// Cache pool may not exist in all environments
	}
}

/**
 * Resolve deterministic UUIDs matching backend resolvers (SchoolIdResolver, CurrentAcademicYearResolver).
 * Without these, SQL-inserted test data won't be found by the API.
 */
function resolveDeterministicIds(): { schoolId: string; academicYearId: string } {
	const output = execSync(
		`docker compose -f "${composeFile}" exec -T php php -r '` +
			`require "/app/vendor/autoload.php"; ` +
			`$t="${TENANT_ID}"; $ns="6ba7b814-9dad-11d1-80b4-00c04fd430c8"; ` +
			`echo Ramsey\\Uuid\\Uuid::uuid5($ns,"school-$t")->toString()."\\n"; ` +
			`$m=(int)date("n"); $s=$m>=9?(int)date("Y"):(int)date("Y")-1; $e=$s+1; ` +
			`echo Ramsey\\Uuid\\Uuid::uuid5($ns,"$t:$s-$e")->toString();` +
			`' 2>&1`,
		{ encoding: 'utf-8' }
	).trim();
	const [schoolId, academicYearId] = output.split('\n');
	return { schoolId, academicYearId };
}

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

async function waitForPageReady(page: import('@playwright/test').Page) {
	await expect(
		page.getByRole('heading', { name: /affectations enseignants/i })
	).toBeVisible({ timeout: 15000 });
	// Wait for data loading to finish (either empty state or table appears)
	await expect(
		page.locator('.empty-state, .assignments-table, .alert-error')
	).toBeVisible({ timeout: 15000 });
}

async function openCreateDialog(page: import('@playwright/test').Page) {
	// Use .first() because both the header and empty-state have a "Nouvelle affectation" button
	const button = page.getByRole('button', { name: /nouvelle affectation/i }).first();
	await expect(button).toBeEnabled();
	await button.click();
	await expect(page.getByRole('dialog')).toBeVisible({ timeout: 10000 });

	// Wait for dropdown options to load (more than just the placeholder)
	await expect(page.locator('#assignment-class option')).not.toHaveCount(1, { timeout: 10000 });
}

async function createAssignmentViaUI(page: import('@playwright/test').Page) {
	await waitForPageReady(page);
	await openCreateDialog(page);
	await page.locator('#assignment-teacher').selectOption({ index: 1 });
	await page.locator('#assignment-class').selectOption({ index: 1 });
	await page.locator('#assignment-subject').selectOption({ index: 1 });
	await page.getByRole('button', { name: /affecter/i }).click();
	await expect(page.getByRole('dialog')).not.toBeVisible({ timeout: 10000 });
}

test.describe('Teacher Assignments (Story 2.8)', () => {
	test.beforeAll(async () => {
		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' }
		);

		execSync(
			`docker compose -f "${composeFile}" exec -T php php bin/console app:dev:create-test-user --tenant=ecole-alpha --email=e2e-teacher-assign@example.com --password=TeacherTest123 --role=ROLE_PROF 2>&1`,
			{ encoding: 'utf-8' }
		);

		// Resolve deterministic IDs that match the backend resolvers
		const { schoolId, academicYearId } = resolveDeterministicIds();

		runCommand(
			`INSERT INTO school_classes (id, tenant_id, school_id, academic_year_id, name, level, status, created_at, updated_at) VALUES (gen_random_uuid(), '${TENANT_ID}', '${schoolId}', '${academicYearId}', 'E2E-Assign-6A', '6ème', 'active', NOW(), NOW()) ON CONFLICT DO NOTHING`
		);

		runCommand(
			`INSERT INTO subjects (id, tenant_id, school_id, name, code, status, created_at, updated_at) VALUES (gen_random_uuid(), '${TENANT_ID}', '${schoolId}', 'E2E-Assign-Maths', 'E2EMATH', 'active', NOW(), NOW()) ON CONFLICT DO NOTHING`
		);

		clearCache();
	});

	test.beforeEach(async () => {
		runCommand(`DELETE FROM teacher_assignments WHERE tenant_id = '${TENANT_ID}'`);

		// Re-ensure class and subject exist (may have been deleted by parallel specs)
		const { schoolId, academicYearId } = resolveDeterministicIds();
		try {
			runCommand(
				`INSERT INTO school_classes (id, tenant_id, school_id, academic_year_id, name, level, status, created_at, updated_at) VALUES (gen_random_uuid(), '${TENANT_ID}', '${schoolId}', '${academicYearId}', 'E2E-Assign-6A', '6ème', 'active', NOW(), NOW()) ON CONFLICT DO NOTHING`
			);
			runCommand(
				`INSERT INTO subjects (id, tenant_id, school_id, name, code, status, created_at, updated_at) VALUES (gen_random_uuid(), '${TENANT_ID}', '${schoolId}', 'E2E-Assign-Maths', 'E2EMATH', 'active', NOW(), NOW()) ON CONFLICT DO NOTHING`
			);
		} catch {
			// Ignore if already exists
		}

		clearCache();
	});

	// ============================================================================
	// Navigation
	// ============================================================================
	test.describe('Navigation', () => {
		test('assignments link appears in admin navigation', async ({ page }) => {
			await loginAsAdmin(page);
			await page.goto(`${ALPHA_URL}/admin`);

			// Hover "Organisation" category to reveal dropdown
			const nav = page.locator('.desktop-nav');
			await nav.getByRole('button', { name: /organisation/i }).hover();
			const navLink = nav.getByRole('menuitem', { name: /affectations/i });
			await expect(navLink).toBeVisible({ timeout: 15000 });
		});

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

			await expect(page.getByRole('heading', { name: /affectations enseignants/i })).toBeVisible({ timeout: 15000 });
		});
	});

	// ============================================================================
	// Empty State
	// ============================================================================
	test.describe('Empty State', () => {
		test('shows empty state when no assignments exist', async ({ page }) => {
			await loginAsAdmin(page);
			await page.goto(`${ALPHA_URL}/admin/assignments`);

			await waitForPageReady(page);
			await expect(page.getByText(/aucune affectation/i)).toBeVisible({ timeout: 10000 });
		});
	});

	// ============================================================================
	// AC1: Create Assignment
	// ============================================================================
	test.describe('AC1: Create Assignment', () => {
		test('can create a new assignment', async ({ page }) => {
			await loginAsAdmin(page);
			await page.goto(`${ALPHA_URL}/admin/assignments`);
			await waitForPageReady(page);

			await openCreateDialog(page);

			const teacherSelect = page.locator('#assignment-teacher');
			await expect(teacherSelect).toBeVisible();
			const teacherOptions = teacherSelect.locator('option');
			const teacherCount = await teacherOptions.count();
			expect(teacherCount).toBeGreaterThan(1);

			await teacherSelect.selectOption({ index: 1 });

			const classSelect = page.locator('#assignment-class');
			await expect(classSelect).toBeVisible();
			await classSelect.selectOption({ index: 1 });

			const subjectSelect = page.locator('#assignment-subject');
			await expect(subjectSelect).toBeVisible();
			await subjectSelect.selectOption({ index: 1 });

			await page.getByRole('button', { name: /affecter/i }).click();

			await expect(page.getByRole('dialog')).not.toBeVisible({ timeout: 10000 });
			await expect(page.getByText(/affectation créée/i)).toBeVisible({ timeout: 10000 });

			const table = page.locator('.assignments-table');
			await expect(table).toBeVisible({ timeout: 10000 });
			const rows = table.locator('tbody tr');
			const rowCount = await rows.count();
			expect(rowCount).toBeGreaterThanOrEqual(1);
		});

		test('shows error when creating duplicate assignment', async ({ page }) => {
			await loginAsAdmin(page);
			await page.goto(`${ALPHA_URL}/admin/assignments`);

			// Create first assignment
			await createAssignmentViaUI(page);

			// Attempt to create the same assignment again
			await openCreateDialog(page);
			await page.locator('#assignment-teacher').selectOption({ index: 1 });
			await page.locator('#assignment-class').selectOption({ index: 1 });
			await page.locator('#assignment-subject').selectOption({ index: 1 });
			await page.getByRole('button', { name: /affecter/i }).click();

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

		test('cancel closes the modal without creating', async ({ page }) => {
			await loginAsAdmin(page);
			await page.goto(`${ALPHA_URL}/admin/assignments`);
			await waitForPageReady(page);

			await openCreateDialog(page);

			await page.getByRole('button', { name: /annuler/i }).click();
			await expect(page.getByRole('dialog')).not.toBeVisible({ timeout: 5000 });
		});
	});

	// ============================================================================
	// AC4: Remove Assignment
	// ============================================================================
	test.describe('AC4: Remove Assignment', () => {
		test('can remove an assignment', async ({ page }) => {
			await loginAsAdmin(page);
			await page.goto(`${ALPHA_URL}/admin/assignments`);

			await createAssignmentViaUI(page);

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

			const removeButton = page.locator('.btn-remove').first();
			await removeButton.click();

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

			await expect(page.getByText(/notes existantes seront conservées/i)).toBeVisible();

			await confirmDialog.getByRole('button', { name: /retirer/i }).click();

			await expect(confirmDialog).not.toBeVisible({ timeout: 10000 });
			await expect(page.getByText(/affectation retirée/i)).toBeVisible({ timeout: 10000 });
		});
	});

	// ============================================================================
	// AC5: Class Detail - Teachers List
	// ============================================================================
	test.describe('AC5: Class Detail Teachers', () => {
		test('class detail page shows teachers section', async ({ page }) => {
			await loginAsAdmin(page);
			await page.goto(`${ALPHA_URL}/admin/assignments`);

			await createAssignmentViaUI(page);

			await page.goto(`${ALPHA_URL}/admin/classes`);
			await expect(page.getByRole('heading', { name: /gestion des classes/i })).toBeVisible({ timeout: 15000 });

			const modifyButton = page.locator('.btn-secondary', { hasText: /modifier/i }).first();
			await modifyButton.click();
			await page.waitForURL(/\/admin\/classes\/[\w-]+/);

			await expect(page.getByRole('heading', { name: /enseignants affectés/i })).toBeVisible({ timeout: 10000 });
			await expect(page.getByRole('link', { name: /gérer les affectations/i })).toBeVisible();
		});
	});
});
