/**
 * Comprehensive OTP Flow E2E Tests
 *
 * Tests both signup and login OTP flows with MailHog integration
 */

import { test, expect } from '@playwright/test';

const MAILHOG_API = 'http://localhost:8025/api/v2';
const APP_URL = 'http://localhost:9000';

/**
 * Helper: Get latest email from MailHog for a recipient
 */
async function getLatestEmail(email: string): Promise<any> {
  const response = await fetch(`${MAILHOG_API}/messages`);
  const data = await response.json();

  const userEmails = data.items.filter((item: any) =>
    item.Raw.To?.includes(email) || item.To?.[0]?.Mailbox?.includes(email.split('@')[0])
  );

  return userEmails.length > 0 ? userEmails[0] : null;
}

/**
 * Helper: Extract OTP code from email content
 */
function extractOTPFromEmail(email: any): string | null {
  const html = email?.Content?.Body || email?.MIME?.Parts?.[0]?.Body || '';

  // Look for 6-digit code in common patterns
  const patterns = [
    /<div class="otp-code">(\d{6})<\/div>/,
    /verification code.*?(\d{6})/i,
    /OTP.*?(\d{6})/i,
    /code.*?(\d{6})/i,
  ];

  for (const pattern of patterns) {
    const match = html.match(pattern);
    if (match) return match[1];
  }

  return null;
}

/**
 * Helper: Clear all MailHog messages
 */
async function clearMailHog() {
  await fetch(`${MAILHOG_API}/messages`, { method: 'DELETE' });
}

