import { db } from "@/lib/db";
import { pagos, servicios, gastosServicios, pagosServicios } from "@/lib/schema";
import { eq, desc } from "drizzle-orm";
import { calcularResumen } from "@/lib/penalties";
import { calcularAjustesIpc } from "@/lib/ipc";
import { getContratoData, getIpcData, getAcuerdoVerbalFromDb } from "@/lib/data";
import { syncPagosServicios } from "@/lib/sync-pagos-servicios";
import { buildServicePaymentNotes, type ServicePaymentMode } from "@/lib/service-payment-meta";
import { REPORT_TYPES, generarReporteTabla, type ReportType } from "./reports";
import { uploadMedia } from "./evolution";

// ── Mistral tool definitions ──────────────────────────────────────────────────

export const TOOL_DEFINITIONS = [
  {
    type: "function" as const,
    function: {
      name: "generar_reporte_tabla",
      description:
        "Genera un reporte tabular determinístico para WhatsApp. Usar para tablas, historiales, últimos pagos, resúmenes, detalles e imágenes de datos. No inventa columnas: aplica un esquema fijo según el tipo pedido.",
      parameters: {
        type: "object",
        properties: {
          tipo: {
            type: "string",
            enum: [...REPORT_TYPES],
            description: "Tipo de reporte fijo a generar",
          },
          nombre_servicio: {
            type: "string",
            description: "Obligatorio para tipo=servicio_detalle. Nombre del servicio a consultar.",
          },
          limite: {
            type: "number",
            description: "Opcional. Cantidad máxima de filas para historiales de pagos.",
          },
        },
        required: ["tipo"],
      },
    },
  },
  {
    type: "function" as const,
    function: {
      name: "consultar_deuda_alquiler",
      description:
        "Consulta la deuda total de alquiler: cuotas impagas, diferencias por ajuste IPC no aplicado, y penalidades por mora. Devuelve resumen completo.",
      parameters: { type: "object", properties: {}, required: [] },
    },
  },
  {
    type: "function" as const,
    function: {
      name: "consultar_servicios_pendientes",
      description:
        "Devuelve el resumen actual de deuda por servicio como texto plano. NOTA: Para mostrar una tabla visual al usuario, preferí SIEMPRE usar generar_reporte_tabla con tipo=deuda_servicios_resumen en lugar de esta herramienta.",
      parameters: { type: "object", properties: {}, required: [] },
    },
  },
  {
    type: "function" as const,
    function: {
      name: "consultar_detalle_servicio",
      description: "Detalle de un servicio específico: todos sus períodos, montos y estado de pago.",
      parameters: {
        type: "object",
        properties: {
          nombre_servicio: {
            type: "string",
            description: "Nombre parcial o completo del servicio (ej: 'luz', 'gas', 'expensas')",
          },
        },
        required: ["nombre_servicio"],
      },
    },
  },
  {
    type: "function" as const,
    function: {
      name: "obtener_proximo_ajuste_ipc",
      description:
        "Informa cuándo es el próximo ajuste IPC del contrato y cuánto sería el nuevo monto de alquiler.",
      parameters: { type: "object", properties: {}, required: [] },
    },
  },
  {
    type: "function" as const,
    function: {
      name: "registrar_pago_alquiler",
      description: "Registra un pago de alquiler en la base de datos.",
      parameters: {
        type: "object",
        properties: {
          periodo: {
            type: "string",
            description: "Período en formato YYYY-MM (ej: '2026-03')",
          },
          monto: { type: "number", description: "Monto pagado en ARS" },
          fecha_pago: {
            type: "string",
            description: "Fecha del pago en formato YYYY-MM-DD",
          },
          notas: { type: "string", description: "Notas adicionales (opcional)" },
        },
        required: ["periodo", "monto", "fecha_pago"],
      },
    },
  },
  {
    type: "function" as const,
    function: {
      name: "registrar_pago_servicio",
      description: "Registra un pago de servicios en la base de datos. Puede ser global (redistribuido entre deudas pendientes) o específico para un servicio puntual.",
      parameters: {
        type: "object",
        properties: {
          monto: { type: "number", description: "Monto pagado en ARS" },
          fecha_pago: {
            type: "string",
            description: "Fecha del pago en formato YYYY-MM-DD",
          },
          notas: { type: "string", description: "Notas adicionales (opcional)" },
          creado_por: { type: "string", description: "Nombre de quien registró el pago (opcional)" },
          payment_mode: {
            type: "string",
            enum: ["global", "specific"],
            description: "Usar 'global' para pagos generales de servicios o 'specific' para un servicio puntual",
          },
          service_id: {
            type: "number",
            description: "ID del servicio si el pago es específico",
          },
          service_name: {
            type: "string",
            description: "Nombre del servicio si el pago es específico y no se conoce el ID exacto",
          },
          proof_urls: {
            type: "array",
            items: { type: "string" },
            description: "URLs de comprobantes asociados (opcional)",
          },
        },
        required: ["monto", "fecha_pago"],
      },
    },
  },
  {
    type: "function" as const,
    function: {
      name: "exportar_datos_csv",
      description:
        "Genera un archivo CSV (hoja de cálculo) con los datos solicitados y lo envía como documento adjunto por WhatsApp. Usar cuando el usuario pida datos en formato planilla, hoja de cálculo, CSV o Excel.",
      parameters: {
        type: "object",
        properties: {
          tipo: {
            type: "string",
            enum: ["deuda_servicios", "deuda_alquiler", "detalle_servicio", "resumen_completo"],
            description: "Tipo de reporte: deuda_servicios (deuda por servicio), deuda_alquiler (cuotas de alquiler), detalle_servicio (detalle de un servicio), resumen_completo (todo junto)",
          },
          nombre_servicio: {
            type: "string",
            description: "Solo para tipo=detalle_servicio: nombre del servicio",
          },
        },
        required: ["tipo"],
      },
    },
  },
] as const;

