import { Page } from '@playwright/test';
import { testConfig } from './test-config';
import { createGmail2FAFetcher } from './email-2fa-fetcher';

export interface AuthenticationResult {
  success: boolean;
  username?: string;
  redirectUrl?: string;
  sessionValid?: boolean;
  error?: string;
}

export class ShopFlowAuthenticator {
  private static instance: ShopFlowAuthenticator;
  private isAuthenticated = false;

  private constructor() {}

  static getInstance(): ShopFlowAuthenticator {
    if (!ShopFlowAuthenticator.instance) {
      ShopFlowAuthenticator.instance = new ShopFlowAuthenticator();
    }
    return ShopFlowAuthenticator.instance;
  }

  async authenticate(page: Page): Promise<AuthenticationResult> {
    try {
      // Navigate to login page
      await page.goto(testConfig.baseUrl);
      await page.waitForTimeout(2000);

      // Check if already logged in by looking for login form
      const loginForm = page.locator('input[type="email"], input[placeholder*="email" i], input[name*="email" i]');
      const isLoginFormVisible = await loginForm.isVisible().catch(() => false);

      if (!isLoginFormVisible) {
        console.log('Already authenticated, skipping login');
        this.isAuthenticated = true;
        const currentUrl = page.url();
        return { 
          success: true, 
          sessionValid: true,
          redirectUrl: currentUrl
        };
      }

      // Wait for login form to be visible
      await page.waitForSelector('input[type="email"], input[placeholder*="email" i], input[name*="email" i]', { timeout: 10000 });

      // Find and fill login form
      const emailInput = page.locator('input[type="email"], input[placeholder*="email" i], input[name*="email" i], input[placeholder*="Enter your email" i]').first();
      const passwordInput = page.locator('input[type="password"], input[placeholder*="password" i], input[name*="password" i], input[placeholder*="Enter your password" i]').first();
      const loginButton = page.locator('button:has-text("LOGIN"), button:has-text("Login"), input[type="submit"]').first();

      await emailInput.fill(testConfig.testEmail);
      await passwordInput.fill(testConfig.testPassword);
      
      // Capture timestamp RIGHT BEFORE clicking login - this is when 2FA email will be sent
      const loginTimestamp = new Date();
      console.log(`📅 Login timestamp captured: ${loginTimestamp.toISOString()}`);
      
      await loginButton.click();

      // Wait for response and check for error messages (blocked IP, access denied, etc.)
      await page.waitForTimeout(3000);

      // Check for blocked IP / access denied error messages
      const errorAlert = page.locator('.alert-danger, .alert.alert-danger').first();
      const hasErrorAlert = await errorAlert.isVisible({ timeout: 2000 }).catch(() => false);
      
      if (hasErrorAlert) {
        const errorText = await errorAlert.textContent().catch(() => '') || '';
        // Check for blocked IP/email error messages
        if (errorText.includes('Access denied') || 
            errorText.includes('blocked') || 
            errorText.includes('Your IP and email address are currently blocked') ||
            errorText.includes('You are not allowed to use this service')) {
          console.error(`❌ Access denied - IP/Email blocked: ${errorText}`);
          return { 
            success: false, 
            error: `Access denied: ${errorText.trim()}`,
            sessionValid: false
          };
        }
        // Check for other error messages
        if (errorText.includes('Invalid') || errorText.includes('incorrect') || errorText.includes('wrong')) {
          console.error(`❌ Login failed: ${errorText}`);
          return { 
            success: false, 
            error: `Login failed: ${errorText.trim()}`,
            sessionValid: false
          };
        }
      }

      // Check if we're on 2FA page
      const currentUrl = page.url();
      if (currentUrl.includes('/2fa-validation')) {
        console.log('Reached 2FA page, attempting to get code...');
        
        // Try automated 2FA if configured
        const gmailPassword = testConfig.gmailAppPassword || testConfig.gmailImapPassword;
        if (gmailPassword && gmailPassword.trim() !== '') {
          try {
            console.log('Attempting automated 2FA code retrieval...');
            const emailFetcher = createGmail2FAFetcher(gmailPassword);
            const result = await emailFetcher.fetch2FACode(loginTimestamp, testConfig.twoFaMaxWaitTime, testConfig.twoFaRetryInterval);
            
            if (result.success && result.code) {
              console.log(`Using automated 2FA code: ${result.code}`);
              const codeInput = page.locator('input[type="number"], input[type="text"], input[placeholder*="code" i], input[placeholder*="Paste the code" i], [role="spinbutton"]').first();
              await codeInput.fill(result.code);
              await page.locator('button:has-text("VERIFY"), button:has-text("Verify")').first().click();
              
              // Wait for redirect with better logic and check for error messages
              console.log('Waiting for 2FA verification to complete...');
              await page.waitForTimeout(2000); // Wait for any error messages to appear
              
              // Check for error messages (check multiple selectors separately)
              const errorSelectors = [
                '.alert-danger:visible',
                '.error:visible',
                '[class*="error"]:visible',
                'text=Incorrect validation code',
                'text=Please try again'
              ];
              
              let hasError = false;
              let errorText = '';
              for (const selector of errorSelectors) {
                const errorElement = page.locator(selector);
                if (await errorElement.count() > 0) {
                  hasError = true;
                  errorText = (await errorElement.first().textContent().catch(() => selector)) || selector;
                  break;
                }
              }
              
              if (hasError) {
                console.log(`❌ 2FA error detected: ${errorText}`);
                return { success: false, error: `2FA validation failed: ${errorText}` };
              }
              
              // Check if we successfully left the 2FA page
              let attempts = 0;
              const maxAttempts = 10; // 10 seconds total
              let success = false;
              
              while (attempts < maxAttempts && !success) {
                await page.waitForTimeout(1000); // Wait 1 second
                const currentUrl = page.url();
                success = !currentUrl.includes('/2fa-validation');
                
                if (success) {
                  console.log('2FA verification successful!');
                  this.isAuthenticated = true;
                  const finalUrl = page.url();
                  return { 
                    success: true, 
                    username: testConfig.testEmail,
                    redirectUrl: finalUrl,
                    sessionValid: true
                  };
                }
                
                attempts++;
                console.log(`Waiting for 2FA completion... (${attempts}/${maxAttempts})`);
              }
              
              if (!success) {
                return { success: false, error: '2FA verification timeout' };
              }
            } else {
              console.log('Automated 2FA failed:', result.error);
            }
          } catch (error) {
            console.log('Automated 2FA failed:', error);
          }
        } else {
          console.log('Gmail app password not configured, skipping automated 2FA');
        }

        // Check if 2FA is already completed (due to persistence)
        console.log('Checking if 2FA is already completed...');
        await page.waitForTimeout(3000);
        
        const finalUrl = page.url();
        if (!finalUrl.includes('/2fa-validation')) {
          console.log('2FA appears to be already completed');
          this.isAuthenticated = true;
          return { 
            success: true,
            username: testConfig.testEmail,
            redirectUrl: finalUrl,
            sessionValid: true
          };
        }

        // Manual 2FA fallback
        console.log('Waiting for manual 2FA code input...');
        console.log('Please enter the 2FA code manually in the browser window');
        console.log('The test will wait for you to complete 2FA...');
        
        // Wait longer for manual input and check periodically
        let attempts = 0;
        const maxAttempts = 6; // 30 seconds total
        let success = false;
        
        while (attempts < maxAttempts && !success) {
          await page.waitForTimeout(5000); // Wait 5 seconds
          const currentUrl = page.url();
          success = !currentUrl.includes('/2fa-validation');
          
          if (success) {
            console.log('2FA completed successfully!');
            this.isAuthenticated = true;
            const completedUrl = page.url();
            return {
              success: true,
              username: testConfig.testEmail,
              redirectUrl: completedUrl,
              sessionValid: true
            };
          }
          
          attempts++;
          console.log(`Waiting for 2FA completion... (${attempts}/${maxAttempts})`);
        }
        
        if (!success) {
          return { success: false, error: 'Manual 2FA verification timeout' };
        }
        
        return { success: false, error: '2FA verification failed' };
      } else {
        console.log('No 2FA required, authentication successful');
        this.isAuthenticated = true;
        const redirectUrl = page.url();
        return { 
          success: true,
          username: testConfig.testEmail,
          redirectUrl: redirectUrl,
          sessionValid: true
        };
      }

    } catch (error) {
      console.log('Authentication failed:', error);
      return { success: false, error: error instanceof Error ? error.message : 'Unknown error' };
    }
  }

