import Imap from 'imap';
import { getCredentialsFromSession, decryptCredentials } from './encryption';

interface ConnectionPoolItem {
  connection: any;
  sessionId: string;
  lastUsed: number;
  isConnected: boolean;
}

class IMAPConnectionPool {
  private connections: Map<string, ConnectionPoolItem> = new Map();
  private maxConnections = 5;
  private connectionTimeout = 300000; // 5 minutes
  private reconnectAttempts = 3;
  private reconnectDelay = 1000; // 1 second

  private cleanupInterval: NodeJS.Timeout;

  constructor() {
    // Cleanup expired connections every minute
    this.cleanupInterval = setInterval(() => {
      this.cleanupExpiredConnections();
    }, 60000);
  }

  async getConnection(sessionId: string): Promise<any> {
    // Check if we have an existing connection
    const existing = this.connections.get(sessionId);
    if (existing && existing.isConnected) {
      // Test connection health
      if (await this.testConnection(existing.connection)) {
        existing.lastUsed = Date.now();
        return existing.connection;
      } else {
        // Connection is dead, remove it
        this.removeConnection(sessionId);
      }
    }

    // Create new connection
    return await this.createConnection(sessionId);
  }

  private async createConnection(sessionId: string): Promise<any> {
    const encryptedCredentials = getCredentialsFromSession(sessionId);
    if (!encryptedCredentials) {
      throw new Error('No credentials found for session');
    }

    let credentials;
    try {
      credentials = decryptCredentials(encryptedCredentials);
    } catch (error) {
      throw new Error('Failed to decrypt credentials');
    }

    const imapConfig = {
      user: credentials.email,
      password: credentials.password,
      host: process.env.NUXT_EMAIL_IMAP_HOST || 'mail.portnimara.com',
      port: parseInt(process.env.NUXT_EMAIL_IMAP_PORT || '993'),
      tls: true,
      tlsOptions: { 
        rejectUnauthorized: false
      },
      connTimeout: 15000, // 15 seconds connection timeout
      authTimeout: 10000, // 10 seconds auth timeout
      keepalive: {
        interval: 10000, // Send keepalive every 10 seconds
        idleInterval: 300000, // IDLE for 5 minutes
        forceNoop: true
      }
    };

    return new Promise((resolve, reject) => {
      const imap = new Imap(imapConfig);
      let connectionAttempts = 0;

      const attemptConnection = () => {
        connectionAttempts++;
        
        imap.once('ready', () => {
          console.log(`[IMAPPool] Connection established for session ${sessionId}`);
          
          // Store connection
          this.connections.set(sessionId, {
            connection: imap,
            sessionId,
            lastUsed: Date.now(),
            isConnected: true
          });

          // Set up error handlers
          imap.on('error', (err: any) => {
            console.error(`[IMAPPool] Connection error for session ${sessionId}:`, err);
            this.markConnectionAsDead(sessionId);
          });

          imap.on('end', () => {
            console.log(`[IMAPPool] Connection ended for session ${sessionId}`);
            this.removeConnection(sessionId);
          });

          resolve(imap);
        });

        imap.once('error', (err: any) => {
          console.error(`[IMAPPool] Connection attempt ${connectionAttempts} failed:`, err);
          
          if (connectionAttempts < this.reconnectAttempts) {
            setTimeout(() => {
              try {
                imap.connect();
              } catch (connectError) {
                console.error(`[IMAPPool] Reconnect attempt failed:`, connectError);
                attemptConnection();
              }
            }, this.reconnectDelay * connectionAttempts);
          } else {
            reject(err);
          }
        });

        try {
          imap.connect();
        } catch (connectError) {
          console.error(`[IMAPPool] Initial connect failed:`, connectError);
          if (connectionAttempts < this.reconnectAttempts) {
            setTimeout(attemptConnection, this.reconnectDelay);
          } else {
            reject(connectError);
          }
        }
      };

      attemptConnection();
    });
  }

  private async testConnection(imap: any): Promise<boolean> {
    return new Promise((resolve) => {
      try {
        if (!imap || imap.state !== 'authenticated') {
          resolve(false);
          return;
        }

        // Test with a simple NOOP command
        imap.seq.fetch('1:1', { bodies: 'HEADER' }, (err: any) => {
          // Even if fetch fails, if no connection error then connection is likely OK
          resolve(!err || err.code !== 'ECONNRESET');
        });

        // Timeout the test after 5 seconds
        setTimeout(() => resolve(false), 5000);
      } catch (error) {
        resolve(false);
      }
    });
  }

  private markConnectionAsDead(sessionId: string): void {
    const connection = this.connections.get(sessionId);
    if (connection) {
      connection.isConnected = false;
    }
  }

  private removeConnection(sessionId: string): void {
    const connection = this.connections.get(sessionId);
    if (connection) {
      try {
        if (connection.connection && typeof connection.connection.end === 'function') {
          connection.connection.end();
        }
      } catch (error) {
        console.error(`[IMAPPool] Error closing connection:`, error);
      }
      this.connections.delete(sessionId);
    }
  }

  private cleanupExpiredConnections(): void {
    const now = Date.now();
    for (const [sessionId, connection] of this.connections.entries()) {
      if (now - connection.lastUsed > this.connectionTimeout) {
        console.log(`[IMAPPool] Cleaning up expired connection for session ${sessionId}`);
        this.removeConnection(sessionId);
      }
    }
  }

  async closeConnection(sessionId: string): Promise<void> {
    this.removeConnection(sessionId);
  }

  async closeAllConnections(): Promise<void> {
    for (const sessionId of this.connections.keys()) {
      this.removeConnection(sessionId);
    }
  }

  destroy(): void {
    if (this.cleanupInterval) {
      clearInterval(this.cleanupInterval);
    }
    this.closeAllConnections();
  }
}

// Global instance
let globalIMAPPool: IMAPConnectionPool | null = null;

export function getIMAPPool(): IMAPConnectionPool {
  if (!globalIMAPPool) {
    globalIMAPPool = new IMAPConnectionPool();
  }
  return globalIMAPPool;
}

export function destroyIMAPPool(): void {
  if (globalIMAPPool) {
    globalIMAPPool.destroy();
    globalIMAPPool = null;
  }
}