// ── Tool executors ────────────────────────────────────────────────────────────

export async function executeTool(name: string, args: Record<string, unknown>): Promise<string> {
  try {
    switch (name) {
      case "generar_reporte_tabla":
        return await generarReporteTabla(args.tipo as ReportType, {
          nombre_servicio: args.nombre_servicio as string | undefined,
          limite: typeof args.limite === "number" ? args.limite : undefined,
        });
      case "consultar_deuda_alquiler":
        return await toolConsultarDeudaAlquiler();
      case "consultar_servicios_pendientes":
        return await toolConsultarServiciosPendientes();
      case "consultar_detalle_servicio":
        return await toolConsultarDetalleServicio(args.nombre_servicio as string);
      case "obtener_proximo_ajuste_ipc":
        return await toolObtenerProximoAjusteIpc();
      case "registrar_pago_alquiler":
        return await toolRegistrarPagoAlquiler(
          args.periodo as string,
          args.monto as number,
          args.fecha_pago as string,
          args.notas as string | undefined,
          args.creado_por as string | undefined,
          args.proof_urls as string[] | undefined
        );
      case "registrar_pago_servicio":
        return await toolRegistrarPagoServicio(
          args.monto as number,
          args.fecha_pago as string,
          args.notas as string | undefined,
          args.creado_por as string | undefined,
          (args.payment_mode as ServicePaymentMode | undefined) ?? "global",
          args.service_id as number | undefined,
          args.service_name as string | undefined,
          args.proof_urls as string[] | undefined
        );
      case "exportar_datos_csv":
        return await toolExportarDatosCsv(
          args.tipo as string,
          args.nombre_servicio as string | undefined
        );
      default:
        return `Tool desconocido: ${name}`;
    }
  } catch (err) {
    console.error(`Error ejecutando tool ${name}:`, err);
    return `Error al ejecutar ${name}: ${err instanceof Error ? err.message : String(err)}`;
  }
}

