import { AjusteCalculado, obtenerAlquilerEsperado, generarPeriodos } from "./ipc";

export interface CuotaMensual {
  periodo: string;
  fechaVencimiento: string;
  montoEsperadoIpc: number;
  montoAcordado: number | null;
  montoPagado: number;
  pagado: boolean;
  diferenciaIpc: number;
  diferenciaAcordado: number | null;
  diasMora: number;
  penalidad: number;
}

export interface ResumenPenalidades {
  cuotas: CuotaMensual[];
  totalDiferenciaIpc: number;
  totalDiferenciaAcordado: number;
  totalPenalidad: number;
  penalidadDiariaHoy: number;
  alquilerActualIpc: number;
  alquilerAcordado: number | null;
  deudaMensualImpaga: number;
  diferenciaAjustesIpc: number;
}

export interface ContratoCalculo {
  montoBaseInicial: number;
  diaVencimiento: number;
  penalDiario: number;
  fechaInicio: string;
}

function diasEntreFechas(desde: string, hasta: string): number {
  const d1 = new Date(desde + "T00:00:00");
  const d2 = new Date(hasta + "T00:00:00");
  return Math.max(0, Math.floor((d2.getTime() - d1.getTime()) / (1000 * 60 * 60 * 24)));
}

export function calcularResumen(
  hoy: string,
  ajustes: AjusteCalculado[],
  contrato: ContratoCalculo,
  pagosRealizados?: Map<string, { monto: number; fechaPago: string | null }>,
  acuerdo?: { monto: number; desdePeriodo: string }
): ResumenPenalidades {
  const periodoActual = hoy.substring(0, 7);
  const periodos = generarPeriodos(periodoActual, contrato.fechaInicio);

  const cuotas: CuotaMensual[] = [];
  let totalDiferenciaIpc = 0;
  let totalDiferenciaAcordado = 0;
  let totalPenalidad = 0;
  let deudaMensualImpaga = 0;
  let diferenciaAjustesIpc = 0;

  for (const periodo of periodos) {
    const [anio, mes] = periodo.split("-").map(Number);
    const ultimoDia = new Date(anio, mes, 0).getDate();
    const diaVenc = Math.min(contrato.diaVencimiento, ultimoDia);
    const fechaVencimiento = `${periodo}-${String(diaVenc).padStart(2, "0")}`;

    const montoEsperadoIpc = obtenerAlquilerEsperado(periodo, ajustes, contrato.montoBaseInicial);

    const montoAcordado = acuerdo && periodo >= acuerdo.desdePeriodo ? acuerdo.monto : null;

    const pagoInfo = pagosRealizados?.get(periodo);
    let montoPagado: number;
    let pagado: boolean;

    if (pagoInfo) {
      montoPagado = pagoInfo.monto;
      pagado = pagoInfo.fechaPago !== null;
    } else if (hoy >= fechaVencimiento) {
      montoPagado = 0;
      pagado = false;
    } else {
      continue;
    }

    const diferenciaIpc = montoEsperadoIpc - montoPagado;
    const diferenciaAcordado = montoAcordado !== null ? montoAcordado - montoPagado : null;

    let diasMora = 0;
    let penalidad = 0;
    if (pagado) {
      // Payment made: penalty from vencimiento to payment date, based on full IPC amount
      diasMora = diasEntreFechas(fechaVencimiento, pagoInfo!.fechaPago!);
    } else if (hoy > fechaVencimiento) {
      // Not paid yet: penalty from vencimiento to today, based on full IPC amount
      diasMora = diasEntreFechas(fechaVencimiento, hoy);
    }
    if (diasMora > 0) {
      penalidad = Math.round(montoEsperadoIpc * contrato.penalDiario * diasMora);
    }

    totalDiferenciaIpc += Math.max(0, diferenciaIpc);
    if (diferenciaAcordado !== null) totalDiferenciaAcordado += Math.max(0, diferenciaAcordado);
    totalPenalidad += penalidad;

    if (montoPagado === 0) {
      deudaMensualImpaga += montoEsperadoIpc;
    } else if (diferenciaIpc > 0) {
      diferenciaAjustesIpc += diferenciaIpc;
    }

    cuotas.push({
      periodo,
      fechaVencimiento,
      montoEsperadoIpc,
      montoAcordado,
      montoPagado,
      pagado,
      diferenciaIpc: Math.max(0, diferenciaIpc),
      diferenciaAcordado: diferenciaAcordado !== null ? Math.max(0, diferenciaAcordado) : null,
      diasMora,
      penalidad,
    });
  }

  const penalidadDiariaHoy = cuotas.reduce((sum, c) => {
    if (!c.pagado && c.diasMora > 0) {
      return sum + Math.round(c.montoEsperadoIpc * contrato.penalDiario);
    }
    return sum;
  }, 0);

  const alquilerActualIpc = ajustes.length > 0
    ? ajustes[ajustes.length - 1].montoNuevo
    : contrato.montoBaseInicial;

  return {
    cuotas,
    totalDiferenciaIpc,
    totalDiferenciaAcordado,
    totalPenalidad,
    penalidadDiariaHoy,
    alquilerActualIpc,
    alquilerAcordado: acuerdo?.monto ?? null,
    deudaMensualImpaga,
    diferenciaAjustesIpc,
  };
}
