/**
 * sqlite-auth-state.ts
 *
 * Custom Baileys Auth State que guarda las credenciales de sesión
 * directamente en SQLite (tabla wa_session_backup) en lugar del filesystem.
 *
 * Inspirado en el patrón RemoteAuth de OpenWA (whatsapp-web.js).
 * Esto permite que la sesión de WhatsApp sobreviva reinicios y redeploys
 * en Railway.app sin necesitar escanear el QR nuevamente.
 */

import {
  AuthenticationState,
  SignalDataTypeMap,
  initAuthCreds,
  proto,
  BufferJSON,
} from "@whiskeysockets/baileys";
import {
  saveSessionFile,
  loadSessionFiles,
  deleteSessionFile,
} from "../db";

/**
 * Carga o inicializa el estado de autenticación desde la tabla wa_session_backup.
 * Si la tabla está vacía, inicia con credenciales nuevas (genera nuevo QR).
 * Si la tabla tiene datos, restaura la sesión anterior (no necesita QR).
 */
export async function useSQLiteAuthState(): Promise<{
  state: AuthenticationState;
  saveCreds: () => Promise<void>;
}> {
  // Cargar todos los archivos de sesión guardados en SQLite
  const rows = loadSessionFiles();
  const fileMap = new Map(rows.map((r) => [r.file_name, r.content]));

  // Leer credenciales principales (creds.json)
  let creds: AuthenticationState["creds"];
  const credsRaw = fileMap.get("creds.json");
  if (credsRaw) {
    try {
      creds = JSON.parse(credsRaw, BufferJSON.reviver);
      console.log("[sqlite-auth] ✅ Credenciales restauradas desde SQLite.");
    } catch {
      console.warn("[sqlite-auth] ⚠️  Error al parsear credenciales, iniciando sesión nueva.");
      creds = initAuthCreds();
    }
  } else {
    console.log("[sqlite-auth] 🆕 No hay sesión guardada, iniciando sesión nueva.");
    creds = initAuthCreds();
  }

  /**
   * Implementación del store de señales usando el patrón correcto de Baileys.
   * La función get debe ser genérica para que TypeScript infiera el tipo correcto.
   */
  const keys: AuthenticationState["keys"] = {
    get: async <T extends keyof SignalDataTypeMap>(
      type: T,
      ids: string[]
    ): Promise<{ [id: string]: SignalDataTypeMap[T] }> => {
      const data: { [id: string]: SignalDataTypeMap[T] } = {};

      await Promise.all(
        ids.map(async (id) => {
          const fileName = `${type}-${id}.json`;
          const raw = fileMap.get(fileName);
          if (!raw) return;

          try {
            let value = JSON.parse(raw, BufferJSON.reviver);
            // Baileys espera que las app-state-sync-key estén como objetos proto
            if (type === "app-state-sync-key" && value) {
              value = proto.Message.AppStateSyncKeyData.fromObject(value);
            }
            data[id] = value as SignalDataTypeMap[T];
          } catch {
            // Archivo corrupto — ignorar
          }
        })
      );

      return data;
    },

    set: async (data: { [K in keyof SignalDataTypeMap]?: { [id: string]: SignalDataTypeMap[K] | null | undefined } }): Promise<void> => {
      const tasks: Array<() => void> = [];

      for (const _type of Object.keys(data) as Array<keyof SignalDataTypeMap>) {
        const typeData = data[_type];
        if (!typeData) continue;

        for (const [id, value] of Object.entries(typeData)) {
          const fileName = `${_type}-${id}.json`;
          if (value != null) {
            const content = JSON.stringify(value, BufferJSON.replacer);
            fileMap.set(fileName, content);
            tasks.push(() => saveSessionFile(fileName, content));
          } else {
            fileMap.delete(fileName);
            tasks.push(() => deleteSessionFile(fileName));
          }
        }
      }

      // Ejecutar todas las operaciones de DB de forma síncrona (better-sqlite3 es síncrono)
      tasks.forEach((fn) => fn());
    },
  };

  /**
   * Guarda las credenciales principales (creds.json) en SQLite.
   * Se llama automáticamente por Baileys en el evento "creds.update".
   */
  const saveCreds = async (): Promise<void> => {
    const content = JSON.stringify(creds, BufferJSON.replacer);
    fileMap.set("creds.json", content);
    saveSessionFile("creds.json", content);
  };

  return { state: { creds, keys }, saveCreds };
}
