import { TOOL_DEFINITIONS, executeTool } from "./tools";

const MISTRAL_API_URL = "https://api.mistral.ai/v1";
const MODEL = "mistral-large-latest";
const OCR_MODEL = "mistral-ocr-latest";

function mistralHeaders() {
  return {
    "Content-Type": "application/json",
    Authorization: `Bearer ${(process.env.MISTRAL_API_KEY ?? "").trim()}`,
  };
}

const SYSTEM_PROMPT = `Sos el asistente de gestión del inmueble Arcos 1836, CABA (uso gastronómico).
Contrato:
- Locadora: Gloria Ruiz Perkins
- Locatarios: Juan Andrés Gelly y Obes y Alfredo Cayetano Cogorno
- Vigencia: 01/03/2025 – 28/02/2028
- Monto base: $3.500.000 ARS/mes
- Ajuste: trimestral por IPC (jun, sep, dic, mar)
- Mora: 0,30%/día sobre diferencia impaga
- Acuerdo verbal actual: $4.500.000/mes desde mar 2026

Personas autorizadas en este chat:
- Nacho (Ignacio Lagos Ruiz): hijo de la locadora, representante autorizado junto con su hermana Luli. Gestiona cobro de alquileres y servicios.
- Luli (Eleonora Civit Ruiz): hija de la locadora, representante autorizada junto con su hermano Nacho. Gestiona cobro de alquileres y servicios.
- Gloria (Gloria Ruiz Perkins): locadora del inmueble, madre de Nacho y Luli.
- Alfredo (Alfredo Cogorno): locatario del inmueble. NO es dueño — es el inquilino que paga alquiler.

Nacho y Luli representan a la locadora. NO son locatarios. Alfredo y Juan son los locatarios.

Servicios activos que paga el inquilino:
- ABL: 33% de la factura bimestral
- AYSA: 70% de la factura bimestral
- Destapaciones: 100%

Respondés en español rioplatense, de forma concisa y clara.
Usás las herramientas disponibles para consultar datos reales de la base de datos.
IMPORTANTE: Cuando el usuario pide registrar un pago y da los datos necesarios (monto, período, etc.), ejecutá la herramienta INMEDIATAMENTE sin pedir confirmación. Cada mensaje es independiente — NO hay memoria entre mensajes, así que pedir confirmación no tiene sentido porque no vas a recordar nada en el siguiente mensaje.
Para cualquier pedido de tabla, historial, detalle, últimos pagos, resumen estructurado o imagen de datos, usás SIEMPRE la herramienta generar_reporte_tabla. Esta herramienta genera una imagen con datos correctos de la base de datos. Llamás a generar_reporte_tabla UNA SOLA VEZ por consulta — el sistema solo muestra la primera tabla generada.

MAPEO DE REPORTES DE SERVICIOS:
- "deuda de servicios" / "resumen de servicios" → tipo=deuda_servicios_resumen (solo totales por servicio)
- "deuda de servicios mensual" / "deuda por mes" / "desglose mensual" / "detalle mensual de servicios" / "deuda detallada" → tipo=deuda_servicios_detallada (desglose por período y servicio)
- "detalle de [nombre servicio]" / "historial de [nombre]" → tipo=servicio_detalle (con nombre_servicio)

REGLA CRÍTICA: Cuando usás generar_reporte_tabla, NUNCA incluís tablas Markdown ni números en tu texto de respuesta. El sistema envía la tabla como imagen por separado. Tu texto debe ser SOLO un mensaje breve de contexto (ej: "Acá tenés el detalle de la deuda de servicios al día de hoy, DD/MM/YYYY:") SIN números ni tablas. Si inventás números o incluís una tabla Markdown en el texto, esos datos serán INCORRECTOS y generarás confusión. NUNCA inventes números — dejá que las herramientas provean los datos.
No uses asteriscos ni underscores para resaltar texto. En encabezados y celdas usá texto plano.
La fecha de hoy es: ${new Date().toLocaleDateString("es-AR", { weekday: "long", year: "numeric", month: "long", day: "numeric" })}.`;

