import { test, expect } from '@playwright/test';
import { testConfig } from '../../utils/test-config';
import { createGmail2FAFetcher } from '../../utils/email-2fa-fetcher';
import { printTestCase, printSuccess, printWarning } from '../../helpers/test-helpers';

/**
 * 2FA Validation Test
 * Tags: @auth @p1 @2fa
 * 
 * Tests 2FA code fetching and validation
 */

test.describe('01_Authentication - 2FA Validation @auth @p1 @2fa', () => {
  
  test('Test Case 1: 2FA page detection', async ({ page }) => {
    printTestCase(1, '2FA Page Detection');
    
    // Login to trigger 2FA
    await page.goto('/');
    await page.waitForTimeout(2000);
    
    await page.locator('input[type="email"], input[placeholder*="email" i]').first().fill(testConfig.testEmail);
    await page.locator('input[type="password"]').first().fill(testConfig.testPassword);
    await page.locator('button:has-text("LOGIN"), button:has-text("Login")').first().click();
    
    await page.waitForTimeout(3000);
    
    // Check if we're on 2FA page
    const currentUrl = page.url();
    if (currentUrl.includes('/2fa')) {
      printSuccess('2FA page detected');
      
      // Verify 2FA page elements
      // Input field: type="number", name="input2FaCode", placeholder="Paste the code sent to your email here"
      const codeInput = page.locator('input[name="input2FaCode"], input[type="number"][placeholder*="code" i], input[type="number"]');
      // Verify button: button.verify-button.btn.btn-primary with text "VERIFY"
      const verifyButton = page.locator('button:has-text("VERIFY"), button.verify-button.btn-primary');
      
      await expect(codeInput.first()).toBeVisible();
      await expect(verifyButton.first()).toBeVisible();
      
      printSuccess('2FA form elements present');
      
      // Check for remember me functionality (should be on 2FA screen)
      // Checkbox: id="rememberIp", name="rememberIp", class="form-check-input"
      // Label: for="rememberIp", class="form-check-label", text="Remember this device for 30 days"
      const rememberMeCheckbox = page.locator('#rememberIp, input[name="rememberIp"].form-check-input, input#rememberIp');
      const rememberMeLabel = page.locator('label[for="rememberIp"], label.form-check-label:has-text("Remember this device")');
      
      const hasRememberMeCheckbox = await rememberMeCheckbox.isVisible({ timeout: 3000 }).catch(() => false);
      const hasRememberMeLabel = await rememberMeLabel.isVisible({ timeout: 3000 }).catch(() => false);
      
      if (hasRememberMeCheckbox && hasRememberMeLabel) {
        printSuccess('Remember me functionality is available');
      } else {
        printWarning('Remember me functionality not found on 2FA page');
      }
    } else {
      printWarning('No 2FA required or already passed');
    }
  });
  
  test('Test Case 2: 2FA email fetching', async ({ page }) => {
    printTestCase(2, '2FA Email Fetching');
    
    const gmailPassword = testConfig.gmailAppPassword || testConfig.gmailImapPassword;
    
    if (!gmailPassword || gmailPassword.trim() === '') {
      test.skip();
      return;
    }
    
    printSuccess('Gmail credentials configured');
    
    // Login to trigger 2FA email
    await page.goto('/');
    await page.waitForTimeout(2000);
    
    await page.locator('input[type="email"], input[placeholder*="email" i]').first().fill(testConfig.testEmail);
    await page.locator('input[type="password"]').first().fill(testConfig.testPassword);
    
    const loginTimestamp = new Date();
    await page.locator('button:has-text("LOGIN"), button:has-text("Login")').first().click();
    await page.waitForTimeout(3000);
    
    // Check if we need 2FA
    const currentUrl = page.url();
    if (!currentUrl.includes('/2fa')) {
      printWarning('No 2FA required, skipping email fetch test');
      return;
    }
    
    // Attempt to fetch 2FA code
    try {
      printSuccess('Attempting to fetch 2FA code from email...');
      const emailFetcher = createGmail2FAFetcher(gmailPassword);
      const result = await emailFetcher.fetch2FACode(
        loginTimestamp,
        testConfig.twoFaMaxWaitTime,
        testConfig.twoFaRetryInterval
      );
      
      if (result.success && result.code) {
        printSuccess(`2FA code retrieved: ${result.code}`);
        expect(result.code).toMatch(/^\d{6}$/); // Should be 6 digits
      } else {
        printWarning(`2FA fetch failed: ${result.error}`);
      }
    } catch (error) {
      printWarning(`2FA fetch error: ${error}`);
    }
  });
  
  test('Test Case 3: 2FA code validation', async ({ page }) => {
    printTestCase(3, '2FA Code Validation');
    
    // Login to get to 2FA page
    await page.goto('/');
    await page.waitForTimeout(2000);
    
    await page.locator('input[type="email"], input[placeholder*="email" i]').first().fill(testConfig.testEmail);
    await page.locator('input[type="password"]').first().fill(testConfig.testPassword);
    await page.locator('button:has-text("LOGIN"), button:has-text("Login")').first().click();
    await page.waitForTimeout(3000);
    
    const currentUrl = page.url();
    if (!currentUrl.includes('/2fa')) {
      printWarning('No 2FA required, skipping validation test');
      return;
    }
    
    // Test invalid code handling
    // Input field: type="number", name="input2FaCode"
    const codeInput = page.locator('input[name="input2FaCode"], input[type="number"][placeholder*="code" i]').first();
    // Verify button: button.verify-button.btn.btn-primary with text "VERIFY"
    const verifyButton = page.locator('button:has-text("VERIFY"), button.verify-button.btn-primary').first();
    
    // Try invalid code
    await codeInput.fill('000000');
    await verifyButton.click();
    await page.waitForTimeout(2000);
    
    // Should still be on 2FA page or show error
    const urlAfterInvalid = page.url();
    if (urlAfterInvalid.includes('/2fa')) {
      printSuccess('Invalid code rejected (still on 2FA page)');
    }
    
    // Check for error message
    const errorMessage = page.locator('.error, .alert-danger, .text-danger, [class*="error"]');
    const hasError = await errorMessage.isVisible({ timeout: 3000 }).catch(() => false);
    if (hasError) {
      printSuccess('Error message displayed for invalid code');
    }
  });
  
  test('Test Case 4: 2FA timeout handling', async ({ page }) => {
    printTestCase(4, '2FA Timeout Handling');
    
    // This test verifies the timeout configuration is working
    const maxWait = testConfig.twoFaMaxWaitTime;
    const retryInterval = testConfig.twoFaRetryInterval;
    
    expect(maxWait).toBeGreaterThan(0);
    expect(retryInterval).toBeGreaterThan(0);
    expect(retryInterval).toBeLessThan(maxWait);
    
    printSuccess(`2FA timeout configured: maxWait=${maxWait}ms, retry=${retryInterval}ms`);
    
    // Calculate expected retry attempts
    const expectedRetries = Math.floor(maxWait / retryInterval);
    printSuccess(`Expected retry attempts: ${expectedRetries}`);
  });
  
  test('Test Case 5: 2FA code format validation', async ({ page }) => {
    printTestCase(5, '2FA Code Format Validation');
    
    // Test that 2FA code input only accepts valid formats
    await page.goto('/');
    await page.waitForTimeout(2000);
    
    await page.locator('input[type="email"], input[placeholder*="email" i]').first().fill(testConfig.testEmail);
    await page.locator('input[type="password"]').first().fill(testConfig.testPassword);
    await page.locator('button:has-text("LOGIN"), button:has-text("Login")').first().click();
    await page.waitForTimeout(3000);
    
    const currentUrl = page.url();
    if (!currentUrl.includes('/2fa')) {
      printWarning('No 2FA required, skipping format validation test');
      return;
    }
    
    // Input field: type="number", name="input2FaCode"
    const codeInput = page.locator('input[name="input2FaCode"], input[type="number"][placeholder*="code" i], input[type="number"]').first();
    
    // Test various formats
    await codeInput.fill('123456');
    const validCode = await codeInput.inputValue();
    expect(validCode).toBe('123456');
    printSuccess('6-digit code accepted');
    
    // Test alphanumeric input (bypass browser validation to test server-side validation)
    // type="number" inputs don't allow text, so we use evaluate to simulate bypassing client-side validation
    await codeInput.evaluate((el: HTMLInputElement) => {
      el.value = 'abc123';
      el.dispatchEvent(new Event('input', { bubbles: true }));
      el.dispatchEvent(new Event('change', { bubbles: true }));
    });
    const alphaCode = await codeInput.inputValue();
    printSuccess(`Alpha-numeric handling: ${alphaCode}`);
  });
});

