/**
 * Adaptador de WhatsApp (ticket CRM-22). Ver docs/spec-crm-360.md §6.6.1.
 *
 * El CRM no habla con Meta ni con Evolution directamente: habla con esta
 * interfaz. Cambiar de conector es configuración del canal, no código — que es
 * justo lo que permite desarrollar y probar F2 contra Evolution con un número de
 * prueba y pasar a la API oficial cuando Meta apruebe el número, sin tocar la
 * bandeja ni la ingesta.
 */

export type ProveedorWhatsApp = 'meta_cloud' | 'evolution_cloud' | 'evolution_baileys';

export type CategoriaPlantilla = 'marketing' | 'utility' | 'authentication' | 'service';

/** Lo mínimo que un proveedor necesita del canal para poder enviar. */
export interface CanalWhatsApp {
  id: string;
  empresa_id: string;
  proveedor: ProveedorWhatsApp;
  identificador: string | null;
  identificador_externo: string | null;
  /** Ya descifradas por `CanalesService`: el proveedor nunca ve el cifrado. */
  credenciales: CredencialesWhatsApp;
}

export interface CredencialesWhatsApp {
  /** Meta Cloud API. */
  access_token?: string;
  app_secret?: string;
  /** Evolution (cloud o baileys). */
  base_url?: string;
  api_key?: string;
  instancia?: string;
}

export interface EnvioResult {
  /** Id que devuelve el proveedor; es con el que después llegan los acuses. */
  id_externo: string | null;
  estado: 'enviado' | 'encolado' | 'rechazado';
  /** Con qué categoría lo cobró Meta, cuando lo informa. */
  categoria?: CategoriaPlantilla;
  error?: string;
}

/** Un webhook de Meta puede traer varios eventos en un POST. */
export type WebhookEvento =
  | {
      clase: 'mensaje';
      /** Para rutear al canal sin confiar en nada más del payload. */
      identificador_externo: string | null;
      telefono: string;
      nombre_contacto: string | null;
      id_externo: string;
      tipo: 'texto' | 'adjunto' | 'otro';
      texto: string | null;
      adjunto_url: string | null;
      adjunto_mime: string | null;
      /** Hora que informa el proveedor; la de nuestro reloj puede diferir. */
      fecha: Date;
    }
  | {
      clase: 'estado';
      identificador_externo: string | null;
      id_externo: string;
      estado: 'enviado' | 'entregado' | 'leido' | 'fallido';
      error?: string | null;
      fecha: Date;
    };

export interface IWhatsAppProvider {
  readonly tipo: ProveedorWhatsApp;

  enviarTexto(canal: CanalWhatsApp, para: string, texto: string): Promise<EnvioResult>;

  enviarPlantilla(
    canal: CanalWhatsApp,
    para: string,
    plantilla: string,
    idioma: string,
    variables: string[],
  ): Promise<EnvioResult>;

  enviarAdjunto(
    canal: CanalWhatsApp,
    para: string,
    url: string,
    mime: string,
    descripcion?: string,
  ): Promise<EnvioResult>;

  /**
   * Traduce el webhook del proveedor a eventos nuestros. **Valida la firma**:
   * si no cierra, tira `UnauthorizedException` en vez de devolver vacío, porque
   * un payload sin firma válida no es "sin eventos", es un intento de inyección.
   */
  parsearWebhook(
    headers: Record<string, string | undefined>,
    cuerpoCrudo: string,
    canal: CanalWhatsApp,
  ): WebhookEvento[];

  /** Sólo Evolution en modo Baileys: la sesión por QR se cae sola. */
  estadoSesion?(canal: CanalWhatsApp): Promise<'conectado' | 'desconectado' | 'qr_pendiente'>;
}

/**
 * Los envíos automáticos están prohibidos por `evolution_baileys` (decisión D4
 * de la spec §6.6.1): el protocolo no es oficial y un envío masivo es la forma
 * más rápida de que Meta bloquee el número del cliente para siempre. Los envíos
 * manuales de un agente escribiendo en la bandeja sí se permiten.
 */
export class BaileysAutomaticoBloqueadoError extends Error {
  constructor(canal: string) {
    super(
      `El canal "${canal}" usa Baileys (protocolo no oficial) y no puede hacer envíos ` +
        'automáticos: Meta bloquea el número. Pasá el canal a Meta Cloud API para habilitarlos.',
    );
    this.name = 'BaileysAutomaticoBloqueadoError';
  }
}

export const PROVEEDORES_NO_OFICIALES: ProveedorWhatsApp[] = ['evolution_baileys'];

export function permiteAutomaticos(proveedor: ProveedorWhatsApp): boolean {
  return !PROVEEDORES_NO_OFICIALES.includes(proveedor);
}