export interface MistralMessage {
  role: "user" | "assistant" | "tool";
  content: string;
  tool_call_id?: string;
}

export interface ChatAttachment {
  kind: "document";
  url: string;
  filename: string;
  mimetype: string;
}

export interface ChatTableReport {
  title: string;
  watermarkTitle?: string;
  markdown: string;
}

export interface ChatWithToolsResult {
  text: string;
  attachments: ChatAttachment[];
  tableReports: ChatTableReport[];
}

function dedupeAttachments(attachments: ChatAttachment[]): ChatAttachment[] {
  const seen = new Set<string>();

  return attachments.filter((attachment) => {
    const key = [attachment.kind, attachment.url, attachment.filename, attachment.mimetype].join("|");
    if (seen.has(key)) return false;
    seen.add(key);
    return true;
  });
}

function dedupeTableReports(reports: ChatTableReport[]): ChatTableReport[] {
  const seen = new Set<string>();

  return reports.filter((report) => {
    const key = [report.title, report.watermarkTitle ?? "", report.markdown].join("|");
    if (seen.has(key)) return false;
    seen.add(key);
    return true;
  });
}

function extractToolArtifacts(text: string): {
  text: string;
  attachments: ChatAttachment[];
  tableReports: ChatTableReport[];
  hadCsvError: boolean;
} {
  let remaining = text;
  const attachments: ChatAttachment[] = [];
  const tableReports: ChatTableReport[] = [];
  const csvPrefix = "[CSV_FILE:";
  const tablePrefix = "[[TABLE_REPORT:";

  while (true) {
    const start = remaining.indexOf(tablePrefix);
    if (start === -1) break;

    const end = remaining.indexOf("]]", start);
    if (end === -1) break;

    const payload = remaining.slice(start + tablePrefix.length, end);

    try {
      const parsed = JSON.parse(payload) as ChatTableReport;
      if (parsed.title && parsed.markdown) {
        tableReports.push({
          title: parsed.title,
          watermarkTitle: parsed.watermarkTitle,
          markdown: parsed.markdown,
        });
      }
    } catch (error) {
      console.error("Failed to parse table report artifact:", error);
    }

    remaining = `${remaining.slice(0, start)}${remaining.slice(end + 2)}`.trim();
  }

  while (true) {
    const start = remaining.indexOf(csvPrefix);
    if (start === -1) break;

    const end = remaining.indexOf("]", start);
    if (end === -1) break;

    const payload = remaining.slice(start + csvPrefix.length, end);
    const separator = payload.lastIndexOf(":");
    if (separator === -1) break;

    const url = payload.slice(0, separator).trim();
    const filename = payload.slice(separator + 1).trim();
    if (!url || !filename) break;

    attachments.push({
      kind: "document",
      url,
      filename,
      mimetype: "text/csv",
    });

    remaining = `${remaining.slice(0, start)}${remaining.slice(end + 1)}`.trim();
  }

  const hadCsvError = remaining.includes("[CSV_ERROR]");

  remaining = remaining
    .replace(/\[CSV_ERROR\]\s*/g, "")
    .replace(/\n{3,}/g, "\n\n")
    .trim();

  return {
    text: remaining,
    attachments: dedupeAttachments(attachments),
    tableReports: dedupeTableReports(tableReports),
    hadCsvError,
  };
}

