import {
  normalizarReferencia,
  normalizarReferenciaExtraida,
  ReferenciaDocumento,
} from 'src/catalogo/normalizar-referencia';
import { interpretarTipo, TipoDocumento } from 'src/catalogo/tipos-documento';
import { DocumentoRef, EstadoBot, Extraccion } from './tipos';

/**
 * Máquina de estados del turno (plan §6.4). Código puro: decide QUÉ consultar
 * antes de llamar al redactor y qué instrucción darle. El modelo no decide el
 * flujo; solo redacta.
 *
 * Las consultas se hacen ANTES del redactor y sus resultados le llegan como
 * <datos_del_sistema> en el turno del cliente, no como llamadas a herramienta:
 * con un tool_result en la conversación, la API entrega el texto de la
 * respuesta en una sola ráfaga al final, en lugar de ir mostrándolo (medido el
 * 24/10/2026). Las herramientas quedan para seguimientos que no se previeron.
 */
export interface Consulta {
  herramienta: string;
  input: Record<string, unknown>;
}

export interface Plan {
  consultas: Consulta[];
  instruccion: string;
  pideReferencia?: true;
}

const INSTRUCCION_DIAGNOSTICO =
  'Ya consulté el documento (en datos_del_sistema). Explicale al cliente el diagnóstico tal como viene: qué pasó, si tiene que hacer algo y dónde. No lo contradigas.';

// Sin \b después de "sé": en JS \b solo ve letras ASCII y la é no cuenta como letra.
const CATEGORIAS_DOCUMENTO = new Set(['documento_sifen', 'evento_sifen', 'envio_correo']);

const NO_SABE_NUMERO = /\bno\s+(?:s[eé]|tengo|recuerdo|encuentro|me\s+acuerdo)(?![a-záéíóúñ])[^.?!]{0,25}n[uú]mero/i;

const estadoDocumento = (referencia: string, tipo: TipoDocumento | null): Consulta => ({
  herramienta: 'q_estado_documento',
  input: { referencia, tipo: tipo ?? 'CUALQUIERA' },
});