// ── Rent debt — mirrors dashboard/page.tsx calcularResumen logic ──────────────

async function toolConsultarDeudaAlquiler(): Promise<string> {
  const hoy = new Date().toISOString().slice(0, 10);

  const [contrato, ipcData, acuerdo] = await Promise.all([
    getContratoData(),
    getIpcData(),
    getAcuerdoVerbalFromDb(),
  ]);

  // No contratoId filter — matches web app which queries all pagos (single contract system)
  const pagoRows = await db.select().from(pagos);
  const pagosMap = new Map<string, { monto: number; fechaPago: string | null }>();
  for (const p of pagoRows) {
    pagosMap.set(p.periodo, { monto: p.monto, fechaPago: p.fechaPago ?? null });
  }

  const ajustes = calcularAjustesIpc(ipcData, contrato.calendario, contrato.montoBaseInicial);
  const resumen = calcularResumen(hoy, ajustes, contrato, pagosMap, acuerdo);

  const cuotasImpagas = resumen.cuotas.filter((c) => !c.pagado);
  const lines: string[] = [
    `Consulta al ${hoy}`,
    `Alquiler actual (IPC): $${resumen.alquilerActualIpc.toLocaleString("es-AR")}`,
    `Alquiler acordado: $${resumen.alquilerAcordado?.toLocaleString("es-AR") ?? "N/A"}`,
    ``,
    `Cuotas impagas: ${cuotasImpagas.length}`,
  ];

  for (const c of cuotasImpagas) {
    lines.push(
      `  - ${c.periodo}: esperado $${c.montoEsperadoIpc.toLocaleString("es-AR")} | pagado $${c.montoPagado.toLocaleString("es-AR")} | mora ${c.diasMora} días`
    );
  }

  lines.push(``, `Impaga (meses sin pago): $${resumen.deudaMensualImpaga.toLocaleString("es-AR")}`);
  lines.push(`Diferencia IPC (pagos parciales): $${resumen.diferenciaAjustesIpc.toLocaleString("es-AR")}`);
  lines.push(`Total penalidades: $${resumen.totalPenalidad.toLocaleString("es-AR")}`);
  lines.push(`Penalidad diaria hoy: $${resumen.penalidadDiariaHoy.toLocaleString("es-AR")}`);
  lines.push(
    `DEUDA ALQUILER TOTAL: $${(resumen.deudaMensualImpaga + resumen.diferenciaAjustesIpc + resumen.totalPenalidad).toLocaleString("es-AR")}`
  );

  return lines.join("\n");
}

// ── Services debt — mirrors dashboard/page.tsx deudaPorServicio logic ─────────
// Formula: corresponde = SUM(montoFacturado) * (porcentaje/100), deuda = corresponde - SUM(montoPagado)
// montoPagado is already updated by syncPagosServicios whenever a payment is added

async function toolConsultarServiciosPendientes(): Promise<string> {
  const serviciosData = await db
    .select({ id: servicios.id, nombre: servicios.nombre, porcentaje: servicios.porcentaje })
    .from(servicios)
    .where(eq(servicios.activo, 1));

  const gastosData = await db.select().from(gastosServicios);

  let total = 0;
  const lines: string[] = [];

  for (const svc of serviciosData) {
    const gastos = gastosData.filter((g) => g.servicioId === svc.id);
    const totalPagado = gastos.reduce((s, g) => s + (g.montoPagado ?? 0), 0);
    const totalFacturado = gastos.reduce((s, g) => s + g.montoFacturado, 0);
    const corresponde = Math.round(totalFacturado * (svc.porcentaje / 100) * 100) / 100;
    const deuda = Math.round((corresponde - totalPagado) * 100) / 100;
    if (deuda > 0.01) {
      total += deuda;
      lines.push(`  - ${svc.nombre}: deuda $${deuda.toLocaleString("es-AR")}`);
    }
  }

  if (lines.length === 0) return "No hay servicios con deuda pendiente.";

  return [
    "Deuda de servicios:",
    ...lines,
    ``,
    `Total deuda servicios: $${(Math.round(total * 100) / 100).toLocaleString("es-AR")}`,
  ].join("\n");
}

