const express = require('express');
const http = require('http');
const WebSocket = require('ws');
const path = require('path');
const fs = require('fs');
const dotenv = require('dotenv');
const url = require('url');

// ─── Environment Configuration ─────────────────────────────────────────────
const envPaths = [
  path.join(__dirname, '.env'),                          // whatsapp_bridge/.env
  path.join(__dirname, '..', '.env'),                     // mywhatsapp.intaleqapp.com/.env
  '/home/intaleqapp-mywhatsapp/.env'                      // Server user root-level .env
];

let envLoaded = false;
for (const envPath of envPaths) {
  if (fs.existsSync(envPath)) {
    dotenv.config({ path: envPath });
    console.log(`[ENV] Loaded configuration from: ${envPath}`);
    envLoaded = true;
    break;
  }
}
if (!envLoaded) {
  dotenv.config();
  console.log('[ENV] Loaded default environment configuration');
}

// ─── Dependencies ──────────────────────────────────────────────────────────
const { 
  initDatabase, 
  updateSlotStatus, 
  archiveMessage, 
  getChatHistory, 
  searchMessages 
} = require('./database');

const admin = require('firebase-admin');
const PORT = process.env.PORT || 3025;

// ─── Express & HTTP Setup ──────────────────────────────────────────────────
const app = express();
const server = http.createServer(app);
const wss = new WebSocket.Server({ server });

// ─── Firebase Admin SDK Configuration (Background Pushes) ──────────────────
let firebaseApp = null;
const envServiceAccount = process.env.FIREBASE_SERVICE_ACCOUNT;
const envServiceAccountPath = process.env.FIREBASE_SERVICE_ACCOUNT_PATH;
const localServiceAccountPath = path.join(__dirname, 'serviceAccountKey.json');

try {
  if (envServiceAccount) {
    let serviceAccount;
    if (envServiceAccount.trim().startsWith('{')) {
      serviceAccount = JSON.parse(envServiceAccount);
      console.log('[FCM] Initializing Firebase Admin SDK via env JSON string...');
    } else {
      serviceAccount = require(envServiceAccount);
      console.log(`[FCM] Initializing Firebase Admin SDK via env path: ${envServiceAccount}`);
    }
    firebaseApp = admin.initializeApp({
      credential: admin.credential.cert(serviceAccount)
    });
  } else if (envServiceAccountPath && fs.existsSync(envServiceAccountPath)) {
    console.log(`[FCM] Initializing Firebase Admin SDK via custom path: ${envServiceAccountPath}`);
    const serviceAccount = require(envServiceAccountPath);
    firebaseApp = admin.initializeApp({
      credential: admin.credential.cert(serviceAccount)
    });
  } else if (fs.existsSync(localServiceAccountPath)) {
    console.log('[FCM] Initializing Firebase Admin SDK via serviceAccountKey.json...');
    const serviceAccount = require(localServiceAccountPath);
    firebaseApp = admin.initializeApp({
      credential: admin.credential.cert(serviceAccount)
    });
  } else {
    console.warn('[FCM WARNING] No Firebase Service Account found. Background pushes will be disabled.');
  }
} catch (err) {
  console.error('[FCM ERROR] Failed to initialize Firebase Admin SDK:', err.message);
}

// ─── Send Background Push Notification ────────────────────────────────────
async function sendPushNotification(chatId, senderName, body) {
  if (!firebaseApp) return;

  const tokenPath = path.join(__dirname, 'fcm_token.json');
  if (!fs.existsSync(tokenPath)) return;

  try {
    const tokenData = JSON.parse(fs.readFileSync(tokenPath));
    const token = tokenData.token;
    if (!token) return;

    const message = {
      token: token,
      notification: {
        title: senderName || 'WhatsApp Message',
        body: body || 'New Message'
      },
      data: {
        chatId: chatId,
        name: senderName || 'WhatsApp'
      },
      apns: {
        payload: {
          aps: {
            sound: 'default',
            badge: 1
          }
        }
      }
    };

    await admin.messaging().send(message);
    console.log('[FCM] Push notification sent successfully');
  } catch (err) {
    console.error('[FCM SEND ERROR] Failed to send push:', err.message);
  }
}

