const { default: makeWASocket, DisconnectReason, useMultiFileAuthState } = require('@whiskeysockets/baileys');
const qrcode = require('qrcode-terminal');
const fs = require('fs-extra');
const path = require('path');
const { handleIncomingMessage } = require('./features/menu');

let sock;
let reconnectAttempts = 0;
let isConnecting = false;
let connectionState = 'disconnected';
let messageQueue = [];
let processingQueue = false;

const INITIAL_RECONNECT_INTERVAL = 5000; // 5 seconds
let reconnectInterval = INITIAL_RECONNECT_INTERVAL;
const MAX_RECONNECT_ATTEMPTS = 10;
const MESSAGE_RETRY_DELAY = 1000;
const CONNECTION_TIMEOUT = 30000;

// Connection stability tracking
let lastSuccessfulMessage = Date.now();
let consecutiveErrors = 0;
const MAX_CONSECUTIVE_ERRORS = 3;

async function processMessageQueue() {
  if (processingQueue || messageQueue.length === 0) return;

  processingQueue = true;

  while (messageQueue.length > 0) {
    const messageTask = messageQueue.shift();

    try {
      // Check connection before sending
      if (!isConnectionHealthy()) {
        console.log('Connection unhealthy, re-queuing message');
        messageQueue.unshift(messageTask);
        break;
      }

      await messageTask.execute();
      consecutiveErrors = 0;
      lastSuccessfulMessage = Date.now();

      // Small delay between messages to avoid overwhelming
      await new Promise(resolve => setTimeout(resolve, 500));

    } catch (error) {
      console.error('Error processing queued message:', error);
      consecutiveErrors++;

      if (consecutiveErrors >= MAX_CONSECUTIVE_ERRORS) {
        console.log('Too many consecutive errors, triggering reconnection');
        await handleConnectionError();
        break;
      }

      // Retry the message after a delay
      setTimeout(() => {
        messageQueue.unshift(messageTask);
      }, MESSAGE_RETRY_DELAY * consecutiveErrors);
    }
  }

  processingQueue = false;
}

function isConnectionHealthy() {
  const timeSinceLastMessage = Date.now() - lastSuccessfulMessage;
  return (
    sock &&
    sock.user &&
    connectionState === 'open' &&
    !isConnecting &&
    consecutiveErrors < MAX_CONSECUTIVE_ERRORS &&
    timeSinceLastMessage < CONNECTION_TIMEOUT
  );
}

async function cleanupSession() {
  try {
    const sessionDir = path.join(__dirname, 'sessions', 'auth_info_baileys');
    if (await fs.pathExists(sessionDir)) {
      await fs.remove(sessionDir);
      console.log('🧹 Cleaned up session files');
    }
  } catch (error) {
    console.error('❌ Error cleaning up session:', error);
  }
}

async function handleConnectionError() {
  console.log('🔄 Handling connection error, preparing to reconnect...');

  if (sock) {
    try {
      sock.end();
    } catch (error) {
      console.error('Error ending socket:', error);
    }
    sock = null;
  }

  connectionState = 'disconnected';
  isConnecting = false;

  // Clear any pending timeouts
  setTimeout(() => {
    if (connectionState === 'disconnected' && reconnectAttempts < MAX_RECONNECT_ATTEMPTS) {
      startBot();
    }
  }, reconnectInterval);
}

