/**
 * Login Flow E2E Tests
 *
 * Tests the complete login flow including:
 * - CSRF token validation
 * - Email/password login
 * - Phone OTP login
 * - Dashboard access
 * - Logout functionality
 */

import { test, expect } from '@playwright/test';
import { prisma } from '../../lib/db';
import bcrypt from 'bcryptjs';

const BASE_URL = 'http://localhost:9000';
const TEST_PASSWORD = 'Mw!d1T3st#P@ssw0rd2024'; // Strong, unique password
const TEST_NAME = 'Login Test User';

// Use worker index to create unique test users per worker
let workerIndex = 0;
let TEST_EMAIL = '';
let TEST_PHONE = '';
let testUserId: string;
let testPhoneUserId: string;

test.describe.configure({ mode: 'serial' }); // Run tests serially to avoid conflicts

test.describe('Login Flow Tests', () => {
  // Setup: Create test users before all tests
  test.beforeAll(async ({}, testInfo) => {
    // Use worker index for unique emails
    workerIndex = testInfo.parallelIndex;
    TEST_EMAIL = `logintest${workerIndex}@example.com`;
    TEST_PHONE = `+96655123456${workerIndex}${workerIndex}`;

    // Clean up any existing test users
    await prisma.users.deleteMany({
      where: {
        OR: [
          { email: TEST_EMAIL },
          { email: `phonetest${workerIndex}@example.com` },
          { phoneNumber: TEST_PHONE },
        ],
      },
    });

    // Create test user with email/password
    const hashedPassword = await bcrypt.hash(TEST_PASSWORD, 12);
    const emailUser = await prisma.users.create({
      data: {
        email: TEST_EMAIL,
        password: hashedPassword,
        name: TEST_NAME,
        emailVerified: new Date(),
      },
    });
    testUserId = emailUser.id;

    // Create test user with phone number
    const phoneUser = await prisma.users.create({
      data: {
        email: `phonetest${workerIndex}@example.com`,
        phoneNumber: TEST_PHONE,
        name: 'Phone Test User',
        emailVerified: new Date(),
      },
    });
    testPhoneUserId = phoneUser.id;

    console.log('✅ Test users created:', { testUserId, testPhoneUserId, workerIndex });
  });

  // Cleanup: Remove test users after all tests
  test.afterAll(async () => {
    await prisma.users.deleteMany({
      where: {
        OR: [
          { id: testUserId },
          { id: testPhoneUserId },
          { email: TEST_EMAIL },
          { email: `phonetest${workerIndex}@example.com` },
        ],
      },
    });

    console.log('✅ Test users cleaned up');
  });

  test.describe('CSRF Token Validation', () => {
    test('should generate CSRF token from /api/csrf', async ({ page }) => {
      const response = await page.request.get(`${BASE_URL}/api/csrf`);
      expect(response.ok()).toBeTruthy();

      const data = await response.json();
      expect(data.csrfToken).toBeDefined();
      expect(data.csrfToken).toMatch(/^[a-f0-9]{64}$/); // 32 bytes hex = 64 chars

      console.log('✅ CSRF token generated successfully');
    });

    test('should reject login without CSRF token', async ({ page }) => {
      const response = await page.request.post(`${BASE_URL}/api/auth/login`, {
        data: {
          identifier: TEST_EMAIL,
          password: TEST_PASSWORD,
          type: 'email',
        },
      });

      expect(response.status()).toBe(403);
      const data = await response.json();
      expect(data.error).toContain('CSRF');

      console.log('✅ Login rejected without CSRF token');
    });

    test('should reject login with invalid CSRF token', async ({ page }) => {
      const response = await page.request.post(`${BASE_URL}/api/auth/login`, {
        headers: {
          'x-csrf-token': 'invalid-token-123',
        },
        data: {
          identifier: TEST_EMAIL,
          password: TEST_PASSWORD,
          type: 'email',
        },
      });

      expect(response.status()).toBe(403);
      const data = await response.json();
      expect(data.error).toContain('CSRF');

      console.log('✅ Login rejected with invalid CSRF token');
    });
  });

  test.describe('Email/Password Login', () => {
    test('should login successfully with valid credentials', async ({ page }) => {
      // Navigate to login page
      await page.goto(`${BASE_URL}/en/login`);

      // Wait for page to load
      await expect(page.locator('h2:has-text("Log in")')).toBeVisible();

      // Fill in email and password
      await page.fill('input#identifier', TEST_EMAIL);
      await page.fill('input#password', TEST_PASSWORD);

      // Submit form
      await page.click('button[type="submit"]');

      // Should redirect to dashboard
      await page.waitForURL(`${BASE_URL}/en/dashboard`, { timeout: 10000 });
      await expect(page).toHaveURL(`${BASE_URL}/en/dashboard`);

      // Verify dashboard content
      await expect(page.locator('h1').filter({ hasText: 'Welcome' })).toBeVisible();
      await expect(page.locator('h1').filter({ hasText: TEST_NAME })).toBeVisible();

      console.log('✅ Email/password login successful');
    });

    test('should show error for invalid email', async ({ page }) => {
      await page.goto(`${BASE_URL}/en/login`);

      // Fill invalid email
      await page.fill('input#identifier', 'invalid@example.com');
      await page.fill('input#password', 'WrongPassword123!');

      // Submit form
      await page.click('button[type="submit"]');

      // Should show error message
      await expect(page.locator('text=/Invalid credentials/i')).toBeVisible({ timeout: 5000 });

      console.log('✅ Invalid email shows error');
    });

    test('should show error for wrong password', async ({ page }) => {
      await page.goto(`${BASE_URL}/en/login`);

      // Fill correct email but wrong password
      await page.fill('input#identifier', TEST_EMAIL);
      await page.fill('input#password', 'WrongPassword123!');

      // Submit form
      await page.click('button[type="submit"]');

      // Should show error
      await expect(page.locator('text=/Invalid credentials/i')).toBeVisible({ timeout: 5000 });

      console.log('✅ Wrong password shows error');
    });

    test('should lock account after 5 failed login attempts', async ({ page }) => {
      // Reset failed attempts first
      await prisma.users.update({
        where: { id: testUserId },
        data: { failedAttempts: 0, lockedUntil: null },
      });

      // Attempt login 5 times with wrong password
      for (let i = 1; i <= 5; i++) {
        await page.goto(`${BASE_URL}/en/login`);
        await page.fill('input#identifier', TEST_EMAIL);
        await page.fill('input#password', `WrongPassword${i}!`);
        await page.click('button[type="submit"]');
        await page.waitForTimeout(1000);

        console.log(`Attempt ${i}/5 failed`);
      }

      // Verify account is locked in database
      const user = await prisma.users.findUnique({
        where: { id: testUserId },
      });
      expect(user?.lockedUntil).toBeDefined();
      expect(user?.failedAttempts).toBe(5);

      // Try to login with correct password (should fail - account locked)
      await page.goto(`${BASE_URL}/en/login`);
      await page.fill('input#identifier', TEST_EMAIL);
      await page.fill('input#password', TEST_PASSWORD);
      await page.click('button[type="submit"]');

      // Should show account locked message
      await expect(page.locator('text=/Account locked/i')).toBeVisible({ timeout: 5000 });

      // Cleanup: Unlock account
      await prisma.users.update({
        where: { id: testUserId },
        data: { failedAttempts: 0, lockedUntil: null },
      });

      console.log('✅ Account lockout works correctly');
    });

    test('should toggle password visibility', async ({ page }) => {
      await page.goto(`${BASE_URL}/en/login`);

      const passwordInput = page.locator('input[type="password"]');
      const toggleButton = page.locator('button:has(svg)').first();

      // Initially password should be hidden
      await expect(passwordInput).toHaveAttribute('type', 'password');

      // Click toggle button
      await toggleButton.click();

      // Password should now be visible
      const passwordInputAfterToggle = page.locator('input[type="text"]').last();
      await expect(passwordInputAfterToggle).toBeVisible();

      console.log('✅ Password visibility toggle works');
    });
  });

  test.describe('Phone OTP Login', () => {
    test('should request OTP for valid phone number', async ({ page }) => {
      await page.goto(`${BASE_URL}/en/login`);

      // Switch to phone login
      await page.click('button:has-text("Log in with OTP")');

      // Fill phone number
      await page.fill('input#identifier', TEST_PHONE);

      // Submit to request OTP
      await page.click('button[type="submit"]');

      // Should show OTP input screen
      await expect(page.locator('text=/OTP sent to/i')).toBeVisible({ timeout: 5000 });
      await expect(page.locator('input[inputMode="numeric"]')).toBeVisible();

      console.log('✅ OTP requested for phone number');
    });

    test('should verify OTP and login', async ({ page }) => {
      await page.goto(`${BASE_URL}/en/login`);

      // Switch to phone login
      await page.click('button:has-text("Log in with OTP")');

      // Fill phone number
      await page.fill('input#identifier', TEST_PHONE);

      // Request OTP
      await page.click('button[type="submit"]');

      // Wait for OTP screen
      await expect(page.locator('input[inputMode="numeric"]')).toBeVisible({ timeout: 5000 });

      // In dev mode, OTP code should be displayed
      const otpDisplay = page.locator('text=/DEV MODE/i');
      if (await otpDisplay.isVisible()) {
        // Extract OTP code from dev mode display
        const otpText = await page.locator('.font-mono.font-bold').textContent();

        if (otpText) {
          // Fill OTP code
          await page.fill('input[inputMode="numeric"]', otpText.trim());

          // Verify OTP
          await page.click('button:has-text("Verify")');

          // Should redirect to dashboard
          await page.waitForURL(`${BASE_URL}/en/dashboard`, { timeout: 10000 });
          await expect(page).toHaveURL(`${BASE_URL}/en/dashboard`);

          console.log('✅ Phone OTP login successful');
        }
      } else {
        console.warn('⚠️ OTP not displayed (expected in dev mode)');
      }
    });

    test('should show error for invalid OTP', async ({ page }) => {
      await page.goto(`${BASE_URL}/en/login`);

      // Switch to phone login
      await page.click('button:has-text("Log in with OTP")');

      // Fill phone number
      await page.fill('input#identifier', TEST_PHONE);

      // Request OTP
      await page.click('button[type="submit"]');

      // Wait for OTP screen
      await expect(page.locator('input[inputMode="numeric"]')).toBeVisible({ timeout: 5000 });

      // Fill invalid OTP
      await page.fill('input[inputMode="numeric"]', '000000');

      // Verify (should fail)
      await page.click('button:has-text("Verify")');

      // Should show error
      await expect(page.locator('text=/Invalid.*OTP/i')).toBeVisible({ timeout: 5000 });

      console.log('✅ Invalid OTP shows error');
    });
  });

  test.describe('Dashboard Access', () => {
    test('should redirect unauthenticated users to login', async ({ page }) => {
      // Try to access dashboard directly without login
      await page.goto(`${BASE_URL}/en/dashboard`);

      // Should redirect to login
      await page.waitForURL(/\/en\/login/, { timeout: 5000 });
      await expect(page).toHaveURL(/\/en\/login/);

      console.log('✅ Unauthenticated users redirected to login');
    });

    test('should display dashboard for authenticated users', async ({ page }) => {
      // Login first
      await page.goto(`${BASE_URL}/en/login`);
      await page.fill('input#identifier', TEST_EMAIL);
      await page.fill('input#password', TEST_PASSWORD);
      await page.click('button[type="submit"]');

      // Wait for dashboard
      await page.waitForURL(`${BASE_URL}/en/dashboard`, { timeout: 10000 });

      // Verify dashboard elements
      await expect(page.locator('h1').filter({ hasText: 'Welcome' })).toBeVisible();
      await expect(page.locator('h1').filter({ hasText: TEST_NAME })).toBeVisible();
      // Verify stats grid is visible (alternative to account info text)
      await expect(page.locator('text=/Upcoming Appointments|Active Clients/i')).toBeVisible();

      console.log('✅ Dashboard displays correctly for authenticated users');
    });
  });

  test.describe('Logout', () => {
    test('should logout and redirect to home', async ({ page }) => {
      // Login first
      await page.goto(`${BASE_URL}/en/login`);
      await page.fill('input#identifier', TEST_EMAIL);
      await page.fill('input#password', TEST_PASSWORD);
      await page.click('button[type="submit"]');

      // Wait for dashboard
      await page.waitForURL(`${BASE_URL}/en/dashboard`, { timeout: 10000 });

      // Click logout button
      await page.click('button:has-text("Logout")');

      // Should redirect to home
      await page.waitForURL(`${BASE_URL}/en`, { timeout: 5000 });
      await expect(page).toHaveURL(`${BASE_URL}/en`);

      // Try to access dashboard again (should redirect to login)
      await page.goto(`${BASE_URL}/en/dashboard`);
      await page.waitForURL(/\/en\/login/, { timeout: 5000 });

      console.log('✅ Logout works and redirects to home');
    });
  });

  test.describe('Security Features', () => {
    test('should mask password input by default', async ({ page }) => {
      await page.goto(`${BASE_URL}/en/login`);

      const passwordInput = page.locator('input#password');
      await expect(passwordInput).toHaveAttribute('type', 'password');

      console.log('✅ Password is masked by default');
    });

    test('should validate email format', async ({ page }) => {
      await page.goto(`${BASE_URL}/en/login`);

      // Enter invalid email
      await page.fill('input#identifier', 'not-an-email');
      await page.fill('input#password', TEST_PASSWORD);
      await page.click('button[type="submit"]');

      // Should show validation error
      await expect(page.locator('text=/Invalid.*email/i')).toBeVisible({ timeout: 3000 });

      console.log('✅ Email validation works');
    });

    test('should enforce rate limiting', async ({ page, context }) => {
      // Create new context to avoid cookie conflicts
      const newPage = await context.newPage();

      // Try to login multiple times quickly (should hit rate limit)
      for (let i = 0; i < 12; i++) {
        await newPage.goto(`${BASE_URL}/en/login`);
        await newPage.fill('input[type="text"]', `test${i}@example.com`);
        await newPage.fill('input[type="password"]', 'password123');

        const response = await newPage.request.post(`${BASE_URL}/api/auth/login`, {
          headers: {
            'Content-Type': 'application/json',
          },
          data: {
            identifier: `test${i}@example.com`,
            password: 'password123',
            type: 'email',
          },
        });

        if (response.status() === 429) {
          console.log(`✅ Rate limit enforced after ${i + 1} attempts`);
          await newPage.close();
          return;
        }
      }

      await newPage.close();
      console.warn('⚠️ Rate limiting may not be working as expected');
    });
  });

  test.describe('Audit Logging', () => {
    test('should log successful login', async ({ page }) => {
      // Clear previous audit logs for this user
      await prisma.audit_logs.deleteMany({
        where: { userId: testUserId },
      });

      // Login
      await page.goto(`${BASE_URL}/en/login`);
      await page.fill('input#identifier', TEST_EMAIL);
      await page.fill('input#password', TEST_PASSWORD);
      await page.click('button[type="submit"]');

      // Wait for redirect
      await page.waitForURL(`${BASE_URL}/en/dashboard`, { timeout: 10000 });

      // Check audit log
      const auditLog = await prisma.audit_logs.findFirst({
        where: {
          userId: testUserId,
          type: 'login_success',
        },
        orderBy: { createdAt: 'desc' },
      });

      expect(auditLog).toBeDefined();
      expect(auditLog?.success).toBe(true);
      expect(auditLog?.action).toContain('logged in');

      console.log('✅ Successful login logged in audit log');
    });

    test('should log failed login attempts', async ({ page }) => {
      // Clear previous audit logs
      await prisma.audit_logs.deleteMany({
        where: { userId: testUserId },
      });

      // Login with wrong password
      await page.goto(`${BASE_URL}/en/login`);
      await page.fill('input#identifier', TEST_EMAIL);
      await page.fill('input#password', 'WrongPassword123!');
      await page.click('button[type="submit"]');

      // Wait for error
      await page.waitForTimeout(2000);

      // Check audit log
      const auditLog = await prisma.audit_logs.findFirst({
        where: {
          userId: testUserId,
          type: 'login_failed',
        },
        orderBy: { createdAt: 'desc' },
      });

      expect(auditLog).toBeDefined();
      expect(auditLog?.success).toBe(false);
      expect(auditLog?.action).toContain('Failed login');

      // Reset failed attempts
      await prisma.users.update({
        where: { id: testUserId },
        data: { failedAttempts: 0 },
      });

      console.log('✅ Failed login logged in audit log');
    });
  });
});