// ─── 6-Slot Multi-Tenant Registry State ────────────────────────────────────
const slots = new Map(); // slotId (1-6) -> { waClient, clientReady, qrCodeCache }
const connectedClients = new Set(); // Set of active WS connections

// Uncaught Exceptions Catching
process.on('uncaughtException', (err) => console.error('[CRITICAL] Uncaught Exception:', err));
process.on('unhandledRejection', (reason, p) => console.error('[CRITICAL] Unhandled Rejection at:', p, 'reason:', reason));

// ─── WebSocket Routing & Messaging ─────────────────────────────────────────
function broadcast(slotId, payload) {
  const data = JSON.stringify(payload);
  connectedClients.forEach((ws) => {
    if (ws.readyState === WebSocket.OPEN && ws.slotId === slotId) {
      try {
        ws.send(data);
      } catch (err) {
        console.error(`[WS] Broadcast error on slot ${slotId}:`, err.message);
      }
    }
  });
}

function sendTo(ws, payload) {
  if (ws.readyState === WebSocket.OPEN) {
    try {
      ws.send(JSON.stringify(payload));
    } catch (err) {
      console.error('[WS] Send error:', err.message);
    }
  }
}

// ─── Formatting helpers ────────────────────────────────────────────────────
async function formatChat(chat) {
  let avatar = null;
  try {
    avatar = await chat.client.pupPage.evaluate((chatId) => {
      try {
        const contact = window.Store.Contact.get(chatId);
        return contact && contact.profilePicThumb ? (contact.profilePicThumb.imgFull || contact.profilePicThumb.img) : null;
      } catch (_) { return null; }
    }, chat.id._serialized);

    if (!avatar) {
      avatar = await Promise.race([
        chat.getProfilePicUrl().catch(() => null),
        new Promise(resolve => setTimeout(() => resolve(null), 800))
      ]);
    }
  } catch (_) {}

  let lastMessageFormatted = null;
  if (chat.lastMessage) {
    lastMessageFormatted = {
      body: chat.lastMessage.body || '',
      timestamp: chat.lastMessage.timestamp || Math.floor(Date.now() / 1000),
      fromMe: chat.lastMessage.fromMe || false,
      hasMedia: chat.lastMessage.hasMedia || false
    };
  }

  return {
    id: chat.id._serialized,
    name: chat.name || 'Unknown',
    isGroup: chat.isGroup || false,
    unreadCount: chat.unreadCount || 0,
    avatar: avatar,
    lastMessage: lastMessageFormatted,
    timestamp: chat.timestamp || Math.floor(Date.now() / 1000),
    pinned: chat.pinned || false,
    isMuted: chat.isMuted || false
  };
}

function formatMessage(msg) {
  let ack = msg.ack || 0;
  if (ack < 0) ack = 0;

  let type = "chat";
  if (["chat", "image", "video", "audio", "document", "sticker"].includes(msg.type)) {
    type = msg.type;
  } else if (msg.type === "ptt") {
    type = "audio";
  }

  return {
    id: msg.id._serialized,
    body: msg.body || '',
    fromMe: msg.fromMe || false,
    timestamp: msg.timestamp || Math.floor(Date.now() / 1000),
    type: type,
    hasMedia: msg.hasMedia || false,
    isForwarded: msg.isForwarded || false,
    author: msg.author || null,
    ack: ack
  };
}

// ─── Puppeteer / Chrome Config ──────────────────────────────────────────────
const puppeteerConfig = {
  headless: 'new',
  args: [
    '--no-sandbox',
    '--disable-setuid-sandbox',
    '--disable-dev-shm-usage',
    '--disable-accelerated-2d-canvas',
    '--no-first-run',
    '--disable-gpu',
    '--disable-web-security',
    '--disable-features=IsolateOrigins,site-per-process',
    '--disable-background-timer-throttling',
    '--disable-backgrounding-occluded-windows',
    '--disable-renderer-backgrounding',
    '--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36'
  ]
};