test.describe('OTP Complete Flow Tests', () => {
  test.beforeEach(async () => {
    // Clear MailHog before each test
    await clearMailHog();
  });

  test('Signup flow with email OTP verification', async ({ page }) => {
    const testEmail = `test-signup-${Date.now()}@example.com`;

    // 1. Navigate to signup page
    await page.goto(`${APP_URL}/en/signup`);
    await expect(page).toHaveURL(/\/en\/signup/);

    // 2. Fill signup form
    await page.fill('input[name="email"]', testEmail);
    await page.fill('input[name="password"]', 'SecurePassword123!@#');
    await page.fill('input[name="name"]', 'Test User');

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

    // 4. Wait for redirect to OTP verification page
    await expect(page).toHaveURL(/\/verify/, { timeout: 10000 });

    // 5. Wait for email to arrive in MailHog
    await page.waitForTimeout(2000);

    // 6. Get OTP from MailHog
    const email = await getLatestEmail(testEmail);
    expect(email).toBeTruthy();

    const otpCode = extractOTPFromEmail(email);
    expect(otpCode).toBeTruthy();
    expect(otpCode).toMatch(/^\d{6}$/);

    console.log(`📧 OTP Code extracted from MailHog: ${otpCode}`);

    // 7. Enter OTP code
    if (otpCode) {
      const digits = otpCode.split('');
      for (let i = 0; i < digits.length; i++) {
        await page.fill(`input[type="text"]:nth-of-type(${i + 1})`, digits[i]);
      }

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

      // 9. Verify redirected to next step (company-info or dashboard)
      await expect(page).toHaveURL(/\/(company-info|dashboard)/, { timeout: 10000 });
    }
  });

  test('Login flow with email OTP (passwordless)', async ({ page }) => {
    const testEmail = 'existing-user@example.com';

    // Prerequisite: User must exist in database
    // In real test, you'd create the user first via API or use a known test account

    // 1. Navigate to login page
    await page.goto(`${APP_URL}/en/login`);
    await expect(page).toHaveURL(/\/en\/login/);

    // 2. Enter email
    await page.fill('input[name="identifier"]', testEmail);

    // 3. Click "Use OTP instead" link to switch to OTP mode
    const useOTPLink = page.locator('text=Use OTP instead');
    if (await useOTPLink.isVisible()) {
      await useOTPLink.click();
    }

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

    // 5. Wait for OTP step
    await expect(page.locator('text=/Enter.*code|OTP/')).toBeVisible({ timeout: 5000 });

    // 6. Wait for email in MailHog
    await page.waitForTimeout(2000);

    // 7. Get OTP from MailHog
    const email = await getLatestEmail(testEmail);
    expect(email).toBeTruthy();

    const otpCode = extractOTPFromEmail(email);
    expect(otpCode).toBeTruthy();
    expect(otpCode).toMatch(/^\d{6}$/);

    console.log(`📧 Login OTP Code extracted from MailHog: ${otpCode}`);

    // 8. Enter OTP code
    if (otpCode) {
      const otpInput = page.locator('input[inputmode="numeric"]').first();
      await otpInput.fill(otpCode);

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

      // 10. Verify redirected to dashboard
      await expect(page).toHaveURL(/\/dashboard/, { timeout: 10000 });
    }
  });

  test('OTP resend functionality', async ({ page }) => {
    const testEmail = `test-resend-${Date.now()}@example.com`;

    // 1. Start signup flow
    await page.goto(`${APP_URL}/en/signup`);
    await page.fill('input[name="email"]', testEmail);
    await page.fill('input[name="password"]', 'SecurePassword123!@#');
    await page.fill('input[name="name"]', 'Test User');
    await page.click('button[type="submit"]');

    // 2. Wait for OTP page
    await expect(page).toHaveURL(/\/verify/, { timeout: 10000 });

    // 3. Clear MailHog
    await clearMailHog();

    // 4. Wait for resend button to be enabled (60s countdown)
    // In test mode, we might want to reduce this timeout
    const resendButton = page.locator('button:has-text("Resend")');

    // Wait for countdown to finish (or click if already available)
    await page.waitForTimeout(2000);

    // 5. Click resend
    if (await resendButton.isEnabled({ timeout: 1000 }).catch(() => false)) {
      await resendButton.click();

      // 6. Wait for new email
      await page.waitForTimeout(2000);

      // 7. Verify new email received
      const email = await getLatestEmail(testEmail);
      expect(email).toBeTruthy();

      const otpCode = extractOTPFromEmail(email);
      expect(otpCode).toBeTruthy();

      console.log(`📧 Resent OTP Code: ${otpCode}`);
    }
  });

  test('Rate limiting on OTP send endpoint', async ({ page }) => {
    const testEmail = `test-ratelimit-${Date.now()}@example.com`;

    await page.goto(`${APP_URL}/en/signup`);

    // Try to send multiple OTP requests rapidly
    for (let i = 0; i < 6; i++) {
      await page.fill('input[name="email"]', testEmail);
      await page.fill('input[name="password"]', 'SecurePassword123!@#');
      await page.fill('input[name="name"]', 'Test User');
      await page.click('button[type="submit"]');

      if (i < 3) {
        // First 3 should succeed
        await expect(page).toHaveURL(/\/verify/, { timeout: 5000 });
        await page.goto(`${APP_URL}/en/signup`); // Go back
      } else {
        // 4th+ should be rate limited
        await expect(page.locator('text=/rate limit|too many/i')).toBeVisible({ timeout: 5000 });
        break;
      }

      await page.waitForTimeout(500);
    }
  });

  test('Invalid OTP code handling', async ({ page }) => {
    const testEmail = `test-invalid-${Date.now()}@example.com`;

    // 1. Start signup
    await page.goto(`${APP_URL}/en/signup`);
    await page.fill('input[name="email"]', testEmail);
    await page.fill('input[name="password"]', 'SecurePassword123!@#');
    await page.fill('input[name="name"]', 'Test User');
    await page.click('button[type="submit"]');

    // 2. Wait for OTP page
    await expect(page).toHaveURL(/\/verify/, { timeout: 10000 });

    // 3. Enter invalid OTP
    const digits = '999999'.split('');
    for (let i = 0; i < digits.length; i++) {
      await page.fill(`input[type="text"]:nth-of-type(${i + 1})`, digits[i]);
    }

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

    // 5. Expect error message
    await expect(page.locator('text=/invalid|expired/i')).toBeVisible({ timeout: 5000 });
  });

  test('Expired OTP handling', async ({ page }) => {
    // This test would require manipulating time or waiting 10+ minutes
    // Skipping for now, but implementation would be similar to invalid OTP test
    test.skip();
  });

  test('Arabic language OTP flow', async ({ page }) => {
    const testEmail = `test-arabic-${Date.now()}@example.com`;

    // 1. Navigate to Arabic signup
    await page.goto(`${APP_URL}/ar/signup`);
    await expect(page).toHaveURL(/\/ar\/signup/);

    // 2. Fill form
    await page.fill('input[name="email"]', testEmail);
    await page.fill('input[name="password"]', 'SecurePassword123!@#');
    await page.fill('input[name="name"]', 'مستخدم تجريبي');

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

    // 4. Wait for Arabic OTP page
    await expect(page).toHaveURL(/\/ar\/.*verify/, { timeout: 10000 });

    // 5. Verify Arabic UI elements
    await expect(page.locator('text=/رمز التحقق/')).toBeVisible();

    // 6. Get OTP from MailHog (should have Arabic email template)
    await page.waitForTimeout(2000);
    const email = await getLatestEmail(testEmail);
    expect(email).toBeTruthy();

    // 7. Verify Arabic email subject
    expect(email.Content.Headers.Subject).toContain('رمز التحقق');

    const otpCode = extractOTPFromEmail(email);
    expect(otpCode).toBeTruthy();

    console.log(`📧 Arabic OTP Code: ${otpCode}`);
  });

  test('Brute force protection on OTP verification', async ({ page }) => {
    const testEmail = `test-bruteforce-${Date.now()}@example.com`;

    // 1. Start signup and get to OTP page
    await page.goto(`${APP_URL}/en/signup`);
    await page.fill('input[name="email"]', testEmail);
    await page.fill('input[name="password"]', 'SecurePassword123!@#');
    await page.fill('input[name="name"]', 'Test User');
    await page.click('button[type="submit"]');
    await expect(page).toHaveURL(/\/verify/, { timeout: 10000 });

    // 2. Try multiple invalid OTP codes
    for (let attempt = 1; attempt <= 6; attempt++) {
      const invalidCode = `${attempt}${attempt}${attempt}${attempt}${attempt}${attempt}`;

      const digits = invalidCode.split('');
      for (let i = 0; i < digits.length; i++) {
        await page.fill(`input[type="text"]:nth-of-type(${i + 1})`, digits[i]);
      }

      await page.click('button[type="submit"]');
      await page.waitForTimeout(1000);

      if (attempt >= 5) {
        // Should be blocked after 5 attempts
        await expect(page.locator('text=/maximum.*attempt|too many/i')).toBeVisible({ timeout: 5000 });
        break;
      } else {
        // Should show invalid error
        await expect(page.locator('text=/invalid|incorrect/i')).toBeVisible({ timeout: 5000 });
      }

      // Clear inputs for next attempt
      for (let i = 0; i < 6; i++) {
        await page.fill(`input[type="text"]:nth-of-type(${i + 1})`, '');
      }
    }
  });

  test('Login page shows email OTP option', async ({ page }) => {
    // 1. Navigate to login page
    await page.goto(`${APP_URL}/en/login`);

    // 2. Enter email address
    await page.fill('input[type="text"]', 'test@example.com');

    // 3. Verify "Use OTP instead" link is visible
    await expect(page.locator('text=Use OTP instead')).toBeVisible();

    // 4. Click to switch to OTP mode
    await page.click('text=Use OTP instead');

    // 5. Verify password field is hidden
    await expect(page.locator('input[type="password"]')).not.toBeVisible();

    // 6. Verify OTP notice is shown
    await expect(page.locator('text=/verification code.*email/')).toBeVisible();

    // 7. Verify can switch back to password mode
    await page.click('text=Use password instead');
    await expect(page.locator('input[type="password"]')).toBeVisible();
  });

  test('Phone number login uses OTP (existing feature)', async ({ page }) => {
    // 1. Navigate to login page
    await page.goto(`${APP_URL}/en/login`);

    // 2. Enter phone number instead of email
    await page.fill('input[type="text"]', '+966501234567');

    // 3. Password field should not be required for phone
    // 4. Submit should send OTP
    await page.click('button[type="submit"]');

    // 5. Should move to OTP verification step
    await expect(page.locator('text=/Enter.*code|OTP/')).toBeVisible({ timeout: 5000 });
  });

  test('Email OTP login end-to-end flow', async ({ page, request }) => {
    // This test requires a pre-existing user account
    // Create user via API first
    const testEmail = `test-login-otp-${Date.now()}@example.com`;

    // Create user (simplified - in real test you'd use proper signup)
    const createUserResponse = await request.post(`${APP_URL}/api/auth/register`, {
      data: {
        email: testEmail,
        password: 'TestPassword123!@#',
        name: 'Test Login User',
      },
    });

    if (!createUserResponse.ok()) {
      console.log('User creation failed, might already exist - continuing test');
    }

    // Clear MailHog from signup email
    await page.waitForTimeout(1000);
    await clearMailHog();

    // 1. Navigate to login page
    await page.goto(`${APP_URL}/en/login`);

    // 2. Enter email
    await page.fill('input[type="text"]', testEmail);

    // 3. Switch to OTP mode
    const useOTPLink = page.locator('text=Use OTP instead');
    if (await useOTPLink.isVisible()) {
      await useOTPLink.click();
    }

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

    // 5. Wait for OTP step
    await expect(page.locator('text=/Enter.*code|OTP/')).toBeVisible({ timeout: 5000 });

    // 6. Get OTP from MailHog
    await page.waitForTimeout(2000);
    const email = await getLatestEmail(testEmail);
    expect(email).toBeTruthy();

    const otpCode = extractOTPFromEmail(email);
    expect(otpCode).toBeTruthy();

    console.log(`📧 Login OTP Code: ${otpCode}`);

    // 7. Enter OTP
    if (otpCode) {
      const otpInput = page.locator('input[inputmode="numeric"]').first();
      await otpInput.fill(otpCode);

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

      // 9. Verify redirected to dashboard
      await expect(page).toHaveURL(/\/dashboard/, { timeout: 10000 });

      // 10. Verify user is logged in
      await expect(page.locator('text=/dashboard|welcome/i')).toBeVisible();
    }
  });

  test('Switch between email/password and email/OTP login modes', async ({ page }) => {
    await page.goto(`${APP_URL}/en/login`);

    // 1. Verify default is email/password mode
    await expect(page.locator('input[type="password"]')).toBeVisible();

    // 2. Switch to OTP mode
    await page.click('text=Use OTP instead');
    await expect(page.locator('input[type="password"]')).not.toBeVisible();
    await expect(page.locator('text=/verification code.*email/')).toBeVisible();

    // 3. Switch back to password mode
    await page.click('text=Use password instead');
    await expect(page.locator('input[type="password"]')).toBeVisible();
    await expect(page.locator('text=/verification code.*email/')).not.toBeVisible();

    // 4. Switch between email and phone login methods
    await page.click('text=Login with phone number');
    await expect(page.locator('input[type="password"]')).not.toBeVisible();

    // 5. Switch back to email
    await page.click('text=Login with email');
    await expect(page.locator('input[type="password"]')).toBeVisible();
  });
});

