import Imap from 'imap';

export interface EmailConfig {
  host: string;
  port: number;
  user: string;
  password: string;
  secure: boolean;
}

export interface TwoFactorCodeResult {
  code: string | null;
  success: boolean;
  error?: string;
  messageCount: number;
}

/**
 * Fetches 2FA code from Gmail using IMAP
 * Similar to the Katalon implementation but using Node.js/TypeScript
 */
export class Email2FAFetcher {
  private config: EmailConfig;

  constructor(config: EmailConfig) {
    this.config = config;
  }

  /**
   * Fetches the latest 2FA code from Gmail
   * @param loginTimestamp - The timestamp when login was attempted (to find recent emails)
   * @param maxWaitTime - Maximum time to wait for email (in milliseconds)
   * @param retryInterval - Time between retries (in milliseconds)
   */
  async fetch2FACode(
    loginTimestamp: Date,
    maxWaitTime: number = 30000,
    retryInterval: number = 5000
  ): Promise<TwoFactorCodeResult> {
    const startTime = Date.now();
    let lastError: string | null = null;

    return new Promise((resolve) => {
      const imap = new Imap({
        user: this.config.user,
        password: this.config.password,
        host: this.config.host,
        port: this.config.port,
        tls: this.config.secure,
        tlsOptions: { rejectUnauthorized: false }
      });

      const searchForEmails = () => {
        try {
          // Format the date for IMAP SINCE search (IMAP uses DD-MMM-YYYY format)
          // Subtract 10 seconds from loginTimestamp to account for any clock drift
          const searchDate = new Date(loginTimestamp.getTime() - 10000);
          const day = searchDate.getDate().toString().padStart(2, '0');
          const monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
          const month = monthNames[searchDate.getMonth()];
          const year = searchDate.getFullYear();
          const formattedDate = `${day}-${month}-${year}`;
          
          console.log(`🔍 Searching for 2FA emails since: ${formattedDate} (${searchDate.toISOString()})`);
          
          // Search for emails with subject "2FA Validation" that arrived SINCE the login timestamp
          imap.search([['SUBJECT', '2FA Validation'], ['SINCE', formattedDate]], (err, results) => {
            if (err) {
              lastError = err.message;
              console.error('Error searching for 2FA emails:', err.message);
              
              // Retry if we haven't exceeded max wait time
              const elapsedTime = Date.now() - startTime;
              if (elapsedTime < maxWaitTime) {
                setTimeout(searchForEmails, retryInterval);
              } else {
                imap.end();
                resolve({
                  code: null,
                  success: false,
                  error: lastError || 'Timeout: No 2FA email found within the specified time',
                  messageCount: 0
                });
              }
              return;
            }

            if (results && results.length > 0) {
              console.log(`📧 Found ${results.length} email(s) matching search criteria, fetching most recent...`);
              
              // Sort results to get most recent first (highest message ID = most recent)
              const sortedResults = results.sort((a, b) => b - a);
              
              // Try each message starting from most recent
              let messageIndex = 0;
              const tryNextMessage = () => {
                if (messageIndex >= sortedResults.length) {
                  // No more messages to try, wait and retry search
                  const elapsedTime = Date.now() - startTime;
                  if (elapsedTime < maxWaitTime) {
                    console.log(`No valid 2FA code found in ${sortedResults.length} message(s). Waiting ${retryInterval/1000} seconds before retry...`);
                    setTimeout(searchForEmails, retryInterval);
                  } else {
                    imap.end();
                    resolve({
                      code: null,
                      success: false,
                      error: 'Could not extract valid 2FA code from any message',
                      messageCount: results.length
                    });
                  }
                  return;
                }
                
                const messageId = sortedResults[messageIndex];
                console.log(`🔍 Checking message ${messageIndex + 1}/${sortedResults.length} (ID: ${messageId})`);
                
                const fetch = imap.fetch(messageId, { bodies: '' });
                fetch.on('message', (msg) => {
                  let messageContent = '';
                  
                  msg.on('body', (stream) => {
                    let buffer = '';
                    stream.on('data', (chunk) => {
                      buffer += chunk.toString();
                    });
                    
                    stream.on('end', () => {
                      messageContent = buffer;
                      const code = this.parse2FACodeFromContent(messageContent);
                      
                      if (code) {
                        console.log(`✅ 2FA Code found in message ${messageIndex + 1}: ${code}`);
                        imap.end();
                        resolve({
                          code,
                          success: true,
                          messageCount: results.length
                        });
                      } else {
                        console.log(`⚠ No code found in message ${messageIndex + 1}, trying next...`);
                        messageIndex++;
                        tryNextMessage();
                      }
                    });
                  });
                });
                
                fetch.on('error', (err) => {
                  console.error(`Error fetching message ${messageId}:`, err.message);
                  messageIndex++;
                  tryNextMessage();
                });
              };
              
              tryNextMessage();
            } else {
              // No messages found, retry if we haven't exceeded max wait time
              const elapsedTime = Date.now() - startTime;
              if (elapsedTime < maxWaitTime) {
                console.log(`2FA email not found yet. Waiting ${retryInterval/1000} seconds before retry... (Elapsed: ${elapsedTime/1000}s)`);
                setTimeout(searchForEmails, retryInterval);
              } else {
                console.log('Timeout reached. No 2FA email found within the specified time.');
                imap.end();
                resolve({
                  code: null,
                  success: false,
                  error: 'Timeout: No 2FA email found within the specified time',
                  messageCount: 0
                });
              }
            }
          });
        } catch (error) {
          lastError = error instanceof Error ? error.message : String(error);
          console.error('Error during 2FA email search:', lastError);
          
          const elapsedTime = Date.now() - startTime;
          if (elapsedTime < maxWaitTime) {
            setTimeout(searchForEmails, retryInterval);
          } else {
            imap.end();
            resolve({
              code: null,
              success: false,
              error: lastError || 'Error during email search',
              messageCount: 0
            });
          }
        }
      };

      imap.once('ready', () => {
        imap.openBox('INBOX', true, (err, box) => {
          if (err) {
            console.error('Error opening inbox:', err.message);
            imap.end();
            resolve({
              code: null,
              success: false,
              error: err.message,
              messageCount: 0
            });
            return;
          }
          
          searchForEmails();
        });
      });

      imap.once('error', (err) => {
        console.error('IMAP connection error:', err.message);
        resolve({
          code: null,
          success: false,
          error: err.message,
          messageCount: 0
        });
      });

      imap.once('end', () => {
        console.log('IMAP connection ended');
      });

      imap.connect();
    });
  }