export async function chatWithTools(userMessage: string): Promise<ChatWithToolsResult> {
  const messages: object[] = [
    { role: "system", content: SYSTEM_PROMPT },
    { role: "user", content: userMessage },
  ];
  const attachments: ChatAttachment[] = [];
  const tableReports: ChatTableReport[] = [];

  // Agentic loop: up to 5 iterations for tool calls
  for (let i = 0; i < 5; i++) {
    const res = await fetch(`${MISTRAL_API_URL}/chat/completions`, {
      method: "POST",
      headers: mistralHeaders(),
      body: JSON.stringify({
        model: MODEL,
        messages,
        tools: TOOL_DEFINITIONS,
        tool_choice: "auto",
        max_tokens: 1024,
      }),
    });

    if (!res.ok) {
      const err = await res.text();
      console.error("Mistral chat error:", err);
      return {
        text: "Hubo un error al procesar tu consulta. Intentá de nuevo.",
        attachments: [],
        tableReports: [],
      };
    }

    const data = await res.json();
    const choice = data.choices?.[0];
    if (!choice) {
      return {
        text: "Sin respuesta del modelo.",
        attachments: dedupeAttachments(attachments),
        tableReports: dedupeTableReports(tableReports),
      };
    }

    const message = choice.message;
    messages.push(message);

    if (choice.finish_reason === "stop" || !message.tool_calls?.length) {
      const extracted = extractToolArtifacts(message.content ?? "Sin respuesta.");
      attachments.push(...extracted.attachments);
      tableReports.push(...extracted.tableReports);

      return {
        text: extracted.text || "Sin respuesta.",
        attachments: dedupeAttachments(attachments),
        tableReports: dedupeTableReports(tableReports),
      };
    }

    // Execute all tool calls
    for (const toolCall of message.tool_calls) {
      const toolName = toolCall.function.name;
      const toolArgs = JSON.parse(toolCall.function.arguments ?? "{}");
      const result = await executeTool(toolName, toolArgs);
      const extracted = extractToolArtifacts(result);
      attachments.push(...extracted.attachments);
      tableReports.push(...extracted.tableReports);

      messages.push({
        role: "tool",
        tool_call_id: toolCall.id,
        content:
          extracted.text ||
          (extracted.tableReports.length > 0
            ? `Reporte generado: ${extracted.tableReports.map((report) => report.title).join(", ")}.`
            : null) ||
          (extracted.hadCsvError
            ? "No se pudo generar el archivo CSV solicitado."
            : "Operación completada."),
      });
    }
  }

  return {
    text: "No pude completar la consulta. Intentá con una pregunta más específica.",
    attachments: dedupeAttachments(attachments),
    tableReports: dedupeTableReports(tableReports),
  };
}

export interface OcrResult {
  amount?: number;
  date?: string;
  reference?: string;
  bank?: string;
  rawText: string;
}

export async function ocrReceipt(imageBase64: string, mimeType = "image/jpeg"): Promise<OcrResult> {
  const documentPayload = mimeType === "application/pdf"
    ? {
        type: "document_url",
        document_url: `data:${mimeType};base64,${imageBase64}`,
      }
    : {
        type: "image_url",
        image_url: `data:${mimeType};base64,${imageBase64}`,
      };

  const res = await fetch(`${MISTRAL_API_URL}/ocr`, {
    method: "POST",
    headers: mistralHeaders(),
    body: JSON.stringify({
      model: OCR_MODEL,
      document: documentPayload,
    }),
  });

  if (!res.ok) {
    console.error("Mistral OCR error:", await res.text());
    return { rawText: "" };
  }

  const data = await res.json();
  const rawText: string = data.pages?.[0]?.markdown ?? data.text ?? "";

  // Extract structured data from raw OCR text using the chat model
  const extractRes = await fetch(`${MISTRAL_API_URL}/chat/completions`, {
    method: "POST",
    headers: mistralHeaders(),
    body: JSON.stringify({
      model: MODEL,
      messages: [
        {
          role: "user",
          content: `Del siguiente texto OCR de un comprobante bancario, extraé: monto (número sin puntos ni comas), fecha (YYYY-MM-DD), número de referencia/comprobante, y nombre del banco. Respondé SOLO con JSON válido con keys: amount (number|null), date (string|null), reference (string|null), bank (string|null).\n\nTexto OCR:\n${rawText}`,
        },
      ],
      response_format: { type: "json_object" },
      max_tokens: 256,
    }),
  });

  if (!extractRes.ok) return { rawText };

  const extractData = await extractRes.json();
  const extracted = JSON.parse(extractData.choices?.[0]?.message?.content ?? "{}");

  return {
    amount: extracted.amount ?? undefined,
    date: extracted.date ?? undefined,
    reference: extracted.reference ?? undefined,
    bank: extracted.bank ?? undefined,
    rawText,
  };
}