async function startBot() {
  if (isConnecting) {
    console.log('⏳ Already attempting to connect...');
    return;
  }

  isConnecting = true;
  connectionState = 'connecting';

  try {
    console.log('🚀 Starting WhatsApp bot...');

    const sessionDir = path.join(__dirname, 'sessions');
    await fs.ensureDir(sessionDir);

    const { state, saveCreds } = await useMultiFileAuthState(path.join(sessionDir, 'auth_info_baileys'));

    sock = makeWASocket({
      auth: state,
      printQRInTerminal: false,
      browser: ['Pamsika Bot', 'Safari', '10.0.0'],

      // Enhanced connection options for stability
      connectTimeoutMs: 30000,
      defaultQueryTimeoutMs: 60000,
      keepAliveIntervalMs: 30000,
      retryRequestDelayMs: 3000,

      // Message handling options
      getMessage: async (key) => {
        return { conversation: 'Message not available' };
      },

      // User agent configuration
      userAgent: {
        platform: 'WEB',
        appVersion: {
          primary: 2,
          secondary: 3000,
          tertiary: 1023223821,
        },
        mcc: '000',
        mnc: '000',
        osVersion: '0.1',
        device: 'Desktop',
        osBuildNumber: '0.1',
        releaseChannel: 'RELEASE',
        localeLanguageIso6391: 'en',
        localeCountryIso31661Alpha2: 'US',
      },

      // WebSocket options
      ws: {
        headers: {
          'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
        },
      },

      // Transaction options
      transactionOpts: {
        maxCommitRetries: 2,
        delayBetweenTriesMs: 5000,
      },
    });

    // Connection event handling
    sock.ev.on('connection.update', async (update) => {
      const { connection, lastDisconnect, qr } = update;

      console.log('📡 Connection update:', { connection, hasQR: !!qr });

      if (qr) {
        console.log('📱 QR Code generated. Please scan with WhatsApp:');
        qrcode.generate(qr, { small: true });
        connectionState = 'waiting_qr';
      }

      if (connection === 'close') {
        const statusCode = lastDisconnect?.error?.output?.statusCode;
        const errorMessage = lastDisconnect?.error?.message || 'Unknown error';

        console.log(`❌ Connection closed - Status: ${statusCode}, Error: ${errorMessage}`);

        // Determine if we should reconnect
        const shouldReconnect = (
          statusCode !== DisconnectReason.loggedOut &&
          statusCode !== 401 &&
          reconnectAttempts < MAX_RECONNECT_ATTEMPTS
        );

        connectionState = 'disconnected';
        isConnecting = false;

        if (shouldReconnect) {
          reconnectAttempts++;
          console.log(`🔄 Attempting to reconnect (attempt ${reconnectAttempts}/${MAX_RECONNECT_ATTEMPTS})...`);

          // Progressive backoff for reconnection
          const delay = Math.min(reconnectInterval * Math.pow(1.5, reconnectAttempts - 1), 60000);

          setTimeout(() => {
            if (connectionState === 'disconnected') {
              startBot();
            }
          }, delay);

        } else {
          console.log('❌ Maximum reconnection attempts reached or logged out. Manual restart required.');

          if (statusCode === DisconnectReason.loggedOut) {
            await cleanupSession();
          }

          process.exit(1);
        }

      } else if (connection === 'open') {
        console.log('✅ WhatsApp connection established!');

        connectionState = 'open';
        isConnecting = false;
        reconnectAttempts = 0;
        reconnectInterval = INITIAL_RECONNECT_INTERVAL;
        lastSuccessfulMessage = Date.now();
        consecutiveErrors = 0;

        // Process any queued messages
        if (messageQueue.length > 0) {
          console.log(`📨 Processing ${messageQueue.length} queued messages`);
          processMessageQueue();
        }

      } else if (connection === 'connecting') {
        console.log('⏳ Connecting to WhatsApp...');
        connectionState = 'connecting';

      } else if (connection === 'refused' || connection === 'timeout') {
        console.log(`⚠️ Connection ${connection}. Will retry...`);
        connectionState = 'disconnected';
        isConnecting = false;

        setTimeout(() => {
          if (connectionState === 'disconnected') {
            startBot();
          }
        }, INITIAL_RECONNECT_INTERVAL);
      }
    });

    // Credentials update handling
    sock.ev.on('creds.update', async () => {
      try {
        await saveCreds();
        console.log('💾 Credentials updated successfully');
      } catch (error) {
        console.error('❌ Error saving credentials:', error);

        // If credentials save fails repeatedly, clean up and restart
        if (consecutiveErrors > 2) {
          await cleanupSession();
          setTimeout(startBot, 5000);
        }
      }
    });

    // Message handling with enhanced error recovery
    sock.ev.on('messages.upsert', async (messageUpdate) => {
      try {
        for (const message of messageUpdate.messages) {
          if (!message.key.fromMe && message.message) {
            const phone = message.key.remoteJid;
            let messageContent = '';
            let messageType = 'text';

            // Extract message content
            if (message.message.conversation) {
              messageContent = message.message.conversation;
            } else if (message.message.extendedTextMessage) {
              messageContent = message.message.extendedTextMessage.text;
            } else if (message.message.imageMessage && message.message.imageMessage.caption) {
              messageContent = message.message.imageMessage.caption;
              messageType = 'image';
            }

            if (messageContent && messageContent.trim()) {
              console.log(`📩 New message from ${phone}: "${messageContent}"`);

              // Check connection health before processing
              if (!isConnectionHealthy()) {
                console.log('⚠️ Connection unhealthy, queueing message processing');

                messageQueue.push({
                  execute: async () => {
                    await handleIncomingMessage(sock, phone, messageContent.trim(), messageType);
                  },
                  timestamp: Date.now(),
                  phone,
                  message: messageContent.trim()
                });

                return;
              }

              // Process message immediately if connection is healthy
              try {
                await handleIncomingMessage(sock, phone, messageContent.trim(), messageType);
                lastSuccessfulMessage = Date.now();
                consecutiveErrors = 0;

              } catch (error) {
                console.error('❌ Error processing message:', error);
                consecutiveErrors++;

                // If error is connection-related, trigger reconnection
                if (
                  error.message.includes('xml-not-well-formed') ||
                  error.message.includes('Stream Errored') ||
                  error.message.includes('Connection Closed') ||
                  consecutiveErrors >= MAX_CONSECUTIVE_ERRORS
                ) {
                  console.log('🔄 Connection error detected, handling...');
                  await handleConnectionError();
                }
              }
            }
          }
        }

      } catch (error) {
        console.error('❌ Error in message handler:', error);
        consecutiveErrors++;

        if (consecutiveErrors >= MAX_CONSECUTIVE_ERRORS) {
          await handleConnectionError();
        }
      }
    });

    // Handle stream errors specifically
    sock.ev.on('stream:error', (error) => {
      console.error('❌ Stream error occurred:', error);
      handleConnectionError();
    });

    // WebSocket error handling
    if (sock.ws) {
      sock.ws.on('error', (error) => {
        console.error('❌ WebSocket error:', error);
        handleConnectionError();
      });

      sock.ws.on('close', (code, reason) => {
        console.log(`❌ WebSocket closed: ${code} - ${reason}`);
        if (connectionState === 'open') {
          handleConnectionError();
        }
      });
    }

  } catch (error) {
    console.error('❌ Error starting bot:', error);
    isConnecting = false;
    connectionState = 'disconnected';

    // Retry with exponential backoff
    const delay = Math.min(reconnectInterval * Math.pow(2, reconnectAttempts), 30000);
    setTimeout(() => {
      if (reconnectAttempts < MAX_RECONNECT_ATTEMPTS) {
        reconnectAttempts++;
        startBot();
      }
    }, delay);
  }
}