const possiblePaths = [
  process.env.CHROME_BIN,
  '/usr/bin/chromium',
  '/usr/bin/chromium-browser',
  '/usr/bin/google-chrome',
  '/usr/bin/google-chrome-stable'
];
for (const p of possiblePaths) {
  if (p && fs.existsSync(p)) {
    puppeteerConfig.executablePath = p;
    break;
  }
}

// ─── Bootstrap Slot ────────────────────────────────────────────────────────
function bootstrapSlot(slotId) {
  if (slotId < 1 || slotId > 6) return;
  if (slots.has(slotId)) return slots.get(slotId);

  console.log(`[BOOTSTRAP] Initializing Slot ${slotId}...`);
  const { Client, LocalAuth } = require('whatsapp-web.js');

  const client = new Client({
    authStrategy: new LocalAuth({
      clientId: `slot-${slotId}`,
      dataPath: path.join(__dirname, '.wwebjs_auth')
    }),
    puppeteer: puppeteerConfig
  });

  const slotState = {
    waClient: client,
    clientReady: false,
    qrCodeCache: null
  };
  slots.set(slotId, slotState);

  // ─── Listeners ───────────────────────────────────────────────────────────
  client.on('qr', (qr) => {
    console.log(`[WA] QR generated for Slot ${slotId}`);
    
    const QRCode = require('qrcode');
    QRCode.toDataURL(qr, (err, url) => {
      if (err) {
        console.error(`[WA] Failed to generate QR DataURL for Slot ${slotId}:`, err.message);
        return;
      }
      slotState.qrCodeCache = url;
      slotState.clientReady = false;
      
      updateSlotStatus(slotId, 'qrcode');
      broadcast(slotId, { type: 'qr', qr: url });
    });
  });

  client.on('authenticated', () => {
    console.log(`[WA] Slot ${slotId} authenticated successfully.`);
    slotState.qrCodeCache = null;
    updateSlotStatus(slotId, 'authenticated');
    broadcast(slotId, { type: 'authenticated' });
  });

  client.on('ready', () => {
    console.log(`[WA] Slot ${slotId} is ready and connected.`);
    slotState.clientReady = true;
    slotState.qrCodeCache = null;

    const phoneNumber = client.info.wid.user;
    updateSlotStatus(slotId, 'connected', phoneNumber);
    broadcast(slotId, { type: 'ready', phoneNumber });
    broadcast(slotId, { type: 'status', ready: true });
  });

  client.on('message', async (msg) => {
    console.log(`[WA] [Slot ${slotId}] Message received from: ${msg.from}`);
    try {
      const chat = await msg.getChat();
      
      // Auto-Archive Message to MySQL
      msg.senderName = msg._data.notifyName || chat.name;
      await archiveMessage(slotId, msg);

      const formatted = formatMessage(msg);
      broadcast(slotId, { type: 'new_message', chatId: msg.from, data: formatted });

      // Handle Background Push Notification (skip if chat is muted)
      if (!msg.fromMe && !chat.isMuted) {
        try {
          const contact = await msg.getContact();
          const senderName = contact.name || contact.pushname || msg.from.split('@')[0];
          let body = msg.body || '';
          if (!body && msg.hasMedia) {
            body = '📷 Media/Attachment';
          }
          await sendPushNotification(msg.from, senderName, body);
        } catch (fcmErr) {
          console.error('[FCM PUSH ERROR] Background push failed:', fcmErr.message);
        }
      }
    } catch (err) {
      console.error('[WA] Error processing new message:', err.message);
    }
  });

  client.on('message_create', async (msg) => {
    // Archive outgoing messages sent from this phone natively
    if (msg.fromMe) {
      try {
        msg.senderName = 'Me';
        await archiveMessage(slotId, msg);
      } catch (_) {}
    }
  });

  client.on('message_ack', (msg, ack) => {
    try {
      broadcast(slotId, {
        type: 'message_ack',
        messageId: msg.id._serialized,
        chatId: msg.to || msg.from,
        ack: ack
      });
    } catch (err) {
      console.error('[WA] Error broadcasting message_ack:', err.message);
    }
  });

  client.on('vote', (vote) => {
    try {
      console.log(`[WA] [Slot ${slotId}] Vote received for poll: ${vote.pollMessageId}`);
      broadcast(slotId, {
        type: 'poll_vote',
        pollMessageId: vote.pollMessageId,
        sender: vote.sender,
        selectedOptions: vote.selectedOptions,
        timestamp: vote.timestamp
      });
    } catch (err) {
      console.error('[WA] Error broadcasting vote:', err.message);
    }
  });

  client.on('disconnected', (reason) => {
    console.warn(`[WA] Slot ${slotId} disconnected! Reason:`, reason);
    slotState.clientReady = false;
    
    updateSlotStatus(slotId, 'disconnected');
    broadcast(slotId, { type: 'disconnected', reason });
    broadcast(slotId, { type: 'status', ready: false });

    try { client.destroy(); } catch (_) {}
    slots.delete(slotId);

    // Auto-reconnect in 6 seconds
    console.log(`[WA] Slot ${slotId} reconnecting in 6 seconds...`);
    setTimeout(() => bootstrapSlot(slotId), 6000);
  });

  client.initialize().catch((err) => {
    console.error(`[WA] Slot ${slotId} initialization failed:`, err.message);
    slots.delete(slotId);
    setTimeout(() => bootstrapSlot(slotId), 6000);
  });

  return slotState;
}

