// Lógica financiera para calculadora de PREVENTA (esquema en porcentajes)

const fmtMXN = (n, opts = {}) => {
  if (n === null || n === undefined || isNaN(n)) return "—";
  const { decimals = 0, compact = false } = opts;
  if (compact && Math.abs(n) >= 1_000_000) {
    return "$" + (n / 1_000_000).toLocaleString("es-MX", { maximumFractionDigits: 2 }) + " M";
  }
  if (compact && Math.abs(n) >= 1_000) {
    return "$" + (n / 1_000).toLocaleString("es-MX", { maximumFractionDigits: 1 }) + " K";
  }
  return "$" + n.toLocaleString("es-MX", { minimumFractionDigits: decimals, maximumFractionDigits: decimals });
};

const fmtPct = (n, decimals = 2) => {
  if (n === null || n === undefined || isNaN(n) || !isFinite(n)) return "—";
  return n.toLocaleString("es-MX", { minimumFractionDigits: decimals, maximumFractionDigits: decimals }) + "%";
};

const fmtNum = (n, decimals = 0) => {
  if (n === null || n === undefined || isNaN(n)) return "—";
  return n.toLocaleString("es-MX", { minimumFractionDigits: decimals, maximumFractionDigits: decimals });
};

// Pago hipotecario sistema francés
function pagoHip(monto, tasaAnual, plazoAnios) {
  if (monto <= 0 || plazoAnios <= 0) return 0;
  const n = plazoAnios * 12;
  const r = tasaAnual / 100 / 12;
  if (r === 0) return monto / n;
  return (monto * r * Math.pow(1 + r, n)) / (Math.pow(1 + r, n) - 1);
}

function saldoCreditoEnAnio(monto, tasaAnual, plazoAnios, anio) {
  if (monto <= 0 || anio <= 0) return monto;
  if (anio >= plazoAnios) return 0;
  const r = tasaAnual / 100 / 12;
  const n = plazoAnios * 12;
  const m = anio * 12;
  if (r === 0) return monto * (1 - m / n);
  const pago = pagoHip(monto, tasaAnual, plazoAnios);
  // Saldo = pago * ((1 - (1+r)^(-(n-m))) / r)
  return pago * (1 - Math.pow(1 + r, -(n - m))) / r;
}

// Cálculo principal del esquema de pagos
function calcular(i) {
  const precio = +i.precio || 0;
  const engPct = +i.engPct || 0;
  const mensPct = +i.mensPct || 0;
  const entregaPct = +i.entregaPct || 0;
  const escrituraPct = +i.escrituraPct || 0;
  const numMensualidades = Math.max(1, Math.floor(+i.numMensualidades || 1));

  const sumaEsquema = engPct + mensPct + entregaPct;
  const balanceadoOk = Math.abs(sumaEsquema - 100) < 0.01;

  const enganche = precio * (engPct / 100);
  const totalMensualidades = precio * (mensPct / 100);
  const mensualidad = numMensualidades > 0 ? totalMensualidades / numMensualidades : 0;
  const contraEntrega = precio * (entregaPct / 100);
  const escrituracion = precio * (escrituraPct / 100);

  const totalAPagar = enganche + totalMensualidades + contraEntrega + escrituracion;
  const totalSinEscritura = enganche + totalMensualidades + contraEntrega;

  // Cronograma
  const cronograma = [];
  cronograma.push({ mes: 0, concepto: "Firma de contrato", detalle: "Enganche", pago: enganche, porcentaje: engPct });
  for (let m = 1; m <= numMensualidades; m++) {
    cronograma.push({
      mes: m, concepto: `Mensualidad ${m} de ${numMensualidades}`,
      detalle: "Pago durante construcción", pago: mensualidad,
      porcentaje: numMensualidades > 0 ? mensPct / numMensualidades : 0,
    });
  }
  cronograma.push({ mes: numMensualidades + 1, concepto: "Entrega", detalle: "Saldo contra entrega", pago: contraEntrega, porcentaje: entregaPct });
  cronograma.push({ mes: numMensualidades + 1, concepto: "Escrituración", detalle: "Gastos notariales (no cuenta para esquema)", pago: escrituracion, porcentaje: escrituraPct, extra: true });

  let acum = 0;
  cronograma.forEach((p) => { acum += p.pago; p.acumulado = acum; p.porcentajeAcum = precio > 0 ? (acum / precio) * 100 : 0; });

  return {
    precio, enganche, totalMensualidades, mensualidad, contraEntrega, escrituracion,
    totalAPagar, totalSinEscritura,
    sumaEsquema, balanceadoOk,
    numMensualidades, mensPct, engPct, entregaPct, escrituraPct,
    cronograma,
  };
}