  isUserAuthenticated(): boolean {
    return this.isAuthenticated;
  }

  reset(): void {
    this.isAuthenticated = false;
  }

  /**
   * Opens the user dropdown menu where the logout link is typically located.
   * Based on header.php: the dropdown toggle is a.nav-link.dropdown-toggle containing img.avatar.rounded-circle
   * @param page The Playwright page object.
   * @returns True if the dropdown was successfully opened, false otherwise.
   */
  public async openUserDropdown(page: Page): Promise<boolean> {
    // Wait for the header/navbar to be present first
    await page.waitForSelector('nav, .navbar, header', { timeout: 5000 }).catch(() => {});
    
    // Wait for avatar to be visible (confirms user is authenticated and page is loaded)
    const avatar = page.locator('img.avatar.rounded-circle').last();
    try {
      await avatar.waitFor({ state: 'visible', timeout: 10000 });
    } catch {
      console.warn('⚠ Avatar not visible - user may not be authenticated');
      return false;
    }
    
    // Check if dropdown is already open (has .show class on menu)
    const dropdownMenu = page.locator('.dropdown-menu.show').last();
    const isAlreadyOpen = await dropdownMenu.isVisible({ timeout: 1000 }).catch(() => false);
    if (isAlreadyOpen) {
      console.log('✓ Dropdown menu already open');
      return true;
    }
    
    // Based on actual HTML: a.nav-link.dropdown-toggle.d-flex.align-items-center containing img.avatar.rounded-circle
    // Strategy 1: Direct selector matching actual HTML structure
    const dropdownToggle = page.locator('a.nav-link.dropdown-toggle:has(img.avatar.rounded-circle)').last();
    const toggleCount = await dropdownToggle.count();
    
    if (toggleCount > 0) {
      const isVisible = await dropdownToggle.isVisible({ timeout: 3000 }).catch(() => false);
      if (isVisible) {
        await dropdownToggle.click();
        // Wait for Bootstrap 5 to add .show class to dropdown menu
        await page.waitForSelector('.dropdown-menu.show', { timeout: 3000 }).catch(() => {});
        console.log('✓ Opened user dropdown menu (strategy 1)');
        return true;
      } else {
        console.warn('⚠ Dropdown toggle found but not visible');
      }
    } else {
      console.warn('⚠ Dropdown toggle not found with selector: a.nav-link.dropdown-toggle:has(img.avatar.rounded-circle)');
    }
    
    // Strategy 2: Find avatar image and traverse to parent toggle
    const avatarCount = await avatar.count();
    if (avatarCount > 0) {
      const isVisible = await avatar.isVisible({ timeout: 3000 }).catch(() => false);
      if (isVisible) {
        // Find parent anchor with dropdown-toggle - based on actual HTML structure
        const parentToggle = avatar.locator('xpath=ancestor::a[contains(@class, "dropdown-toggle")]');
        if (await parentToggle.count() > 0) {
          await parentToggle.click();
          await page.waitForSelector('.dropdown-menu.show', { timeout: 3000 }).catch(() => {});
          console.log('✓ Opened user dropdown menu (strategy 2)');
          return true;
        } else {
          console.warn('⚠ Avatar found but parent dropdown-toggle not found');
        }
      }
    }
    
    // Strategy 3: Find dropdown in nav-item.dropdown.ms-2 (actual HTML structure)
    const navDropdown = page.locator('.nav-item.dropdown.ms-2 a.dropdown-toggle:has(img.avatar)').last();
    if (await navDropdown.count() > 0) {
      const isVisible = await navDropdown.isVisible({ timeout: 3000 }).catch(() => false);
      if (isVisible) {
        await navDropdown.click();
        await page.waitForSelector('.dropdown-menu.show', { timeout: 3000 }).catch(() => {});
        console.log('✓ Opened user dropdown menu (strategy 3)');
        return true;
      }
    }
    
    console.warn('⚠ Could not open user dropdown menu - all strategies failed');
    return false;
  }