// ── Service detail — per-period breakdown ────────────────────────────────────

async function toolConsultarDetalleServicio(nombreServicio: string): Promise<string> {
  const serviciosData = await db.select().from(servicios).where(eq(servicios.activo, 1));

  const match = serviciosData.find((s) =>
    s.nombre.toLowerCase().includes(nombreServicio.toLowerCase())
  );
  if (!match) return `No se encontró el servicio "${nombreServicio}".`;

  const gastos = await db
    .select()
    .from(gastosServicios)
    .where(eq(gastosServicios.servicioId, match.id))
    .orderBy(desc(gastosServicios.periodo));

  if (gastos.length === 0) return `No hay gastos registrados para ${match.nombre}.`;

  const pct = match.porcentaje / 100;
  const lines = [`Detalle ${match.nombre} (responsabilidad: ${match.porcentaje}%):`];
  for (const g of gastos) {
    const corresponde = Math.round(g.montoFacturado * pct * 100) / 100;
    const pagado = g.montoPagado ?? 0;
    const pendiente = Math.round((corresponde - pagado) * 100) / 100;
    const estado =
      pendiente <= 0
        ? `al día`
        : `PENDIENTE $${pendiente.toLocaleString("es-AR")} (pagado $${pagado.toLocaleString("es-AR")} / corresponde $${corresponde.toLocaleString("es-AR")})`;
    lines.push(`  ${g.periodo}: facturado $${g.montoFacturado.toLocaleString("es-AR")} — ${estado}`);
  }
  return lines.join("\n");
}

// ── Next IPC adjustment ───────────────────────────────────────────────────────

async function toolObtenerProximoAjusteIpc(): Promise<string> {
  const hoy = new Date().toISOString().slice(0, 7); // YYYY-MM

  const [contrato, ipcData] = await Promise.all([getContratoData(), getIpcData()]);

  const ajustes = calcularAjustesIpc(ipcData, contrato.calendario, contrato.montoBaseInicial);
  const ultimoAjuste = ajustes[ajustes.length - 1];
  const montoActual = ultimoAjuste?.montoNuevo ?? contrato.montoBaseInicial;

  const proximos = contrato.calendario.filter((c) => c.fechaAjuste > hoy);
  if (proximos.length === 0) return "No hay ajustes IPC futuros en el calendario del contrato.";

  const proximo = proximos[0];
  const tieneIndices = ipcData[proximo.mesIndiceInicio] && ipcData[proximo.mesIndiceFin];

  if (tieneIndices) {
    const coef = ipcData[proximo.mesIndiceFin] / ipcData[proximo.mesIndiceInicio];
    const montoNuevo = Math.round(montoActual * coef);
    const variacion = ((coef - 1) * 100).toFixed(2);
    return [
      `Próximo ajuste IPC: ${proximo.fechaAjuste}`,
      `Índices: ${proximo.mesIndiceInicio} → ${proximo.mesIndiceFin}`,
      `Variación: +${variacion}%`,
      `Monto actual: $${montoActual.toLocaleString("es-AR")}`,
      `Monto estimado nuevo: $${montoNuevo.toLocaleString("es-AR")}`,
    ].join("\n");
  } else {
    return [
      `Próximo ajuste IPC: ${proximo.fechaAjuste}`,
      `Índices necesarios: ${proximo.mesIndiceInicio} → ${proximo.mesIndiceFin}`,
      `Monto actual: $${montoActual.toLocaleString("es-AR")}`,
      `(Índices INDEC pendientes de carga para calcular monto exacto)`,
    ].join("\n");
  }
}

// ── Register rent payment ─────────────────────────────────────────────────────