// Enhanced message sending with queue and retry
async function sendTextMessage(phone, message, retries = 3) {
  return new Promise((resolve, reject) => {
    const sendTask = {
      execute: async () => {
        if (!sock || !isConnectionHealthy()) {
          throw new Error('WhatsApp connection is not available');
        }

        const formattedPhone = phone.includes('@s.whatsapp.net')
          ? phone
          : `${phone.replace(/[^0-9]/g, '')}@s.whatsapp.net`;

        let lastError;

        for (let i = 0; i < retries; i++) {
          try {
            await sock.sendMessage(formattedPhone, { text: message });
            return;
          } catch (error) {
            lastError = error;
            console.error(`❌ Send attempt ${i + 1} failed:`, error.message);

            if (
              error.message.includes('xml-not-well-formed') ||
              error.message.includes('Stream Errored')
            ) {
              handleConnectionError();
              throw error;
            }

            if (i < retries - 1) {
              await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
            }
          }
        }

        throw lastError;
      },
      timestamp: Date.now(),
      phone,
      message
    };

    if (isConnectionHealthy()) {
      sendTask.execute().then(resolve).catch(reject);
    } else {
      messageQueue.push({
        execute: async () => {
          try {
            await sendTask.execute();
            resolve();
          } catch (error) {
            reject(error);
          }
        },
        timestamp: Date.now(),
        phone,
        message
      });
    }
  });
}