test.describe('MailHog Integration Tests', () => {
  test('MailHog is accessible and receiving emails', async () => {
    // 1. Check MailHog API is accessible
    const response = await fetch(`${MAILHOG_API}/messages`);
    expect(response.ok).toBeTruthy();

    // 2. Verify MailHog web UI is accessible
    const webResponse = await fetch('http://localhost:8025');
    expect(webResponse.ok).toBeTruthy();
  });

  test('Email templates are bilingual (English and Arabic)', async ({ request }) => {
    await clearMailHog();

    // Send OTP in English
    const engEmail = `test-en-${Date.now()}@example.com`;
    await request.post(`${APP_URL}/api/send-otp`, {
      data: {
        email: engEmail,
        language: 'en',
      },
    });

    await new Promise(resolve => setTimeout(resolve, 2000));
    const enEmail = await getLatestEmail(engEmail);
    expect(enEmail.Content.Headers.Subject).toContain('Verification Code');

    await clearMailHog();

    // Send OTP in Arabic
    const arEmail = `test-ar-${Date.now()}@example.com`;
    await request.post(`${APP_URL}/api/send-otp`, {
      data: {
        email: arEmail,
        language: 'ar',
      },
    });

    await new Promise(resolve => setTimeout(resolve, 2000));
    const arEmailMsg = await getLatestEmail(arEmail);
    expect(arEmailMsg.Content.Headers.Subject).toContain('رمز التحقق');
  });
});