async function toolRegistrarPagoAlquiler(
  periodo: string,
  monto: number,
  fechaPago: string,
  notas?: string,
  creadoPor?: string,
  proofUrls?: string[]
): Promise<string> {
  const storedNotes = buildServicePaymentNotes(notas?.trim() ?? "", {
    proofUrls,
    uploader: creadoPor,
  });

  const [created] = await db
    .insert(pagos)
    .values({
      contratoId: 1, // all existing pagos use contratoId=1
      periodo,
      monto,
      fechaPago,
      notas: storedNotes || null,
      creadoPor: creadoPor ?? "bot-whatsapp",
    })
    .returning({ id: pagos.id });

  return `Pago de alquiler registrado: $${monto.toLocaleString("es-AR")} para período ${periodo} (ID: ${created.id})`;
}

// ── Register service payment — mirrors POST /api/admin/pagos-servicios ────────
// Inserts into pagos_servicios then runs syncPagosServicios to redistribute

async function toolRegistrarPagoServicio(
  monto: number,
  fechaPago: string,
  notas?: string,
  creadoPor?: string,
  paymentMode: ServicePaymentMode = "global",
  serviceId?: number,
  serviceName?: string,
  proofUrls?: string[]
): Promise<string> {
  const visibleNotes = [
    paymentMode === "specific" && serviceName ? `Pago especifico: ${serviceName}.` : null,
    notas?.trim() || null,
  ].filter((value): value is string => Boolean(value)).join(" ");

  const storedNotes = buildServicePaymentNotes(visibleNotes, {
    paymentMode,
    serviceId,
    serviceName,
    proofUrls,
    uploader: creadoPor,
  });

  const [created] = await db
    .insert(pagosServicios)
    .values({
      fecha: fechaPago,
      monto,
      notas: storedNotes || null,
      creadoPor: creadoPor ?? "bot-whatsapp",
    })
    .returning({ id: pagosServicios.id });

  await syncPagosServicios();

  return `Pago de servicio registrado: $${monto.toLocaleString("es-AR")} fecha ${fechaPago} (ID: ${created.id})`;
}

// ── Active services helper ────────────────────────────────────────────────────

export async function getActiveServices() {
  return db
    .select({ id: servicios.id, nombre: servicios.nombre })
    .from(servicios)
    .where(eq(servicios.activo, 1));
}

// ── CSV Export ──────────────────────────────────────────────────────────────

function csvRow(cells: (string | number)[]): string {
  return cells.map(c => {
    const s = String(c);
    return s.includes(",") || s.includes('"') || s.includes("\n")
      ? `"${s.replace(/"/g, '""')}"`
      : s;
  }).join(",");
}

function sortByPeriodoDesc<T extends { periodo: string }>(rows: T[]): T[] {
  return [...rows].sort((a, b) => b.periodo.localeCompare(a.periodo));
}

