import makeWASocket, {
  DisconnectReason,
  fetchLatestBaileysVersion,
  Browsers,
  WASocket
} from "@whiskeysockets/baileys";
import { Boom } from "@hapi/boom";
import pino from "pino";
import { setConnectionState, getConnectionState, getPendingOutbox, markOutboxSent, normalizeJid, clearAllSessionFiles } from "../db";
import { handleIncomingMessages } from "./handler";
import { useSQLiteAuthState } from "./sqlite-auth-state";
import { dispatchWebhook } from "../webhooks";

const logger = pino({ level: "silent" });

export interface BotHandle {
  sock: any;
  shutdown: () => Promise<void>;
}

export let handle: BotHandle | null = null;
let reconnectTimer: NodeJS.Timeout | null = null;
let drainerTimer: NodeJS.Timeout | null = null;

/* ── Outbox Drainer ─────────────────────────────────────────────────────────
   Runs every 1.5s and delivers any queued messages from the CRM to WhatsApp.
   This is what makes "Human mode" messaging actually work.
   ───────────────────────────────────────────────────────────────────────── */
function startOutboxDrainer(sock: WASocket): NodeJS.Timeout {
  return setInterval(async () => {
    try {
      const pending = getPendingOutbox(10);
      for (const item of pending) {
        const cleanJid = normalizeJid(item.phone);
        try {
          await sock.sendMessage(cleanJid, { text: item.content });
          markOutboxSent(item.id);
          console.log(`[bot] ✉ Outbox → enviado a ${cleanJid}: "${item.content.slice(0, 60)}"`);
        } catch (sendErr: any) {
          console.error(`[bot] ✘ Outbox → error enviando id=${item.id} a ${cleanJid}:`, sendErr?.message || sendErr);
        }
      }
    } catch {
      // Silent catch — prevents drainer from dying on transient errors
    }
  }, 1500);
}

function stopOutboxDrainer() {
  if (drainerTimer) {
    clearInterval(drainerTimer);
    drainerTimer = null;
  }
}

export async function start(): Promise<BotHandle> {
  if (reconnectTimer) {
    clearTimeout(reconnectTimer);
    reconnectTimer = null;
  }

  console.log("[bot] Iniciando conexión con WhatsApp...");

  let version: [number, number, number] | undefined;
  try {
    const fetched = await fetchLatestBaileysVersion();
    version = fetched.version;
    console.log(`[bot] Versión de WhatsApp Web obtenida: ${version.join(".")}`);
  } catch (err) {
    console.warn("[bot] No se pudo descargar la última versión de WhatsApp Web, usando fallback interno.");
  }

  // ── Fase 2: Usar SQLite Auth State en vez del filesystem ──────────────────
  // Esto permite que la sesión sobreviva redeploys en Railway sin re-escanear QR.
  const { state, saveCreds } = await useSQLiteAuthState();

  const sock = makeWASocket({
    version,
    auth: state,
    logger,
    browser: Browsers.macOS("Desktop"), // Evita error 440
    markOnlineOnConnect: false,
    syncFullHistory: false,
  });

  sock.ev.on("creds.update", saveCreds);

  const shutdown = async () => {
    console.log("[bot] Desconectando y apagando bot...");
    stopOutboxDrainer();
    try {
      await sock.logout();
    } catch {}
    try {
      sock.end(undefined);
    } catch {}
  };

  const botHandle: BotHandle = { sock, shutdown };
  handle = botHandle;

  sock.ev.on("connection.update", async (update) => {
    const { connection, lastDisconnect, qr } = update;

    if (qr) {
      console.log("[bot] Código QR de vinculación generado:");
      try {
        const QRCodeTerminal = await import("qrcode-terminal");
        QRCodeTerminal.default.generate(qr, { small: true });
      } catch {}
      setConnectionState({ status: "qr", qr_string: qr, phone: null });
      dispatchWebhook("session.status", { status: "qr" }).catch(() => {});
    }

    if (connection === "connecting") {
      const dbState = getConnectionState();
      if (dbState.status === "disconnected") {
        setConnectionState({ status: "connecting" });
      }
    }

    if (connection === "open") {
      const rawId = sock.user?.id || "";
      const phone = rawId.split(":")[0].split("@")[0];
      console.log(`[bot] ✅ Conexión abierta. Número vinculado: ${phone}`);
      setConnectionState({ status: "connected", qr_string: null, phone });
      dispatchWebhook("session.status", { status: "connected", phone }).catch(() => {});

      // Start the outbox drainer so human-mode messages actually get sent
      stopOutboxDrainer();
      drainerTimer = startOutboxDrainer(sock);
      console.log("[bot] 🚀 Outbox drainer iniciado.");
    }

    if (connection === "close") {
      const code = (lastDisconnect?.error as Boom)?.output?.statusCode;
      console.log(`[bot] Conexión cerrada con código: ${code}`);

      // Stop drainer immediately on disconnect
      stopOutboxDrainer();

      if (code === DisconnectReason.loggedOut) {
        console.log("[bot] Sesión cerrada (logged out). Limpiando credenciales en SQLite...");
        setConnectionState({ status: "disconnected", qr_string: null, phone: null });
        clearAllSessionFiles(); // Limpiar sesión de SQLite (no del filesystem)
        dispatchWebhook("session.status", { status: "disconnected" }).catch(() => {});
      } else {
        console.log("[bot] Desconexión temporal, reintentando...");
        dispatchWebhook("session.status", { status: "connecting" }).catch(() => {});
        scheduleReconnect(code === 440 ? 15000 : 5000);
      }
    }
  });

  handleIncomingMessages(sock);

  return botHandle;
}

function scheduleReconnect(delay: number) {
  if (reconnectTimer) return;
  reconnectTimer = setTimeout(async () => {
    reconnectTimer = null;
    if (handle) {
      try {
        handle.sock.end(undefined);
      } catch {}
      handle = null;
    }
    await start();
  }, delay);
}


