/* Lenny Niemeyer Investment Model — interactive web engine
   Mirrors LN_Investment_Model_Folclore.xlsx structure & assumptions. */

const LN_MODEL_FILE = "dataroom/10-financial-models/LN_Investment_Model_Folclore.xlsx";

const LN_DEFAULTS = {
  entryMultiple: 8,
  ebitda2024: 17.8,
  stake: 0.25,
  fxEntry: 5.8,
  fxDepreciation: 0.035,
  intlBudget: 20,
  holdYears: 8,
};

const LN_SCENARIOS = {
  bear: {
    label: "Bear",
    revenueCagr: 0.05,
    marginY1: 0.12,
    marginY8: 0.13,
    dividendPayout: 0.30,
    brazilExitMultiple: 7,
    intlExitMultiple: 0.7,
    intlRevenueExit: 7.5,
    intlEbitdaExit: 0.75,
  },
  base: {
    label: "Base",
    revenueCagr: 0.10,
    marginY1: 0.14,
    marginY8: 0.17,
    dividendPayout: 0.40,
    brazilExitMultiple: 12,
    intlExitMultiple: 12,
    intlRevenueExit: 30,
    intlEbitdaExit: 5.4,
  },
  bull: {
    label: "Bull",
    revenueCagr: 0.15,
    marginY1: 0.15,
    marginY8: 0.20,
    dividendPayout: 0.50,
    brazilExitMultiple: 18,
    intlExitMultiple: 18,
    intlRevenueExit: 55,
    intlEbitdaExit: 13.75,
  },
};

/* Sensitivity tables from Excel — Base case MOIC */
const LN_SENSITIVITY_MOIC = {
  cagrs: [0.05, 0.08, 0.10, 0.12, 0.15, 0.18, 0.20],
  multiples: [6, 8, 10, 12, 14, 16, 18],
  grid: [
    [2.68, 2.75, 2.81, 3.08, 3.34, 3.60, 3.87],
    [2.73, 2.81, 3.02, 3.35, 3.68, 4.01, 4.34],
    [2.77, 2.87, 3.10, 3.49, 3.87, 4.25, 4.64],
    [2.81, 2.92, 3.37, 3.81, 4.25, 4.69, 5.14],
    [2.89, 3.03, 3.58, 4.12, 4.67, 5.21, 5.76],
    [2.99, 3.41, 4.08, 4.75, 5.42, 6.09, 6.76],
    [3.06, 3.83, 4.59, 5.36, 6.13, 6.90, 7.67],
  ],
};

const LN_HOLD_SENSITIVITY = [
  { years: 5, moic: 1.78, irr: 12.25 },
  { years: 6, moic: 2.42, irr: 15.85 },
  { years: 7, moic: 3.08, irr: 17.45 },
  { years: 8, moic: 3.71, irr: 17.81 },
  { years: 9, moic: 4.19, irr: 17.25 },
  { years: 10, moic: 4.70, irr: 16.73 },
];

function strikeMultipleFromCagr(cagr) {
  if (cagr < 0.08) return 10;
  if (cagr < 0.12) return 9;
  if (cagr < 0.16) return 8;
  if (cagr < 0.20) return 7;
  return 6;
}

function projectBrazil(assumptions, scenario, years) {
  const gross2024 = 154.1;
  const netRatio = 0.83;
  const taxRate = 0.34;
  const rows = [];
  let gross = gross2024;
  let fx = assumptions.fxEntry;

  for (let y = 0; y <= years; y++) {
    if (y > 0) {
      gross *= (1 + scenario.revenueCagr);
      fx *= (1 + assumptions.fxDepreciation);
    }
    const net = gross * netRatio;
    const margin = scenario.marginY1 + (scenario.marginY8 - scenario.marginY1) * (y / years);
    const ebitda = net * margin;
    const ebitdaUsd = ebitda / fx;
    const netIncome = ebitda * (1 - taxRate) * 0.65;
    const divUsd = y > 0 ? (netIncome * scenario.dividendPayout * assumptions.stake) / fx : 0;
    rows.push({
      year: 2024 + y,
      gross, net, margin, ebitda, fx, ebitdaUsd, divUsd,
    });
  }
  return rows;
}