export function decidirPlan(extraccion: Extraccion, texto: string, estado: EstadoBot): Plan {
  const intencionDocumento = extraccion.intencion === 'incidente_documento' || extraccion.intencion === 'consulta_dato';
  // Una consulta de un dato puede no ser sobre un documento ("¿cuánto cuesta el plan Pro?"):
  // en ese caso no se pide un número de documento.
  // Un incidente con un documento es, por definición, sobre un documento.
  const sobreDocumento =
    extraccion.intencion === 'incidente_documento' ||
    (extraccion.intencion === 'consulta_dato' &&
      (!!extraccion.tipoDocumento ||
        CATEGORIAS_DOCUMENTO.has(extraccion.categoria) ||
        !!interpretarTipo(texto) ||
        /\b(documento|cdc|kude|sifen)\b/i.test(texto)));

  // 1. Eligió uno de los candidatos que se le mostraron ("la nota de crédito", "c2", "la segunda").
  const elegido = elegirCandidato(texto, extraccion, estado.candidatos ?? []);
  if (elegido) {
    return { consultas: [estadoDocumento(elegido.codigo, elegido.tipo)], instruccion: INSTRUCCION_DIAGNOSTICO };
  }

  // 2. Pregunta por los datos de operaciones de la empresa: el consultor escribe el SQL.
  //    Va antes que la referencia: "¿cuánto se cobró de la factura 123?" es de cobros, no
  //    del estado en SIFEN (el consultor filtra por el número). Un número completo o un CDC
  //    solo, sin pregunta, ni llega acá: extraccionDirecta lo manda al diagnóstico.
  if (extraccion.intencion === 'consulta_negocio') {
    return {
      consultas: [{ herramienta: 'q_consulta_negocio', input: { pregunta: texto } }],
      instruccion:
        'Pregunta por los datos de su empresa: el resultado de la consulta está en datos_del_sistema. ' +
        'Respondé con esas cifras. Si los datos muestran un problema (una diferencia, un saldo raro, algo trabado), ' +
        'explicá qué se ve y, si sirve, cómo revisarlo o resolverlo (q_buscar_guia). El cierre es resuelto, ' +
        'o no_resuelto con sin_permiso o fuera_de_alcance si no se pudo consultar.',
    };
  }

  // 3. Dio un número o un CDC. La referencia es la que marcó el extractor: no se vuelve a
  //    buscar un número en el texto, porque "tengo 3 facturas" tomaría el 3 como documento
  //    (pasó en la comparación de modelos del 24/10). Los mensajes con número completo o
  //    CDC ni pasan por el extractor (extraccionDirecta).
  const ref = normalizarReferenciaExtraida(extraccion.referencia);
  if (ref) {
    return {
      consultas: [estadoDocumento(textoReferencia(ref), extraccion.tipoDocumento)],
      instruccion: INSTRUCCION_DIAGNOSTICO,
    };
  }

  // 4. Vuelve sobre el documento de antes ("ya lo reenvié", "¿y ahora?"): verificar de nuevo.
  if (estado.documentoActivo && (intencionDocumento || extraccion.intencion === 'seguimiento')) {
    const verificacion = estado.accionPendiente
      ? ' Antes le indicaste una acción: decile si ya se ve el cambio o si sigue igual.'
      : '';
    return {
      consultas: [estadoDocumento(estado.documentoActivo.codigo, estado.documentoActivo.tipo)],
      instruccion: INSTRUCCION_DIAGNOSTICO + verificacion,
    };
  }

  // 5. Pregunta por la empresa, no por un documento: timbrado, certificado o alertas.
  //    Pedirle un número de documento acá no tiene sentido.
  //    "¿Cómo cargo el certificado?" es una pregunta de uso: va a las guías, más abajo.
  const deUso = extraccion.intencion === 'consulta_operativa';
  if (!deUso && (extraccion.categoria === 'timbrado_certificado' || /\btimbrado|certificado/i.test(texto))) {
    return {
      consultas: [{ herramienta: 'q_salud_empresa', input: {} }],
      instruccion:
        'Pregunta por la configuración de su empresa: el estado está en datos_del_sistema. Explicalo y, si hay algo vencido, qué hacer.',
    };
  }
  if (!deUso && /\balertas?\b/i.test(texto)) {
    return {
      consultas: [{ herramienta: 'q_alertas_abiertas', input: {} }],
      instruccion:
        'Pregunta por las alertas de su empresa: están en datos_del_sistema. Resumilas y decile qué hacer con cada una.',
    };
  }

  // 6. Habla de un documento pero no dijo cuál. Si ya dijo que no sabe el número,
  //    no se le pregunta: se le muestran sus documentos con problemas.
  if (sobreDocumento) {
    if (estado.pedidosDeReferencia === 0 && !NO_SABE_NUMERO.test(texto)) {
      return {
        consultas: [],
        pideReferencia: true,
        instruccion:
          'El cliente habla de un documento pero no dijo cuál. Pedile UNA vez el número (Ej: 001-001-0000123) o el CDC, y el tipo si no lo dijo. ' +
          'Si dice que todavía no pudo emitirlo, no pidas número: ayudalo con q_buscar_guia. El cierre es incompleto.',
      };
    }
    return {
      consultas: [
        {
          herramienta: 'q_listar_documentos',
          input: { estados: ['Rechazado', 'Error', 'Pendiente', 'Documento firmado'], dias: 7, tipo: 'CUALQUIERA' },
        },
        { herramienta: 'q_alertas_abiertas', input: {} },
      ],
      instruccion:
        'No le pidas el número (ya se lo pediste, o dijo que no lo sabe). En datos_del_sistema están sus documentos con problemas de los últimos 7 días y las alertas abiertas: ' +
        'si hay uno claro, explicalo; si hay varios, mostrale tipo, número y fecha con el código (c1, c2…) para que elija.',
    };
  }

  // Pregunta de uso, o un dato que no es de un documento: se responde con las guías.
  if (extraccion.intencion === 'consulta_operativa' || intencionDocumento) {
    return {
      consultas: [{ herramienta: 'q_buscar_guia', input: { pregunta: texto } }],
      instruccion:
        'Es una pregunta de uso del sistema. Respondé como un manual (respuesta directa, pasos con la pantalla y los nombres exactos de botones y campos, requisitos previos) con lo que trae datos_del_sistema; si falta un detalle, q_leer_codigo. No la derives a Novasis. Solo si datos_del_sistema no dice nada útil, el cierre es no_resuelto con motivo consulta_sin_respuesta_en_guias.',
    };
  }

  if (extraccion.intencion === 'seguimiento') {
    return {
      consultas: [{ herramienta: 'q_mis_tickets', input: {} }],
      instruccion: 'Pregunta por una consulta anterior: sus tickets recientes están en datos_del_sistema.',
    };
  }

  return {
    consultas: [],
    instruccion:
      'No hay una consulta concreta todavía. Respondé breve y preguntá en qué lo podés ayudar (documentos electrónicos, datos de su empresa como ventas, cobros o stock, alertas o uso del sistema). El cierre es saludo.',
  };
}

function textoReferencia(ref: ReferenciaDocumento): string {
  if (ref.clase === 'cdc') return ref.cdc;
  return ref.establecimiento && ref.puntoExpedicion
    ? `${ref.establecimiento}-${ref.puntoExpedicion}-${ref.numero}`
    : ref.numero;
}

const ORDINALES: Record<string, number> = { primera: 1, primero: 1, segunda: 2, segundo: 2, tercera: 3, tercero: 3 };

function elegirCandidato(texto: string, extraccion: Extraccion, candidatos: DocumentoRef[]): DocumentoRef | null {
  if (candidatos.length === 0) return null;
  const t = texto.toLowerCase();

  const codigo = t.match(/\bc(\d{1,3})\b/);
  if (codigo) return candidatos.find((c) => c.codigo === `c${codigo[1]}`) ?? null;

  const ordinal = Object.keys(ORDINALES).find((o) => new RegExp(`\\b${o}\\b`).test(t));
  if (ordinal) return candidatos[ORDINALES[ordinal] - 1] ?? null;

  // "la nota de crédito": un único candidato de ese tipo. Si además da un número, manda el número.
  const tipo = extraccion.tipoDocumento ?? interpretarTipo(texto);
  if (tipo && !normalizarReferencia(texto)) {
    const delTipo = candidatos.filter((c) => c.tipo === tipo);
    if (delTipo.length === 1) return delTipo[0];
  }
  return null;
}