// ─── WebSocket Packet Handler ─────────────────────────────────────────────
async function handleMessage(ws, raw) {
  let payload;
  try { payload = JSON.parse(raw); } catch (_) {
    return sendTo(ws, { type: 'error', message: 'Invalid JSON payload' });
  }

  const { type, requestId } = payload;
  if (!requestId) return sendTo(ws, { type: 'error', message: 'Missing requestId' });

  const slotId = ws.slotId || 1;
  const slot = slots.get(slotId);
  const respond = (data) => sendTo(ws, { ...data, requestId });

  if (!slot) return respond({ type: 'error', message: `Slot ${slotId} is not initialized` });
  const waClient = slot.waClient;
  const clientReady = slot.clientReady;

  try {
    switch (type) {
      case 'ping':
        return respond({ type: 'pong', ready: clientReady });

      case 'register_fcm': {
        const { token } = payload;
        if (!token) return respond({ type: 'error', message: 'Token is required' });
        fs.writeFileSync(path.join(__dirname, 'fcm_token.json'), JSON.stringify({ token, updatedAt: new Date().toISOString() }));
        return respond({ type: 'fcm_registered', success: true });
      }

      case 'get_conversations': {
        if (!clientReady) return respond({ type: 'error', message: 'WhatsApp is not ready' });
        try {
          const chats = await waClient.getChats();
          const limit = parseInt(payload.limit) || 50;
          const offset = parseInt(payload.offset) || 0;
          const slice = chats.slice(offset, offset + limit);
          const formatted = await Promise.all(slice.map(formatChat));
          return respond({ type: 'conversations', data: formatted, total: chats.length });
        } catch (err) {
          return respond({ type: 'error', message: err.message });
        }
      }

      case 'get_messages': {
        if (!clientReady) return respond({ type: 'error', message: 'WhatsApp is not ready' });
        const { chatId, limit } = payload;
        if (!chatId) return respond({ type: 'error', message: 'chatId is required' });
        const chat = await waClient.getChatById(chatId);
        const messages = await chat.fetchMessages({ limit: parseInt(limit) || 50 });
        return respond({ type: 'messages', chatId, data: messages.map(formatMessage) });
      }

      case 'get_media': {
        if (!clientReady) return respond({ type: 'error', message: 'WhatsApp is not ready' });
        const { messageId } = payload;
        if (!messageId) return respond({ type: 'error', message: 'messageId is required' });
        
        const parts = messageId.split('_');
        if (parts.length < 2) return respond({ type: 'error', message: 'Invalid messageId format' });
        
        const chat = await waClient.getChatById(parts[1]);
        const messages = await chat.fetchMessages({ limit: 100 });
        const msg = messages.find(m => m.id._serialized === messageId);
        if (!msg || !msg.hasMedia) return respond({ type: 'error', message: 'Media not found' });

        let media = null;
        for (let i = 0; i < 3; i++) {
          try {
            media = await msg.downloadMedia();
            if (media && media.data) break;
          } catch (_) {}
          await new Promise(r => setTimeout(r, 1500));
        }
        if (!media) return respond({ type: 'error', message: 'Failed to download media' });

        // On-the-fly Audio Conversion
        if (media.mimetype && (media.mimetype.includes('audio/ogg') || media.mimetype.includes('ogg'))) {
          try {
            media.data = await convertOggToMp3(media.data);
            media.mimetype = 'audio/mp3';
          } catch (_) {}
        }
        return respond({ type: 'media', messageId, data: media.data, mimetype: media.mimetype });
      }

      case 'mark_read': {
        if (!clientReady) return respond({ type: 'error', message: 'WhatsApp is not ready' });
        const { chatId } = payload;
        if (!chatId) return respond({ type: 'error', message: 'chatId is required' });
        const chat = await waClient.getChatById(chatId);
        await chat.sendSeen();
        return respond({ type: 'marked_read', chatId });
      }

      case 'search_conversations': {
        if (!clientReady) return respond({ type: 'error', message: 'WhatsApp is not ready' });
        const query = (payload.query || '').toLowerCase();
        const chats = await waClient.getChats();
        const filtered = chats.filter(c => (c.name || '').toLowerCase().includes(query));
        const formatted = await Promise.all(filtered.slice(0, 50).map(formatChat));
        return respond({ type: 'conversations', data: formatted, search: true });
      }

      default:
        return respond({ type: 'error', message: `Unknown request: ${type}` });
    }
  } catch (err) {
    return respond({ type: 'error', message: err.message });
  }
}