  /**
   * Logout from the application
   * The logout link is inside a dropdown menu, so we need to open the dropdown first
   */
  async logout(page: Page): Promise<boolean> {
    try {
      console.log('Logging out...');
      
      // Open the user dropdown menu using the dedicated method
      const dropdownOpened = await this.openUserDropdown(page);
      
      // After opening dropdown, wait for the menu to be visible and find logout link
      if (dropdownOpened) {
        // Wait for the dropdown menu with .show class to be visible (Bootstrap 5)
        await page.waitForSelector('.dropdown-menu.show', { state: 'visible', timeout: 3000 }).catch(() => {});
        
        // Try multiple selector strategies to find the logout link
        // Based on actual HTML: a.dropdown-item with href containing "Welcome/logout" and text "Log out"
        const logoutSelectors = [
          'a.dropdown-item[href*="Welcome/logout"]', // Exact match from actual HTML
          '.dropdown-menu.show a.dropdown-item:has-text("Log out")', // Text match with show class
          'a.dropdown-item:has-text("Log out")',
          '[href*="Welcome/logout"]'
        ];
        
        let logoutLink: ReturnType<Page['locator']> | null = null;
        for (const selector of logoutSelectors) {
          const element = page.locator(selector).first();
          const count = await element.count();
          if (count > 0) {
            const isVisible = await element.isVisible({ timeout: 2000 }).catch(() => false);
            if (isVisible) {
              logoutLink = element;
              console.log(`✓ Found logout link with selector: ${selector}`);
              break;
            }
          }
        }
        
        if (logoutLink) {
          try {
            // Wait for logout link to be visible and clickable before clicking
            await logoutLink.waitFor({ state: 'visible', timeout: 5000 });
            
            // Get current URL before logout
            const urlBeforeLogout = page.url();
            
            // Click the logout link - it should always be there if dropdown opened
            await logoutLink.click({ timeout: 10000 });
            
            // Wait for navigation to base URL (logout redirects to base_url('/'))
            // base URL can be baseUrl, baseUrl + '/', or baseUrl + '/login'
            const baseUrlPattern = new RegExp(`^${testConfig.baseUrl.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}/?$`);
            try {
              await page.waitForURL(baseUrlPattern, { timeout: 10000 });
            } catch {
              // Also try waiting for URL that contains login or is different from before
              await page.waitForURL(url => {
                const urlStr = url.toString();
                return urlStr === testConfig.baseUrl || 
                       urlStr === testConfig.baseUrl + '/' ||
                       urlStr.includes('/login') ||
                       (urlStr !== urlBeforeLogout && urlStr.startsWith(testConfig.baseUrl));
              }, { timeout: 10000 }).catch(() => {});
            }
            
            // Wait for page to fully load
            await page.waitForLoadState('networkidle', { timeout: 5000 }).catch(() => {});
            
            // Wait for the login form to appear - use specific ID from login.php: #inputEmail
            const loginForm = page.locator('#inputEmail, input[type="email"][id*="Email"], input[type="email"][name*="Email"], input[type="email"], input[placeholder*="email" i], input[name*="email" i]').first();
            const hasLoginForm = await loginForm.isVisible({ timeout: 10000 }).catch(() => false);
            
            if (hasLoginForm) {
              console.log('✓ Logout successful');
              this.isAuthenticated = false;
              return true;
            }
            
            // Double-check: if URL changed to base URL, logout likely succeeded even if form not immediately visible
            const currentUrl = page.url();
            if (currentUrl === testConfig.baseUrl || currentUrl === testConfig.baseUrl + '/') {
              // Give it one more try to find login form
              const loginFormRetry = page.locator('#inputEmail, input[type="email"]').first();
              const hasLoginFormRetry = await loginFormRetry.isVisible({ timeout: 5000 }).catch(() => false);
              if (hasLoginFormRetry) {
                console.log('✓ Logout successful');
                this.isAuthenticated = false;
                return true;
              }
            }
            
            // If login form not found, logout failed
            console.warn('⚠ Login form not found after logout attempt');
            return false;
          } catch (linkError: any) {
            // Check if we're on base URL (logout might have succeeded)
            try {
              const currentUrl = page.url();
              if (currentUrl === testConfig.baseUrl || currentUrl === testConfig.baseUrl + '/') {
                // Wait a bit and check for login form
                await page.waitForLoadState('networkidle', { timeout: 3000 }).catch(() => {});
                const loginForm = page.locator('#inputEmail, input[type="email"]').first();
                const hasLoginForm = await loginForm.isVisible({ timeout: 5000 }).catch(() => false);
                if (hasLoginForm) {
                  console.log('✓ Logout successful (login form appeared)');
                  this.isAuthenticated = false;
                  return true;
                }
              }
            } catch {
              // Ignore errors checking for login form
            }
            
            console.error('Logout click failed:', linkError);
            return false;
          }
        } else {
          console.warn('⚠ Could not find logout link in dropdown menu');
          return false;
        }
      }
      
      if (!dropdownOpened) {
        console.warn('⚠ Could not open user dropdown menu');
        return false;
      }
      
      // If we got here, dropdown was opened but logout link wasn't found or clicked successfully
      return false;
    } catch (error) {
      console.error('Logout failed:', error);
      return false;
    }
  }

