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

/**
 * Helper to create a password reset token via CLI command
 */
function createResetToken(options: { email: string; expired?: boolean }): string | null {
	const projectRoot = join(__dirname, '../..');
	const composeFile = join(projectRoot, 'compose.yaml');

	try {
		const expiredFlag = options.expired ? ' --expired' : '';
		// Use dev environment to match the running web server
		const result = execSync(
			`docker compose -f "${composeFile}" exec -T php php bin/console app:dev:create-test-password-reset-token --email=${options.email}${expiredFlag} 2>&1`,
			{ encoding: 'utf-8' }
		);

		const tokenMatch = result.match(/Token\s+([a-f0-9-]{36})/i);
		if (tokenMatch) {
			return tokenMatch[1];
		}
		console.error('Could not extract token from output:', result);
		return null;
	} catch (error) {
		console.error('Failed to create reset token:', error);
		return null;
	}
}

test.describe('Password Reset Flow', () => {
	test.describe('Forgot Password Page (/mot-de-passe-oublie)', () => {
		test('displays the forgot password form', async ({ page }) => {
			await page.goto('/mot-de-passe-oublie');

			// Page should have the heading and form elements
			await expect(page.getByRole('heading', { name: /mot de passe oublié/i })).toBeVisible();
			await expect(page.locator('input#email')).toBeVisible();
			await expect(
				page.getByRole('button', { name: /envoyer le lien de réinitialisation/i })
			).toBeVisible();
		});

		test('shows success message after submitting email', async ({ page }) => {
			await page.goto('/mot-de-passe-oublie');

			const emailInput = page.locator('input#email');
			await emailInput.fill('test-forgot@example.com');

			await page.getByRole('button', { name: /envoyer le lien de réinitialisation/i }).click();

			// Should show success message (always, to prevent enumeration)
			await expect(page.getByText(/vérifiez votre boîte mail/i)).toBeVisible({ timeout: 10000 });
		});

		test('has link to login page', async ({ page }) => {
			await page.goto('/mot-de-passe-oublie');

			const loginLink = page.getByRole('link', { name: /retour à la connexion/i });
			await expect(loginLink).toBeVisible();
		});
	});

	test.describe('Reset Password Page (/reset-password/[token])', () => {
		test.describe('Password Form Display', () => {
			test('displays password form', async ({ page }, testInfo) => {
				// Create a fresh token for this test
				const email = `e2e-form-display-${testInfo.project.name}-${Date.now()}@example.com`;
				const token = createResetToken({ email });
				test.skip(!token, 'Could not create test token');

				await page.goto(`/reset-password/${token}`);

				// Form should be visible
				await expect(page.locator('form')).toBeVisible({ timeout: 5000 });
				await expect(page.locator('#password')).toBeVisible();
				await expect(page.locator('#confirmPassword')).toBeVisible();
			});

			test('validates password requirements in real-time', async ({ page }, testInfo) => {
				const email = `e2e-validation-${testInfo.project.name}-${Date.now()}@example.com`;
				const token = createResetToken({ email });
				test.skip(!token, 'Could not create test token');

				await page.goto(`/reset-password/${token}`);

				const form = page.locator('form');
				await expect(form).toBeVisible({ timeout: 5000 });

				const passwordInput = page.locator('#password');

				// Test short password - should not pass length requirement
				await passwordInput.fill('Abc1!');
				const minLengthItem = page
					.locator('.requirements li')
					.filter({ hasText: /8 caractères/ });
				await expect(minLengthItem).not.toHaveClass(/valid/);

				// Test missing uppercase
				await passwordInput.fill('abcdefgh1!');
				const uppercaseItem = page
					.locator('.requirements li')
					.filter({ hasText: /majuscule/ });
				await expect(uppercaseItem).not.toHaveClass(/valid/);

				// Valid password should show all checkmarks (5 requirements)
				await passwordInput.fill('Abcdefgh1!');
				const validItems = page.locator('.requirements li.valid');
				await expect(validItems).toHaveCount(5);
			});

			test('validates password confirmation matches', async ({ page }, testInfo) => {
				const email = `e2e-confirm-${testInfo.project.name}-${Date.now()}@example.com`;
				const token = createResetToken({ email });
				test.skip(!token, 'Could not create test token');

				await page.goto(`/reset-password/${token}`);

				const form = page.locator('form');
				await expect(form).toBeVisible({ timeout: 5000 });

				const passwordInput = page.locator('#password');
				const confirmInput = page.locator('#confirmPassword');

				await passwordInput.fill('SecurePass123!');
				await confirmInput.fill('DifferentPass123!');

				// Should show mismatch error
				await expect(page.getByText(/mots de passe ne correspondent pas/i)).toBeVisible();

				// Fix confirmation
				await confirmInput.fill('SecurePass123!');
				await expect(page.getByText(/mots de passe ne correspondent pas/i)).not.toBeVisible();
			});

			test('submit button is disabled until form is valid', async ({ page }, testInfo) => {
				const email = `e2e-button-${testInfo.project.name}-${Date.now()}@example.com`;
				const token = createResetToken({ email });
				test.skip(!token, 'Could not create test token');

				await page.goto(`/reset-password/${token}`);

				const form = page.locator('form');
				await expect(form).toBeVisible({ timeout: 5000 });

				const submitButton = page.getByRole('button', { name: /réinitialiser/i });

				// Initially disabled
				await expect(submitButton).toBeDisabled();

				// Fill valid password
				await page.locator('#password').fill('NewSecurePass123!');
				await page.locator('#confirmPassword').fill('NewSecurePass123!');

				// Should now be enabled
				await expect(submitButton).toBeEnabled();
			});
		});

		test.describe('Token Validation', () => {
			test('shows error for invalid token after form submission', async ({ page }) => {
				// Use an invalid token format
				await page.goto('/reset-password/00000000-0000-0000-0000-000000000000');

				// Form should still be visible (validation happens on submit)
				const form = page.locator('form');
				await expect(form).toBeVisible({ timeout: 5000 });

				// Fill valid password and submit
				await page.locator('#password').fill('ValidPassword123!');
				await page.locator('#confirmPassword').fill('ValidPassword123!');

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

				// Should show error after submission
				await expect(page.getByRole('heading', { name: 'Lien invalide' })).toBeVisible({
					timeout: 10000
				});
			});

			test('shows expiration message for expired token after form submission', async ({
				page
			}, testInfo) => {
				const email = `e2e-expired-${testInfo.project.name}-${Date.now()}@example.com`;
				const token = createResetToken({ email, expired: true });
				test.skip(!token, 'Could not create expired test token');

				await page.goto(`/reset-password/${token}`);

				const form = page.locator('form');
				await expect(form).toBeVisible({ timeout: 5000 });

				// Fill valid password and submit
				await page.locator('#password').fill('ValidPassword123!');
				await page.locator('#confirmPassword').fill('ValidPassword123!');

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

				// Should show expired/used error (shows "Lien invalide" heading with expiry message)
				await expect(page.getByRole('heading', { name: 'Lien invalide' })).toBeVisible({
					timeout: 10000
				});
			});
		});

		test.describe('Complete Reset Flow', () => {
			test('successfully resets password and redirects to login', async ({ page }, testInfo) => {
				const email = `e2e-success-${testInfo.project.name}-${Date.now()}@example.com`;
				const token = createResetToken({ email });
				test.skip(!token, 'Could not create test token');

				await page.goto(`/reset-password/${token}`);

				const form = page.locator('form');
				await expect(form).toBeVisible({ timeout: 5000 });

				// Fill form
				await page.locator('#password').fill('NewSecurePass123!');
				await page.locator('#confirmPassword').fill('NewSecurePass123!');

				const submitButton = page.getByRole('button', { name: /réinitialiser/i });
				await expect(submitButton).toBeEnabled();

				// Submit
				await submitButton.click();

				// Should show success message (heading "Mot de passe modifié")
				await expect(page.getByRole('heading', { name: 'Mot de passe modifié' })).toBeVisible({
					timeout: 10000
				});
			});

			test('token cannot be reused after successful reset', async ({ page }, testInfo) => {
				const email = `e2e-reuse-${testInfo.project.name}-${Date.now()}@example.com`;
				const token = createResetToken({ email });
				test.skip(!token, 'Could not create test token');

				// First reset
				await page.goto(`/reset-password/${token}`);
				await expect(page.locator('form')).toBeVisible({ timeout: 5000 });

				await page.locator('#password').fill('FirstPassword123!');
				await page.locator('#confirmPassword').fill('FirstPassword123!');
				await page.getByRole('button', { name: /réinitialiser/i }).click();

				// Wait for success (heading "Mot de passe modifié")
				await expect(page.getByRole('heading', { name: 'Mot de passe modifié' })).toBeVisible({
					timeout: 10000
				});

				// Try to use same token again
				await page.goto(`/reset-password/${token}`);
				await expect(page.locator('form')).toBeVisible({ timeout: 5000 });

				await page.locator('#password').fill('SecondPassword123!');
				await page.locator('#confirmPassword').fill('SecondPassword123!');
				await page.getByRole('button', { name: /réinitialiser/i }).click();

				// Should show error (already used) - page shows "Lien invalide" heading
				await expect(page.getByRole('heading', { name: 'Lien invalide' })).toBeVisible({
					timeout: 10000
				});
			});
		});
	});
});