// Enhanced media message sending
async function sendMediaMessage(phone, mediaUrl, caption = '', retries = 3) {
  return new Promise((resolve, reject) => {
    const sendTask = {
      execute: async () => {
        if (!sock || !isConnectionHealthy()) {
          throw new Error('WhatsApp connection is not available');
        }

        const formattedPhone = phone.includes('@s.whatsapp.net')
          ? phone
          : `${phone.replace(/[^0-9]/g, '')}@s.whatsapp.net`;

        // Determine message type based on file extension
        const fileExtension = path.extname(mediaUrl).toLowerCase();
        let messageData = {};

        if (['.jpg', '.jpeg', '.png'].includes(fileExtension)) {
          messageData = {
            image: { url: mediaUrl },
            caption: caption,
          };
        } else if (['.mp4', '.mov'].includes(fileExtension)) {
          messageData = {
            video: { url: mediaUrl },
            caption: caption,
          };
        } else if (['.pdf', '.doc', '.docx', '.xls', '.xlsx'].includes(fileExtension)) {
          messageData = {
            document: { url: mediaUrl },
            mimetype: 'application/octet-stream',
            caption: caption,
          };
        } else {
          messageData = {
            image: { url: mediaUrl },
            caption: caption,
          };
        }

        let lastError;

        for (let i = 0; i < retries; i++) {
          try {
            await sock.sendMessage(formattedPhone, messageData);
            return;
          } catch (error) {
            lastError = error;
            console.error(`❌ Media send attempt ${i + 1} failed:`, error.message);

            if (
              error.message.includes('xml-not-well-formed') ||
              error.message.includes('Stream Errored')
            ) {
              handleConnectionError();
              throw error;
            }

            if (i < retries - 1) {
              await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
            }
          }
        }

        throw lastError;
      },
      timestamp: Date.now(),
      phone,
      mediaUrl,
      caption
    };

    if (isConnectionHealthy()) {
      sendTask.execute().then(resolve).catch(reject);
    } else {
      messageQueue.push({
        execute: async () => {
          try {
            await sendTask.execute();
            resolve();
          } catch (error) {
            reject(error);
          }
        },
        timestamp: Date.now(),
        phone,
        mediaUrl,
        caption
      });
    }
  });
}

// Health check function
function getConnectionStatus() {
  return {
    connected: isConnectionHealthy(),
    connectionState,
    isConnecting,
    reconnectAttempts,
    queuedMessages: messageQueue.length,
    consecutiveErrors,
    lastSuccessfulMessage: new Date(lastSuccessfulMessage).toISOString(),
    userId: sock?.user?.id || null
  };
}

// Graceful shutdown
process.on('SIGINT', async () => {
  console.log('📤 Received SIGINT, shutting down gracefully...');

  if (sock) {
    try {
      sock.end();
    } catch (error) {
      console.error('Error ending socket:', error);
    }
  }

  process.exit(0);
});

process.on('SIGTERM', async () => {
  console.log('📤 Received SIGTERM, shutting down gracefully...');

  if (sock) {
    try {
      sock.end();
    } catch (error) {
      console.error('Error ending socket:', error);
    }
  }

  process.exit(0);
});

// Handle uncaught exceptions
process.on('uncaughtException', (error) => {
  console.error('❌ Uncaught Exception:', error);

  // Try to reconnect if it's a connection-related error
  if (
    error.message.includes('xml-not-well-formed') ||
    error.message.includes('Stream Errored') ||
    error.message.includes('Connection')
  ) {
    handleConnectionError();
  }
});

module.exports = {
  startBot,
  sendTextMessage,
  sendMediaMessage,
  getConnectionStatus
};