// Cálculo del ROI post-entrega
function calcularROI(precio, contraEntrega, escrituracion, totalAPagar, roiInputs) {
  const tarifaProm = +roiInputs.tarifaProm || 0;
  const ocupacion = +roiInputs.ocupacion || 0;
  const comisionPct = +roiInputs.comisionPct || 0;
  const adminPct = +roiInputs.adminPct || 0;
  const gastosFijos = +roiInputs.gastosFijos || 0;
  const plusvalia = +roiInputs.plusvalia || 0;
  const inflacionRenta = +roiInputs.inflacionRenta || 0;
  const horizonte = Math.max(1, Math.floor(+roiInputs.horizonte || 10));

  // Crédito sobre el saldo contra entrega
  const usarCredito = !!roiInputs.usarCredito;
  const tasaCredito = +roiInputs.tasaCredito || 0;
  const plazoCredito = Math.max(1, Math.floor(+roiInputs.plazoCredito || 20));
  const montoCredito = usarCredito ? contraEntrega : 0;
  const pagoMensualHip = usarCredito ? pagoHip(montoCredito, tasaCredito, plazoCredito) : 0;

  // Inversión propia: lo que el cliente pone de su bolsa.
  // Si usa crédito, el banco aporta el contra entrega; el cliente sólo aporta enganche + mensualidades + escrituración.
  const inversionPropia = usarCredito
    ? totalAPagar - contraEntrega
    : totalAPagar;

  // Ingreso mensual base
  const noches = 30 * (ocupacion / 100);
  const ingresoBruto = tarifaProm * noches;
  const comision = ingresoBruto * (comisionPct / 100);
  const admin = ingresoBruto * (adminPct / 100);
  const ingresoNetoOp = ingresoBruto - comision - admin - gastosFijos;
  const flujoNetoMensual = ingresoNetoOp - pagoMensualHip;
  const flujoNetoAnual = flujoNetoMensual * 12;

  // Indicador "se paga solo": % de la hipoteca cubierta por la renta neta operativa
  const cubrePctHip = pagoMensualHip > 0 ? (ingresoNetoOp / pagoMensualHip) * 100 : null;

  // Proyección año a año
  const proyeccion = [];
  let valor = precio;
  let renta = ingresoNetoOp;
  let acumFlujo = 0;
  for (let y = 1; y <= horizonte; y++) {
    const flujoOpAnual = renta * 12;
    const pagoHipAnual = pagoMensualHip * 12;
    const flujoAnioY = flujoOpAnual - pagoHipAnual;
    acumFlujo += flujoAnioY;

    valor = valor * (1 + plusvalia / 100);
    const saldo = usarCredito ? saldoCreditoEnAnio(montoCredito, tasaCredito, plazoCredito, y) : 0;
    const equity = valor - saldo;

    proyeccion.push({
      anio: y,
      ingresoOp: flujoOpAnual,
      pagoHip: pagoHipAnual,
      flujoNeto: flujoAnioY,
      flujoAcumulado: acumFlujo,
      valor,
      saldo,
      equity,
      plusvaliaAcum: valor - precio,
    });

    renta = renta * (1 + inflacionRenta / 100);
  }

  const ultimo = proyeccion[proyeccion.length - 1];
  const plusvaliaTotal = ultimo.valor - precio;
  const flujoAcumTotal = ultimo.flujoAcumulado;
  const equityFinal = ultimo.equity;
  // Ganancia total = equity al final + flujo acumulado − inversión propia
  const gananciaTotal = equityFinal + flujoAcumTotal - inversionPropia;
  const roiTotal = inversionPropia > 0 ? (gananciaTotal / inversionPropia) * 100 : 0;
  const roiAnualizado = inversionPropia > 0 && horizonte > 0
    ? (Math.pow(1 + roiTotal / 100, 1 / horizonte) - 1) * 100
    : 0;
  const capRate = precio > 0 ? ((ingresoNetoOp * 12) / precio) * 100 : 0;
  const cashOnCash = inversionPropia > 0 ? (flujoNetoAnual / inversionPropia) * 100 : 0;

  return {
    tarifaProm, ocupacion, noches,
    ingresoBruto, comision, admin, gastosFijos,
    ingresoNetoOp, pagoMensualHip,
    flujoNetoMensual, flujoNetoAnual,
    cubrePctHip,
    usarCredito, montoCredito, tasaCredito, plazoCredito,
    inversionPropia,
    proyeccion, plusvaliaTotal, flujoAcumTotal, equityFinal,
    gananciaTotal, roiTotal, roiAnualizado, capRate, cashOnCash,
    horizonte, plusvalia, inflacionRenta,
    // Sin apalancamiento: como si el cliente pagara todo de su bolsa
    ...(function() {
      const inversionSinApal = totalAPagar; // todo el desembolso
      const flujoOpAnualSinHip = ingresoNetoOp * 12; // sin hipoteca
      // proyeccion sin hipoteca
      let acum = 0; let renta = ingresoNetoOp;
      for (let y = 1; y <= horizonte; y++) {
        acum += renta * 12;
        renta = renta * (1 + inflacionRenta / 100);
      }
      const valorFinal = precio * Math.pow(1 + plusvalia / 100, horizonte);
      const gananciaSinApal = (valorFinal - precio) + acum;
      const roiSinApal = inversionSinApal > 0 ? (gananciaSinApal / inversionSinApal) * 100 : 0;
      const roiAnualSinApal = inversionSinApal > 0 && horizonte > 0
        ? (Math.pow(1 + roiSinApal / 100, 1 / horizonte) - 1) * 100 : 0;
      const cashOnCashSinApal = inversionSinApal > 0 ? (flujoOpAnualSinHip / inversionSinApal) * 100 : 0;
      return { inversionSinApal, roiSinApal, roiAnualSinApal, cashOnCashSinApal, flujoMensualSinApal: ingresoNetoOp };
    })(),
  };
}

window.CalcLogic = { calcular, calcularROI, fmtMXN, fmtPct, fmtNum };

