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

// Test credentials - must match what's created by the command
const _TEST_EMAIL = 'e2e-login@example.com'; // Base email pattern used by getTestEmail()
const TEST_PASSWORD = 'TestPassword123';
const WRONG_PASSWORD = 'WrongPassword123';

// eslint-disable-next-line no-empty-pattern
test.beforeAll(async ({ }, testInfo) => {
	const browserName = testInfo.project.name;

	// Create a test user for login tests
	try {
		const projectRoot = join(__dirname, '../..');
		const composeFile = join(projectRoot, 'compose.yaml');

		// Create a unique email for this browser project to avoid conflicts
		const email = `e2e-login-${browserName}@example.com`;

		const result = execSync(
			`docker compose -f "${composeFile}" exec -T php php bin/console app:dev:create-test-user --email=${email} --password=${TEST_PASSWORD} 2>&1`,
			{ encoding: 'utf-8' }
		);

		 
		console.warn(`[${browserName}] Test user created or exists:`, result.includes('already exists') ? 'exists' : 'created');
	} catch (error) {
		console.error(`[${browserName}] Failed to create test user:`, error);
	}
});

function getTestEmail(browserName: string): string {
	return `e2e-login-${browserName}@example.com`;
}

test.describe('Login Flow', () => {
	test.describe('Successful Login', () => {
		test('logs in successfully and redirects to dashboard', async ({ page }, testInfo) => {
			const email = getTestEmail(testInfo.project.name);

			await page.goto('/login');

			// Verify we're on the login page
			await expect(page.getByRole('heading', { name: /connexion/i })).toBeVisible();

			// Fill in credentials
			await page.locator('#email').fill(email);
			await page.locator('#password').fill(TEST_PASSWORD);

			// Submit button should be enabled
			const submitButton = page.getByRole('button', { name: /se connecter/i });
			await expect(submitButton).toBeEnabled();

			// Submit and wait for navigation to dashboard
			await Promise.all([
				page.waitForURL('/dashboard', { timeout: 30000 }),
				submitButton.click()
			]);

			// We should be on the dashboard
			await expect(page).toHaveURL('/dashboard');
		});
	});

	test.describe('Failed Login', () => {
		test('shows error message for invalid credentials', async ({ page }, testInfo) => {
			const email = getTestEmail(testInfo.project.name);

			await page.goto('/login');

			// Fill in wrong credentials
			await page.locator('#email').fill(email);
			await page.locator('#password').fill(WRONG_PASSWORD);

			// Submit
			const submitButton = page.getByRole('button', { name: /se connecter/i });
			await submitButton.click();

			// Wait for error message
			const errorBanner = page.locator('.error-banner');
			await expect(errorBanner).toBeVisible({ timeout: 5000 });

			// Error should be generic (not reveal if email exists)
			await expect(errorBanner).toContainText(/email ou .* mot de passe .* incorrect/i);
		});

		test('shows error for non-existent user', async ({ page }) => {
			await page.goto('/login');

			// Use a random email that doesn't exist
			await page.locator('#email').fill(`nonexistent-${Date.now()}@example.com`);
			await page.locator('#password').fill('SomePassword123');

			// Submit
			const submitButton = page.getByRole('button', { name: /se connecter/i });
			await submitButton.click();

			// Wait for error message
			const errorBanner = page.locator('.error-banner');
			await expect(errorBanner).toBeVisible({ timeout: 5000 });

			// Error should be the same generic message (security: don't reveal if email exists)
			await expect(errorBanner).toContainText(/email ou .* mot de passe .* incorrect/i);
		});
	});

	test.describe('Form Validation', () => {
		test('submit button is disabled until both fields are filled', async ({ page }) => {
			await page.goto('/login');

			const submitButton = page.getByRole('button', { name: /se connecter/i });

			// Initially disabled (no fields filled)
			await expect(submitButton).toBeDisabled();

			// Fill only email
			await page.locator('#email').fill('test@example.com');
			await expect(submitButton).toBeDisabled();

			// Clear email, fill only password
			await page.locator('#email').fill('');
			await page.locator('#password').fill('password123');
			await expect(submitButton).toBeDisabled();

			// Fill both
			await page.locator('#email').fill('test@example.com');
			await expect(submitButton).toBeEnabled();
		});
	});

	test.describe('Rate Limiting - Fibonacci Delay', () => {
		// Skip rate limiting tests in CI - they require the real rate limiter which is
		// replaced by NullLoginRateLimiter in test environment to avoid IP blocking
		test.skip(!!process.env.CI, 'Rate limiting tests require real rate limiter (skipped in CI)');

		// Configure to run serially to avoid race conditions with rate limiter
		test.describe.configure({ mode: 'serial' });

		// Only run on chromium - rate limiter is shared across browsers running in parallel
		test.skip(({ browserName }) => browserName !== 'chromium', 'Rate limiting tests only run on chromium');

		test('shows progressive delay after failed attempts', async ({ page }, testInfo) => {
			const browserName = testInfo.project.name;
			// Use a unique email to avoid affecting other tests
			const rateLimitEmail = `rate-limit-${browserName}-${Date.now()}@example.com`;

			await page.goto('/login');

			// First attempt - no delay expected
			await page.locator('#email').fill(rateLimitEmail);
			await page.locator('#password').fill('WrongPassword');
			await page.getByRole('button', { name: /se connecter/i }).click();

			// Wait for error
			await expect(page.locator('.error-banner')).toBeVisible({ timeout: 5000 });

			// Wait for form fields to be re-enabled (delay countdown may disable them)
			await expect(page.locator('#password')).toBeEnabled({ timeout: 10000 });

			// Second attempt - should have 1 second delay
			await page.locator('#password').fill('WrongPassword2');
			await page.getByRole('button', { name: /se connecter/i }).click();

			// After second failed attempt, button should show delay countdown
			const _submitButton = page.getByRole('button', { name: /patientez|se connecter/i });

			// Wait for response - the button should briefly show "Patientez Xs..."
			await page.waitForTimeout(500);

			// Check that error message is displayed
			await expect(page.locator('.error-banner')).toBeVisible();
		});

		test('delays increase with Fibonacci sequence', async ({ page }, testInfo) => {
			const browserName = testInfo.project.name;
			const rateLimitEmail = `fibo-${browserName}-${Date.now()}@example.com`;

			await page.goto('/login');

			// Make 4 failed attempts to see increasing delays
			// Fibonacci: attempt 2 = 1s, attempt 3 = 1s, attempt 4 = 2s, attempt 5 = 3s
			for (let i = 0; i < 4; i++) {
				// Wait for form fields to be enabled before filling
				await expect(page.locator('#email')).toBeEnabled({ timeout: 15000 });

				await page.locator('#email').fill(rateLimitEmail);
				await page.locator('#password').fill(`WrongPassword${i}`);

				const submitButton = page.getByRole('button', { name: /se connecter|patientez/i });

				// Wait for button to be enabled if there's a delay
				await expect(submitButton).toBeEnabled({ timeout: 10000 });

				await submitButton.click();

				// Wait for response
				await page.waitForTimeout(300);
			}

			// After 4 attempts, should see error
			await expect(page.locator('.error-banner')).toBeVisible();
		});
	});

	test.describe('CAPTCHA after failed attempts', () => {
		// Skip CAPTCHA tests in CI - they require the real rate limiter which is
		// replaced by NullLoginRateLimiter in test environment to avoid IP blocking
		test.skip(!!process.env.CI, 'CAPTCHA tests require real rate limiter (skipped in CI)');

		// Configure to run serially to avoid race conditions with rate limiter
		test.describe.configure({ mode: 'serial' });

		// Only run on chromium - rate limiter is shared across browsers running in parallel
		test.skip(({ browserName }) => browserName !== 'chromium', 'CAPTCHA tests only run on chromium');

		test('shows CAPTCHA after 5 failed login attempts', async ({ page }, testInfo) => {
			const browserName = testInfo.project.name;
			const captchaEmail = `captcha-${browserName}-${Date.now()}@example.com`;

			await page.goto('/login');

			// Make 5 failed attempts to trigger CAPTCHA requirement
			for (let i = 0; i < 5; i++) {
				// Wait for form fields to be enabled before filling
				await expect(page.locator('#email')).toBeEnabled({ timeout: 15000 });

				await page.locator('#email').fill(captchaEmail);
				await page.locator('#password').fill(`WrongPassword${i}`);

				const submitButton = page.getByRole('button', { name: /se connecter|patientez/i });

				// Wait for button to be enabled
				await expect(submitButton).toBeEnabled({ timeout: 15000 });

				await submitButton.click();

				// Wait for response
				await page.waitForTimeout(500);
			}

			// After 5 failed attempts, CAPTCHA should appear
			const captchaSection = page.locator('.captcha-section');
			await expect(captchaSection).toBeVisible({ timeout: 10000 });

			// Should see the security verification label
			await expect(page.getByText(/vérification de sécurité/i)).toBeVisible();

			// Turnstile container should be present
			await expect(page.locator('.turnstile-container')).toBeVisible();
		});

		// TODO: Revisit this test - the button may intentionally stay enabled
		// with server-side CAPTCHA validation instead of client-side disabling
		test.skip('submit button disabled when CAPTCHA required but not completed', async ({ page }, testInfo) => {
			const browserName = testInfo.project.name;
			const captchaEmail = `captcha-btn-${browserName}-${Date.now()}@example.com`;

			await page.goto('/login');

			// Make 5 failed attempts
			for (let i = 0; i < 5; i++) {
				// Wait for form fields to be enabled before filling
				await expect(page.locator('#email')).toBeEnabled({ timeout: 15000 });

				await page.locator('#email').fill(captchaEmail);
				await page.locator('#password').fill(`WrongPassword${i}`);

				const submitButton = page.getByRole('button', { name: /se connecter|patientez/i });
				await expect(submitButton).toBeEnabled({ timeout: 15000 });
				await submitButton.click();
				await page.waitForTimeout(500);
			}

			// Wait for CAPTCHA to appear
			await expect(page.locator('.captcha-section')).toBeVisible({ timeout: 10000 });

			// Wait for any delay to expire
			await expect(page.getByRole('button', { name: /se connecter/i })).toBeVisible({ timeout: 15000 });

			// Submit button should be disabled because CAPTCHA is not completed
			const submitButton = page.getByRole('button', { name: /se connecter/i });
			await expect(submitButton).toBeDisabled();
		});
	});

	test.describe('Success Message After Activation', () => {
		test('shows success message when redirected after activation', async ({ page }) => {
			await page.goto('/login?activated=true');

			await expect(page.getByText(/compte a été activé avec succès/i)).toBeVisible();
			await expect(page.getByRole('heading', { name: /connexion/i })).toBeVisible();
		});

		test('does not show success message without query param', async ({ page }) => {
			await page.goto('/login');

			await expect(page.getByText(/compte a été activé avec succès/i)).not.toBeVisible();
			await expect(page.getByRole('heading', { name: /connexion/i })).toBeVisible();
		});
	});

	test.describe('Tenant Isolation', () => {
		// Extract port from PLAYWRIGHT_BASE_URL or use default (4173 matches playwright.config.ts)
		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 BETA_URL = `http://ecole-beta.classeo.local:${PORT}`;
		const ALPHA_EMAIL = 'tenant-test-alpha@example.com';
		const BETA_EMAIL = 'tenant-test-beta@example.com';
		const PASSWORD = 'TenantTest123';

		// Create test users on different tenants before running these tests
		test.beforeAll(async () => {
			const projectRoot = join(__dirname, '../..');
			const composeFile = join(projectRoot, 'compose.yaml');

			try {
				// Create user on ecole-alpha
				execSync(
					`docker compose -f "${composeFile}" exec -T php php bin/console app:dev:create-test-user --tenant=ecole-alpha --email=${ALPHA_EMAIL} --password=${PASSWORD} 2>&1`,
					{ encoding: 'utf-8' }
				);
				// Create user on ecole-beta
				execSync(
					`docker compose -f "${composeFile}" exec -T php php bin/console app:dev:create-test-user --tenant=ecole-beta --email=${BETA_EMAIL} --password=${PASSWORD} 2>&1`,
					{ encoding: 'utf-8' }
				);
				// eslint-disable-next-line no-console
				console.log('Tenant isolation test users created');
			} catch (error) {
				console.error('Failed to create tenant test users:', error);
			}
		});

		test('user can login on their own tenant', async ({ page }) => {
			// Alpha user on Alpha tenant - should succeed
			await page.goto(`${ALPHA_URL}/login`);

			await page.locator('#email').fill(ALPHA_EMAIL);
			await page.locator('#password').fill(PASSWORD);

			const submitButton = page.getByRole('button', { name: /se connecter/i });
			await submitButton.click();

			// Should redirect to dashboard (successful login)
			await expect(page).toHaveURL(/\/dashboard/, { timeout: 30000 });
		});

		test('user cannot login on different tenant', async ({ page }) => {
			// Alpha user on Beta tenant - should fail
			await page.goto(`${BETA_URL}/login`);

			await page.locator('#email').fill(ALPHA_EMAIL);
			await page.locator('#password').fill(PASSWORD);

			const submitButton = page.getByRole('button', { name: /se connecter/i });
			await submitButton.click();

			// Should show error (user doesn't exist in this tenant)
			const errorBanner = page.locator('.error-banner');
			await expect(errorBanner).toBeVisible({ timeout: 5000 });
			await expect(errorBanner).toContainText(/email ou .* mot de passe .* incorrect/i);

			// Should still be on login page
			await expect(page).toHaveURL(/\/login/);
		});

		test('each tenant has isolated users', async ({ page }) => {
			// Beta user on Beta tenant - should succeed
			await page.goto(`${BETA_URL}/login`);

			await page.locator('#email').fill(BETA_EMAIL);
			await page.locator('#password').fill(PASSWORD);

			const submitButton = page.getByRole('button', { name: /se connecter/i });
			await submitButton.click();

			// Should redirect to dashboard (successful login)
			await expect(page).toHaveURL(/\/dashboard/, { timeout: 30000 });
		});
	});
});