  /**
   * Extracts text content from HTML
   */
  private extractTextFromHtml(html: string): string {
    // Simple HTML to text conversion
    return html
      .replace(/<[^>]*>/g, '') // Remove HTML tags
      .replace(/&nbsp;/g, ' ') // Replace &nbsp; with space
      .replace(/&amp;/g, '&') // Replace &amp; with &
      .replace(/&lt;/g, '<') // Replace &lt; with <
      .replace(/&gt;/g, '>') // Replace &gt; with >
      .replace(/&quot;/g, '"') // Replace &quot; with "
      .replace(/&#39;/g, "'") // Replace &#39; with '
      .replace(/\s+/g, ' ') // Replace multiple spaces with single space
      .trim();
  }

  /**
   * Parses 2FA code from message content
   */
  private parse2FACodeFromContent(content: string): string | null {
    const lines = content.split('\n');
    
    for (const line of lines) {
      // Look for patterns like "Your 2FA code is: 123456"
      if (line.includes('Your 2FA code is:')) {
        const parts = line.split(':');
        if (parts.length > 1) {
          const code = parts[1].trim();
          // Validate that it's a numeric code
          if (/^\d{6}$/.test(code)) {
            return code;
          }
        }
      }
      
      // Look for other common patterns
      if (line.includes('verification code') || line.includes('2FA code')) {
        // Extract 6-digit number from the line
        const match = line.match(/\b\d{6}\b/);
        if (match) {
          return match[0];
        }
      }
    }
    
    // Fallback: look for any 6-digit number in the content
    const match = content.match(/\b\d{6}\b/);
    return match ? match[0] : null;
  }

  /**
   * Sleep utility function
   */
  private sleep(ms: number): Promise<void> {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
}

/**
 * Default Gmail configuration
 */
export const DEFAULT_GMAIL_CONFIG: EmailConfig = {
  host: 'imap.gmail.com',
  port: 993,
  user: 'siliconkatalontest@gmail.com',
  password: '', // This should be set via environment variable or app password
  secure: true
};

/**
 * Factory function to create Email2FAFetcher with default Gmail config
 */
export function createGmail2FAFetcher(password?: string): Email2FAFetcher {
  const config = { ...DEFAULT_GMAIL_CONFIG };
  if (password) {
    config.password = password;
  } else if (process.env.GMAIL_APP_PASSWORD) {
    config.password = process.env.GMAIL_APP_PASSWORD;
  } else {
    throw new Error('Gmail password not provided. Set GMAIL_APP_PASSWORD environment variable or pass password parameter.');
  }
  
  return new Email2FAFetcher(config);
}

/**
 * Factory function to create Email2FAFetcher with custom configuration
 */
export function createEmail2FAFetcher(config: EmailConfig): Email2FAFetcher {
  return new Email2FAFetcher(config);
}