  /**
   * Validate current session is active
   */
  async validateSession(page: Page): Promise<boolean> {
    try {
      const currentUrl = page.url();
      
      // Check if redirected to login
      if (currentUrl.includes('/login')) {
        this.isAuthenticated = false;
        return false;
      }
      
      // Check for login form (shouldn't be present)
      const loginForm = page.locator('input[type="email"], input[name*="email"]');
      const isLoginFormVisible = await loginForm.isVisible({ timeout: 2000 }).catch(() => false);
      
      if (isLoginFormVisible) {
        this.isAuthenticated = false;
        return false;
      }
      
      // Check for authenticated indicators
      const authIndicators = [
        page.locator('a:has-text("Logout")'),
        page.locator('a:has-text("Profile")'),
        page.locator('.user-menu')
      ];
      
      for (const indicator of authIndicators) {
        if (await indicator.isVisible({ timeout: 2000 }).catch(() => false)) {
          this.isAuthenticated = true;
          return true;
        }
      }
      
      // If no indicators found, assume not authenticated
      this.isAuthenticated = false;
      return false;
    } catch (error) {
      console.error('Session validation error:', error);
      this.isAuthenticated = false;
      return false;
    }
  }
}

// Convenience function for tests
export async function authenticateUser(page: Page): Promise<AuthenticationResult> {
  const authenticator = ShopFlowAuthenticator.getInstance();
  return await authenticator.authenticate(page);
}

// Helper function to check if user is authenticated
export function isAuthenticated(): boolean {
  return ShopFlowAuthenticator.getInstance().isUserAuthenticated();
}