// ─── REST HTTP API Proxy Endpoints ─────────────────────────────────────────
app.use(express.json({ limit: '50mb' }));

app.post('/api/connect', (req, res) => {
  const slotId = parseInt(req.body.slot || req.query.slot) || 1;
  if (slotId < 1 || slotId > 6) return res.status(400).json({ error: 'Slot must be between 1 and 6' });
  
  bootstrapSlot(slotId);
  res.status(200).json({ success: true, message: `Slot ${slotId} initialization triggered.` });
});

app.post('/api/disconnect', async (req, res) => {
  const slotId = parseInt(req.body.slot || req.query.slot) || 1;
  const slot = slots.get(slotId);
  if (!slot) return res.status(404).json({ error: `Slot ${slotId} is not active.` });
  
  try {
    await slot.waClient.destroy();
    slots.delete(slotId);
    await updateSlotStatus(slotId, 'disconnected');
    res.status(200).json({ success: true, message: `Slot ${slotId} disconnected and destroyed.` });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

app.get('/api/slots', (req, res) => {
  const list = [];
  for (let id = 1; id <= 6; id++) {
    const slot = slots.get(id);
    list.push({
      slot: id,
      active: !!slot,
      ready: slot ? slot.clientReady : false,
      hasQrCache: slot ? !!slot.qrCodeCache : false
    });
  }
  res.status(200).json({ success: true, slots: list });
});

app.post('/api/send', async (req, res) => {
  const slotId = parseInt(req.body.slot || req.query.slot) || 1;
  const slot = slots.get(slotId);
  if (!slot || !slot.clientReady) return res.status(503).json({ error: `Slot ${slotId} is not ready` });

  const { phone, message } = req.body;
  if (!phone || !message) return res.status(400).json({ error: 'phone and message are required' });

  try {
    const chatId = phone.includes('@') ? phone : `${phone}@c.us`;
    const sentMsg = await slot.waClient.sendMessage(chatId, message);
    res.status(200).json({ success: true, messageId: sentMsg.id.id });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

app.post('/api/send-media', async (req, res) => {
  const slotId = parseInt(req.body.slot || req.query.slot) || 1;
  const slot = slots.get(slotId);
  if (!slot || !slot.clientReady) return res.status(503).json({ error: `Slot ${slotId} is not ready` });

  const { phone, base64, mimetype, filename, caption } = req.body;
  if (!phone || !base64 || !mimetype) return res.status(400).json({ error: 'phone, base64, and mimetype are required' });

  try {
    let cleanBase64 = base64.trim();
    if (cleanBase64.includes(';base64,')) {
      cleanBase64 = cleanBase64.split(';base64,')[1];
    }

    const { MessageMedia } = require('whatsapp-web.js');
    const media = new MessageMedia(mimetype, cleanBase64, filename || 'file');
    const chatId = phone.includes('@') ? phone : `${phone}@c.us`;
    const sentMsg = await slot.waClient.sendMessage(chatId, media, { caption: caption || '' });
    res.status(200).json({ success: true, messageId: sentMsg.id.id });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

app.post('/api/send-poll', async (req, res) => {
  const slotId = parseInt(req.body.slot || req.query.slot) || 1;
  const slot = slots.get(slotId);
  if (!slot || !slot.clientReady) return res.status(503).json({ error: `Slot ${slotId} is not ready` });

  const { phone, question, options, allowMultiple } = req.body;
  if (!phone || !question || !options || !Array.isArray(options)) {
    return res.status(400).json({ error: 'phone, question, and options (array) are required' });
  }

  try {
    const { Poll } = require('whatsapp-web.js');
    const poll = new Poll(question, options, {
      allowMultipleAnswers: allowMultiple !== false
    });
    const chatId = phone.includes('@') ? phone : `${phone}@c.us`;
    const sentMsg = await slot.waClient.sendMessage(chatId, poll);
    res.status(200).json({ success: true, messageId: sentMsg.id.id });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

app.get('/api/avatar', async (req, res) => {
  const slotId = parseInt(req.query.slot) || 1;
  const slot = slots.get(slotId);
  if (!slot || !slot.clientReady) return res.status(503).json({ error: `Slot ${slotId} is not ready` });

  const { phone } = req.query;
  if (!phone) return res.status(400).json({ error: 'phone parameter is required' });

  try {
    const chatId = phone.includes('@') ? phone : `${phone}@c.us`;
    const avatarUrl = await slot.waClient.getProfilePicUrl(chatId);
    res.status(200).json({ success: true, avatarUrl: avatarUrl || null });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

// ─── NEW: Message Archive endpoints from MySQL ─────────────────────────────
app.get('/api/archive', async (req, res) => {
  const slotId = parseInt(req.query.slot) || 1;
  const { chatId, limit, offset } = req.query;
  if (!chatId) return res.status(400).json({ error: 'chatId parameter is required' });

  try {
    const list = await getChatHistory(slotId, chatId, limit || 50, offset || 0);
    res.status(200).json({ success: true, slot: slotId, chatId, data: list });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

app.get('/api/archive/search', async (req, res) => {
  const slotId = parseInt(req.query.slot) || 1;
  const { query, limit } = req.query;
  if (!query) return res.status(400).json({ error: 'query parameter is required' });

  try {
    const list = await searchMessages(slotId, query, limit || 50);
    res.status(200).json({ success: true, slot: slotId, query, data: list });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

app.get('/health', (req, res) => {
  res.status(200).json({
    status: 'ok',
    slots: slots.size,
    clients: connectedClients.size,
    port: PORT
  });
});

// ─── WebSocket Multi-Slot Connection Handler ──────────────────────────────
wss.on('connection', (ws, req) => {
  const ip = req.socket.remoteAddress;
  
  // Extract slot parameter from query string (e.g. wss://host/?slot=2)
  const query = url.parse(req.url, true).query;
  const slotId = parseInt(query.slot) || 1;

  if (slotId < 1 || slotId > 6) {
    console.warn(`[WS] Connection rejected: Invalid slotId ${slotId} from ${ip}`);
    ws.close(4000, 'Invalid slotId. Must be 1 to 6');
    return;
  }

  ws.slotId = slotId;
  connectedClients.add(ws);
  console.log(`[WS] Client registered to Slot ${slotId} from IP: ${ip}`);

  // Fetch slot status
  const slot = slots.get(slotId);
  sendTo(ws, { type: 'status', ready: slot ? slot.clientReady : false });

  if (slot) {
    if (!slot.clientReady && slot.qrCodeCache) {
      sendTo(ws, { type: 'qr', qr: slot.qrCodeCache });
    }
  } else {
    // Lazy-load Slot 1 by default, others load on demand or if connect API hit
    if (slotId === 1) bootstrapSlot(1);
  }

  ws.on('message', (data) => {
    handleMessage(ws, data.toString());
  });

  ws.on('close', () => {
    connectedClients.delete(ws);
    console.log(`[WS] Client disconnected from Slot ${slotId}`);
  });

  ws.on('error', (err) => {
    console.error(`[WS] Connection error on Slot ${slotId}:`, err.message);
    connectedClients.delete(ws);
  });
});

// ─── Start HTTP Server & Connect Database ─────────────────────────────────
server.listen(PORT, async () => {
  console.log(`[SERVER] Standalone WhatsApp Bridge running on port ${PORT}`);
  try {
    // Initialize MySQL Database
    await initDatabase();

    // Session Migration Logic: Move old session to slot-1 if slot-1 is empty
    const oldSessionPaths = [
      path.join(__dirname, '.wwebjs_auth', 'session-whatsapp-bridge'),
      path.join(__dirname, '.wwebjs_auth', 'session')
    ];
    const slot1SessionPath = path.join(__dirname, '.wwebjs_auth', 'session-slot-1');
    
    if (!fs.existsSync(slot1SessionPath)) {
      for (const oldPath of oldSessionPaths) {
        if (fs.existsSync(oldPath)) {
          console.log(`[MIGRATION] Migrating active WhatsApp session from ${oldPath} to Slot 1...`);
          try {
            if (fs.cpSync) {
              fs.cpSync(oldPath, slot1SessionPath, { recursive: true });
            } else {
              const { execSync } = require('child_process');
              execSync(`cp -R "${oldPath}" "${slot1SessionPath}"`);
            }
            console.log('[MIGRATION] Session migrated successfully! Slot 1 will load connected.');
            break;
          } catch (migrationErr) {
            console.error('[MIGRATION ERROR] Failed to migrate session:', migrationErr.message);
          }
        }
      }
    }

    // Auto-bootstrap Slot 1 at startup
    bootstrapSlot(1);

    // Also auto-bootstrap any other slots that already have saved credentials
    for (let slotId = 2; slotId <= 6; slotId++) {
      const sessionPath = path.join(__dirname, '.wwebjs_auth', `session-slot-${slotId}`);
      if (fs.existsSync(sessionPath)) {
        console.log(`[SERVER] Detected existing credentials for Slot ${slotId}, auto-bootstrapping...`);
        bootstrapSlot(slotId);
      }
    }
  } catch (err) {
    console.error('[CRITICAL] Database initialization or startup failed:', err.message);
  }
});

// ─── OGG to MP3 base64 converter using ffmpeg ─────────────────────────────
function convertOggToMp3(base64Ogg) {
  const { exec } = require('child_process');
  const tmp = require('os').tmpdir();
  const path = require('path');
  const fs = require('fs');

  return new Promise((resolve, reject) => {
    const timeId = Date.now();
    const inputPath = path.join(tmp, `input_${timeId}.ogg`);
    const outputPath = path.join(tmp, `output_${timeId}.mp3`);

    fs.writeFile(inputPath, Buffer.from(base64Ogg, 'base64'), (err) => {
      if (err) return reject(err);

      exec(`ffmpeg -i ${inputPath} -acodec libmp3lame -y ${outputPath}`, (execErr) => {
        // Clean up input ogg file
        fs.unlink(inputPath, () => {});

        if (execErr) return reject(execErr);

        fs.readFile(outputPath, (readErr, data) => {
          // Clean up output mp3 file
          fs.unlink(outputPath, () => {});

          if (readErr) return reject(readErr);
          resolve(data.toString('base64'));
        });
      });
    });
  });
}