async function toolExportarDatosCsv(tipo: string, nombreServicio?: string): Promise<string> {
  const hoy = new Date().toISOString().slice(0, 10);
  let csv = "";
  let filename = "reporte";

  if (tipo === "deuda_servicios" || tipo === "resumen_completo") {
    const serviciosData = await db
      .select({ id: servicios.id, nombre: servicios.nombre, porcentaje: servicios.porcentaje })
      .from(servicios)
      .where(eq(servicios.activo, 1));
    const gastosData = await db.select().from(gastosServicios);
    const onlyPendingDebt = tipo === "deuda_servicios";
    let hasRows = false;

    csv += csvRow(["Servicio", "% a cargo", "Periodo", "Monto facturado", "Corresponde inquilino", "Monto pagado", "Deuda pendiente"]) + "\n";

    for (const svc of serviciosData) {
      const gastos = sortByPeriodoDesc(gastosData.filter(g => g.servicioId === svc.id));
      const pct = svc.porcentaje / 100;
      for (const g of gastos) {
        const corresponde = Math.round(g.montoFacturado * pct * 100) / 100;
        const pagado = g.montoPagado ?? 0;
        const deuda = Math.round((corresponde - pagado) * 100) / 100;
        if (onlyPendingDebt && deuda <= 0.01) continue;

        hasRows = true;
        csv += csvRow([svc.nombre, `${svc.porcentaje}%`, g.periodo, g.montoFacturado, corresponde, pagado, deuda]) + "\n";
      }
    }

    if (!hasRows && onlyPendingDebt) {
      csv += csvRow(["Sin deuda pendiente", "", "", "", "", "", ""]) + "\n";
    }

    filename = `deuda_servicios_${hoy}`;
  }

  if (tipo === "deuda_alquiler" || tipo === "resumen_completo") {
    const [contrato, ipcData, acuerdo] = await Promise.all([
      getContratoData(), getIpcData(), getAcuerdoVerbalFromDb(),
    ]);
    const pagoRows = await db.select().from(pagos);
    const pagosMap = new Map<string, { monto: number; fechaPago: string | null }>();
    for (const p of pagoRows) {
      pagosMap.set(p.periodo, { monto: p.monto, fechaPago: p.fechaPago ?? null });
    }
    const ajustes = calcularAjustesIpc(ipcData, contrato.calendario, contrato.montoBaseInicial);
    const resumen = calcularResumen(hoy, ajustes, contrato, pagosMap, acuerdo);

    if (tipo === "resumen_completo") csv += "\n";
    csv += csvRow(["Periodo", "Monto esperado IPC", "Monto pagado", "Diferencia", "Dias mora", "Penalidad", "Estado"]) + "\n";

    for (const c of resumen.cuotas) {
      const diff = c.montoEsperadoIpc - c.montoPagado;
      csv += csvRow([
        c.periodo,
        c.montoEsperadoIpc,
        c.montoPagado,
        diff,
        c.diasMora,
        c.penalidad,
        c.pagado ? "Pagado" : "Pendiente",
      ]) + "\n";
    }

    csv += csvRow(["TOTAL", "", "", resumen.deudaMensualImpaga + resumen.diferenciaAjustesIpc, "", resumen.totalPenalidad, ""]) + "\n";
    if (tipo === "deuda_alquiler") filename = `deuda_alquiler_${hoy}`;
    else filename = `resumen_completo_${hoy}`;
  }

  if (tipo === "detalle_servicio") {
    if (!nombreServicio) return "Falta el nombre del servicio.";
    const serviciosData = await db.select().from(servicios).where(eq(servicios.activo, 1));
    const match = serviciosData.find(s => s.nombre.toLowerCase().includes(nombreServicio.toLowerCase()));
    if (!match) return `No se encontró el servicio "${nombreServicio}".`;

    const gastos = await db.select().from(gastosServicios)
      .where(eq(gastosServicios.servicioId, match.id))
      .orderBy(desc(gastosServicios.periodo));

    const pct = match.porcentaje / 100;
    csv += csvRow(["Periodo", "Monto facturado", `Corresponde (${match.porcentaje}%)`, "Monto pagado", "Deuda", "Estado"]) + "\n";
    for (const g of gastos) {
      const corresponde = Math.round(g.montoFacturado * pct * 100) / 100;
      const pagado = g.montoPagado ?? 0;
      const deuda = Math.round((corresponde - pagado) * 100) / 100;
      csv += csvRow([g.periodo, g.montoFacturado, corresponde, pagado, deuda, deuda <= 0 ? "Al dia" : "Pendiente"]) + "\n";
    }
    filename = `detalle_${match.nombre.toLowerCase().replace(/\s+/g, "_")}_${hoy}`;
  }

  if (!csv) return "Tipo de reporte no reconocido.";

  // Upload CSV to Oracle VM and return URL
  const base64 = Buffer.from(`\uFEFF${csv}`, "utf-8").toString("base64");
  const url = await uploadMedia(base64, "text/csv", `${filename}.csv`);

  if (!url) return `[CSV_ERROR] No se pudo subir el archivo.`;

  return `[CSV_FILE:${url}:${filename}.csv] Archivo generado con exito.`;
}