function computeLNReturns(assumptions, scenarioKey, overrides) {
  const scenario = { ...LN_SCENARIOS[scenarioKey], ...overrides };
  const years = assumptions.holdYears;

  const entryEv = assumptions.entryMultiple * assumptions.ebitda2024;
  const costStakeBrl = entryEv * assumptions.stake;
  const costStakeUsd = costStakeBrl / assumptions.fxEntry;
  const totalInvestment = costStakeUsd + assumptions.intlBudget;

  const projection = projectBrazil(assumptions, scenario, years);
  const entryEbitda = projection[1]?.ebitda || assumptions.ebitda2024;
  const exitRow = projection[projection.length - 1];
  const exitEbitda = exitRow.ebitda;
  const fxExit = exitRow.fx;

  const ebitdaCagr = entryEbitda > 0
    ? Math.pow(exitEbitda / entryEbitda, 1 / years) - 1
    : 0;

  const strikeMult = strikeMultipleFromCagr(ebitdaCagr);
  const exitEv = exitEbitda * scenario.brazilExitMultiple;
  const strikeEv = exitEbitda * strikeMult;

  const stakeValueUsd = (exitEv * assumptions.stake) / fxExit;
  const optionValueBrl = Math.max(0, exitEv - strikeEv) * 0.75;
  const optionValueUsd = optionValueBrl / fxExit;
  const brazilProceeds = stakeValueUsd + optionValueUsd;

  const intlProceeds = scenario.intlEbitdaExit * scenario.intlExitMultiple;
  const cumulativeDivs = projection.slice(1).reduce((s, r) => s + r.divUsd, 0);

  const totalExit = brazilProceeds + intlProceeds;
  const totalWithDivs = totalExit + cumulativeDivs;
  const moic = totalExit / totalInvestment;
  const moicWithDivs = totalWithDivs / totalInvestment;

  const irr = Math.pow(moic, 1 / years) - 1;
  const irrWithDivs = Math.pow(moicWithDivs, 1 / years) - 1;

  return {
    scenario: scenario.label,
    totalInvestment: totalInvestment.toFixed(1),
    costStakeUsd: costStakeUsd.toFixed(1),
    entryEv: entryEv.toFixed(1),
    exitEbitda: exitEbitda.toFixed(1),
    exitEbitdaUsd: exitRow.ebitdaUsd.toFixed(1),
    ebitdaCagr: (ebitdaCagr * 100).toFixed(1) + "%",
    strikeMult,
    exitMultiple: scenario.brazilExitMultiple,
    exitEv: exitEv.toFixed(1),
    brazilProceeds: brazilProceeds.toFixed(1),
    optionValueUsd: optionValueUsd.toFixed(1),
    intlProceeds: intlProceeds.toFixed(1),
    cumulativeDivs: cumulativeDivs.toFixed(1),
    totalExit: totalExit.toFixed(1),
    totalWithDivs: totalWithDivs.toFixed(1),
    moic: moic.toFixed(2) + "x",
    moicWithDivs: moicWithDivs.toFixed(2) + "x",
    irr: (irr * 100).toFixed(1) + "%",
    irrWithDivs: (irrWithDivs * 100).toFixed(1) + "%",
    projection,
    fxExit: fxExit.toFixed(2),
  };
}

Object.assign(window, {
  LN_MODEL_FILE, LN_DEFAULTS, LN_SCENARIOS, LN_SENSITIVITY_MOIC,
  LN_HOLD_SENSITIVITY, computeLNReturns, strikeMultipleFromCagr, projectBrazil,
});
