// Financial Ratios page — 60 KPIs across 6 categories
// Every ratio is calculated from the live data layer with the formula explicit.

const { useState: useStateRat, useMemo: useMemoRat } = React;

// ─── Period helper: how many months & label for the selected period ─
window.PerduraPeriod = {
  months(p) {
    if (p === "MTD") return 1;
    if (p === "QTD") return 3;
    if (p === "YTD") return 5;   // through May = 5 months YTD in our mock
    if (p === "TTM") return 12;
    return 1;
  },
  label(p) {
    if (p === "MTD") return "May 2026 (month to date)";
    if (p === "QTD") return "Q2 2026 (quarter to date)";
    if (p === "YTD") return "FY26 year to date";
    if (p === "TTM") return "Trailing 12 months";
    return p;
  },
};

// ─── Data source: derive every input the formulas need from PerduraData ─
function buildRatioInputs(data, period = "TTM") {
  const { pl, cash, aging, products } = data;
  const i = pl.revenue.length - 1;
  const N = window.PerduraPeriod.months(period);
  const slice = (arr) => arr.slice(Math.max(0, arr.length - N));

  // Period-scoped flow figures
  const revenue = slice(pl.revenue).reduce((s, v) => s + v, 0);
  const cogs    = slice(pl.cogs).reduce((s, v) => s + v, 0);
  const grossProfit = slice(pl.gp).reduce((s, v) => s + v, 0);
  const opex    = slice(pl.opex).reduce((s, v) => s + v, 0);
  const ebitda  = slice(pl.ebitda).reduce((s, v) => s + v, 0);

  // Annualization factor for ratios that need annualized denominators (DSO etc)
  const annualMultiplier = 12 / N;
  const annualSales = revenue * annualMultiplier;
  const annualCOGS  = cogs * annualMultiplier;

  // Synthesized balance-sheet items (typical SMB profile)
  const currentMonthRev = pl.revenue[i];
  const avgAR = aging.ar.buckets.reduce((s, b) => s + b.value, 0); // open AR
  const overdueAR = aging.ar.buckets.slice(1).reduce((s, b) => s + b.value, 0);
  const avgAP = aging.ap.buckets.reduce((s, b) => s + b.value, 0);

  const inventoryValue = (products || []).reduce((s, p) => s + (p.stock * p.cost), 0) || 412000;
  const avgInventory = inventoryValue;
  const beginInv = inventoryValue * 0.93;
  const endInv   = inventoryValue;

  const cashOnHand = cash.startCash;
  const currentAssets = cashOnHand + avgAR + inventoryValue + 84000; // + other CA
  const currentLiabilities = avgAP + 184000; // + accruals
  const fixedAssets = 1240000;
  const totalAssets = currentAssets + fixedAssets;
  const totalDebt = 936000;
  const totalLiabilities = currentLiabilities + 412000; // + long-term debt
  const shareholdersEquity = totalAssets - totalLiabilities;
  const netIncome = ebitda - 184000 - 78000; // - D&A - tax
  const ebit = ebitda - 184000;
  const interestExpense = 72000;
  const sharesOutstanding = 100000;
  const marketPricePerShare = 28.40;
  const dividendPerShare = 0.84;

  // Operating drivers
  const dailyExpenses = (annualCOGS + opex * annualMultiplier) / 365;
  const cashSpent = Math.abs(cash.burnPerDay) * 30 + (annualCOGS / 12) + (opex * annualMultiplier / 12);
  const cashReceived = annualSales / 12;

  // Customer / SaaS inputs
  const customers = 218;
  const newCustomers = 31;
  const newCustomersPrior = 24;
  const churnedCustomers = 6;
  const cacSpend = 196000;
  const arpu = revenue / customers / 12; // monthly
  const ltv = arpu * 36; // 3-year tenure
  const mrr = 348000;
  const newMRR = 24000;
  const expansionMRR = 14000;
  const churnedMRR = 8600;
  const contractionMRR = 5400;

  // Inventory deep
  const scrapExpenses = (products || []).reduce((s, p) => s + (p.cost * 0.014), 0) || 14200;
  const undeliverableOrders = 4;
  const totalOrders = 278;
  const unitsReceived = (products || []).reduce((s, p) => s + (p.stock * 1.2), 0) || 4800;
  const unitsSold = (products || []).reduce((s, p) => s + p.stock, 0) * 0.84 || 4032;
  const deadStock = (products || []).filter(p => p.velocity === "dead").reduce((s, p) => s + (p.stock * p.cost), 0) || 84000;
  const availableStock = inventoryValue;
  const physicalInv = endInv * 0.989;

  return {
    revenue, cogs, grossProfit, opex, ebitda, netIncome, ebit, currentMonthRev,
    avgAR, overdueAR, avgAP, inventoryValue, avgInventory, beginInv, endInv,
    cashOnHand, currentAssets, currentLiabilities, fixedAssets, totalAssets,
    totalDebt, totalLiabilities, shareholdersEquity,
    interestExpense, sharesOutstanding, marketPricePerShare, dividendPerShare,
    dailyExpenses, cashSpent, cashReceived, annualSales,
    customers, newCustomers, newCustomersPrior, churnedCustomers,
    cacSpend, arpu, ltv, mrr, newMRR, expansionMRR, churnedMRR, contractionMRR,
    scrapExpenses, undeliverableOrders, totalOrders, unitsReceived, unitsSold,
    deadStock, availableStock, physicalInv,
  };
}

// ─── Ratio catalog (60 ratios across 6 categories) ─────────────────────
function buildRatios(inp) {
  // Helpers
  const ratio = (n, d) => d === 0 ? 0 : n / d;
  const pct = (n, d) => d === 0 ? 0 : (n / d) * 100;
  const days = (n, d) => d === 0 ? 0 : (n / d) * 365;

  return {
    accounting: [
      { name: "Accounts Receivable Turnover", desc: "How quickly the company collects outstanding debts from customers", formula: "Net Credit Sales / Average AR", value: ratio(inp.annualSales, inp.avgAR), fields: ["annualSales", "avgAR"], unit: "x", target: 10, dir: "high" },
      { name: "Accounts Payable Turnover", desc: "How quickly the company pays its suppliers", formula: "Total Supplier Purchases / Average AP", value: ratio(inp.cogs, inp.avgAP), fields: ["cogs", "avgAP"], unit: "x", target: 8, dir: "high" },
      { name: "Working Capital Ratio", desc: "Ability to meet short-term obligations", formula: "Current Assets / Current Liabilities", value: ratio(inp.currentAssets, inp.currentLiabilities), fields: ["currentAssets", "currentLiabilities"], unit: "x", target: 2, dir: "high" },
      { name: "Debt-to-Equity Ratio", desc: "Proportion of financing from debt vs equity", formula: "Total Debt / Total Equity", value: ratio(inp.totalDebt, inp.shareholdersEquity), fields: ["totalDebt", "shareholdersEquity"], unit: "x", target: 0.8, dir: "low" },
      { name: "Gross Profit Margin", desc: "Revenue left after deducting COGS", formula: "(Revenue − COGS) / Revenue", value: pct(inp.grossProfit, inp.revenue), fields: ["revenue", "cogs"], unit: "%", target: 50, dir: "high" },
      { name: "Net Profit Margin", desc: "Revenue left after deducting all expenses including tax", formula: "Net Income / Revenue", value: pct(inp.netIncome, inp.revenue), fields: ["netIncome", "revenue"], unit: "%", target: 15, dir: "high" },
      { name: "Return on Assets (ROA)", desc: "How efficiently assets generate profit", formula: "Net Income / Total Assets", value: pct(inp.netIncome, inp.totalAssets), fields: ["netIncome", "totalAssets"], unit: "%", target: 10, dir: "high" },
      { name: "Return on Equity (ROE)", desc: "Profit generated per dollar of shareholder equity", formula: "Net Income / Total Equity", value: pct(inp.netIncome, inp.shareholdersEquity), fields: ["netIncome", "shareholdersEquity"], unit: "%", target: 15, dir: "high" },
      { name: "Inventory Turnover", desc: "Times inventory is sold and replaced", formula: "COGS / Average Inventory", value: ratio(inp.cogs, inp.avgInventory), fields: ["cogs", "avgInventory"], unit: "x", target: 6, dir: "high" },
      { name: "Fixed Asset Turnover", desc: "How effectively fixed assets generate sales", formula: "Revenue / Fixed Assets", value: ratio(inp.revenue, inp.fixedAssets), fields: ["revenue", "fixedAssets"], unit: "x", target: 4, dir: "high" },
    ],
    cash: [
      { name: "Cash Burn Rate", desc: "Net cash spent per period", formula: "Cash Spent − Cash Received (monthly)", value: inp.cashSpent - inp.cashReceived, fields: ["cashSpent", "cashReceived"], unit: "$", target: 0, dir: "low" },
      { name: "Average Days Delinquent", desc: "Effectiveness of collection efforts", formula: "DSO − Best Possible DSO", value: days(inp.avgAR, inp.annualSales) - 28, fields: ["avgAR", "annualSales"], unit: "d", target: 7, dir: "low" },
      { name: "Operating Cash Flow", desc: "Money generated by daily operations", formula: "Net Income + Non-Cash Expenses − Δ Working Capital", value: inp.netIncome + 184000 - 64000, fields: ["netIncome"], unit: "$", target: 1000000, dir: "high" },
      { name: "Free Cash Flow", desc: "OCF + interest − asset purchases", formula: "OCF + Interest − Asset Purchases", value: inp.netIncome + 184000 - 64000 + inp.interestExpense - 320000, fields: ["netIncome", "interestExpense"], unit: "$", target: 800000, dir: "high" },
      { name: "Overdues Ratio", desc: "Effectiveness of collecting cash; quality of receivables", formula: "Overdues / Total Receivables", value: pct(inp.overdueAR, inp.avgAR), fields: ["overdueAR", "avgAR"], unit: "%", target: 15, dir: "low" },
      { name: "Days Inventory Outstanding (DIO)", desc: "Days inventory is held before being sold", formula: "(Avg Inventory / Yearly COGS) × 365", value: days(inp.avgInventory, inp.cogs), fields: ["avgInventory", "cogs"], unit: "d", target: 45, dir: "low" },
      { name: "Days Sales Outstanding (DSO)", desc: "Days to collect payment for a sale", formula: "(Avg AR / Annual Sales) × 365", value: days(inp.avgAR, inp.annualSales), fields: ["avgAR", "annualSales"], unit: "d", target: 35, dir: "low" },
      { name: "Days Payables Outstanding (DPO)", desc: "Days to pay suppliers", formula: "(Avg AP / Yearly COGS) × 365", value: days(inp.avgAP, inp.cogs), fields: ["avgAP", "cogs"], unit: "d", target: 40, dir: "high" },
      { name: "Cash Conversion Cycle", desc: "Days to convert inventory into cash from sales", formula: "DIO + DSO − DPO", value: days(inp.avgInventory, inp.cogs) + days(inp.avgAR, inp.annualSales) - days(inp.avgAP, inp.cogs), fields: ["avgInventory","cogs","avgAR","annualSales","avgAP"], unit: "d", target: 60, dir: "low" },
      { name: "Cash Reserves in Days", desc: "Days the company could survive with no income", formula: "Cash Reserves / Average Daily Expenses", value: ratio(inp.cashOnHand, inp.dailyExpenses), fields: ["cashOnHand", "dailyExpenses"], unit: "d", target: 90, dir: "high" },
    ],
    saas: [
      { name: "Customer Churn Rate", desc: "Customers lost in a given time frame", formula: "Customers Lost / Total Customers", value: pct(inp.churnedCustomers, inp.customers), fields: ["churnedCustomers", "customers"], unit: "%", target: 5, dir: "low" },
      { name: "New Buyer Growth Rate", desc: "Speed of new customer acquisition", formula: "(New this period − New last period) / New last period", value: pct(inp.newCustomers - inp.newCustomersPrior, inp.newCustomersPrior), fields: ["newCustomers", "newCustomersPrior"], unit: "%", target: 10, dir: "high" },
      { name: "Lifetime Value (LTV)", desc: "Revenue from a customer over retention period", formula: "Customer Value × Avg Lifespan", value: inp.ltv, fields: ["arpu"], unit: "$", target: 4000, dir: "high" },
      { name: "Customer Acquisition Cost (CAC)", desc: "Cost to acquire a new customer", formula: "S&M Spend / New Customers Acquired", value: ratio(inp.cacSpend, inp.newCustomers), fields: ["cacSpend", "newCustomers"], unit: "$", target: 800, dir: "low" },
      { name: "Net Burn Rate", desc: "Net cash spent in a time frame", formula: "Cash Spent − Cash Received", value: inp.cashSpent - inp.cashReceived, fields: ["cashSpent", "cashReceived"], unit: "$", target: 0, dir: "low" },
      { name: "Runway (months)", desc: "Months before cash runs out", formula: "Cash Balance / Monthly Burn", value: ratio(inp.cashOnHand, Math.max(inp.cashSpent - inp.cashReceived, 1)), fields: ["cashOnHand"], unit: "mo", target: 12, dir: "high" },
      { name: "Average Revenue Per User (ARPU)", desc: "Avg revenue per customer per month", formula: "Total Revenue / Customers", value: inp.arpu, fields: ["revenue", "customers"], unit: "$", target: 1200, dir: "high" },
      { name: "SaaS Quick Ratio", desc: "Revenue added vs revenue lost", formula: "(New MRR + Expansion) / (Churned + Contraction)", value: ratio(inp.newMRR + inp.expansionMRR, inp.churnedMRR + inp.contractionMRR), fields: ["newMRR","expansionMRR","churnedMRR","contractionMRR"], unit: "x", target: 4, dir: "high" },
      { name: "Monthly Recurring Revenue (MRR)", desc: "Monthly revenue from subscribers", formula: "Customers × Average Billed Amount", value: inp.mrr, fields: ["mrr"], unit: "$", target: 400000, dir: "high" },
      { name: "Total Addressable Market", desc: "Revenue potential at full coverage", formula: "ACV × Potential Clients", value: inp.arpu * 12 * 18000, fields: ["arpu"], unit: "$", target: 100000000, dir: "high" },
    ],
    investor: [
      { name: "Return on Investment (ROI)", desc: "Money made vs investment", formula: "Income from Asset / Asset Invested", value: pct(inp.netIncome, inp.totalAssets), fields: ["netIncome", "totalAssets"], unit: "%", target: 12, dir: "high" },
      { name: "Return on Equity (ROE)", desc: "Return on shareholders' equity", formula: "Net Income / Shareholders' Equity", value: pct(inp.netIncome, inp.shareholdersEquity), fields: ["netIncome", "shareholdersEquity"], unit: "%", target: 15, dir: "high" },
      { name: "Earnings per Share (EPS)", desc: "Profitability on a per-share basis", formula: "Net Income / Avg Outstanding Shares", value: ratio(inp.netIncome, inp.sharesOutstanding), fields: ["netIncome", "sharesOutstanding"], unit: "$", target: 2, dir: "high" },
      { name: "Price-to-Earnings (P/E)", desc: "Share price relative to earnings", formula: "Market Price per Share / EPS", value: ratio(inp.marketPricePerShare, ratio(inp.netIncome, inp.sharesOutstanding)), fields: ["marketPricePerShare", "netIncome", "sharesOutstanding"], unit: "x", target: 15, dir: "low" },
      { name: "Dividend Yield", desc: "Return from dividends", formula: "Annual Dividend per Share / Market Price", value: pct(inp.dividendPerShare, inp.marketPricePerShare), fields: ["dividendPerShare", "marketPricePerShare"], unit: "%", target: 2.5, dir: "high" },
      { name: "Debt-to-Equity Ratio", desc: "Financial leverage", formula: "Total Liabilities / Shareholders' Equity", value: ratio(inp.totalLiabilities, inp.shareholdersEquity), fields: ["totalLiabilities", "shareholdersEquity"], unit: "x", target: 1.0, dir: "low" },
      { name: "Current Ratio", desc: "Pay current liabilities with current assets", formula: "Current Assets / Current Liabilities", value: ratio(inp.currentAssets, inp.currentLiabilities), fields: ["currentAssets", "currentLiabilities"], unit: "x", target: 2.0, dir: "high" },
      { name: "Quick Ratio", desc: "Pay current liabilities with liquid assets", formula: "(Current Assets − Inventory) / Current Liabilities", value: ratio(inp.currentAssets - inp.inventoryValue, inp.currentLiabilities), fields: ["currentAssets", "inventoryValue", "currentLiabilities"], unit: "x", target: 1.0, dir: "high" },
      { name: "Gross Margin Ratio", desc: "Product/service profitability", formula: "(Revenue − COGS) / Revenue", value: pct(inp.grossProfit, inp.revenue), fields: ["revenue", "cogs"], unit: "%", target: 50, dir: "high" },
      { name: "Net Promoter Score (NPS)", desc: "Customer satisfaction & loyalty", formula: "% Promoters − % Detractors", value: 42, fields: [], unit: "", target: 40, dir: "high" },
    ],
    balance: [
      { name: "Current Ratio", desc: "Short-term liabilities vs assets", formula: "Current Assets / Current Liabilities", value: ratio(inp.currentAssets, inp.currentLiabilities), fields: ["currentAssets", "currentLiabilities"], unit: "x", target: 2.0, dir: "high" },
      { name: "Quick Ratio", desc: "Short-term liabilities vs liquid assets", formula: "(Current Assets − Inventory) / Current Liabilities", value: ratio(inp.currentAssets - inp.inventoryValue, inp.currentLiabilities), fields: ["currentAssets", "inventoryValue", "currentLiabilities"], unit: "x", target: 1.0, dir: "high" },
      { name: "Debt-to-Equity Ratio", desc: "Proportion of debt vs equity financing", formula: "Total Liabilities / Shareholders' Equity", value: ratio(inp.totalLiabilities, inp.shareholdersEquity), fields: ["totalLiabilities", "shareholdersEquity"], unit: "x", target: 1.0, dir: "low" },
      { name: "Debt Ratio", desc: "Proportion of assets financed by debt", formula: "Total Liabilities / Total Assets", value: pct(inp.totalLiabilities, inp.totalAssets), fields: ["totalLiabilities", "totalAssets"], unit: "%", target: 50, dir: "low" },
      { name: "Interest Coverage Ratio", desc: "Ability to pay interest on debt", formula: "EBIT / Interest Expense", value: ratio(inp.ebit, inp.interestExpense), fields: ["ebit", "interestExpense"], unit: "x", target: 5, dir: "high" },
      { name: "Return on Assets (ROA)", desc: "Profit generation from assets", formula: "Net Income / Total Assets", value: pct(inp.netIncome, inp.totalAssets), fields: ["netIncome", "totalAssets"], unit: "%", target: 10, dir: "high" },
      { name: "Return on Equity (ROE)", desc: "Profit from shareholder investment", formula: "Net Income / Shareholders' Equity", value: pct(inp.netIncome, inp.shareholdersEquity), fields: ["netIncome", "shareholdersEquity"], unit: "%", target: 15, dir: "high" },
      { name: "Inventory Turnover", desc: "How quickly inventory sells", formula: "COGS / Average Inventory", value: ratio(inp.cogs, inp.avgInventory), fields: ["cogs", "avgInventory"], unit: "x", target: 6, dir: "high" },
      { name: "Accounts Receivable Turnover", desc: "How quickly receivables are collected", formula: "Revenue / Average AR", value: ratio(inp.revenue, inp.avgAR), fields: ["revenue", "avgAR"], unit: "x", target: 10, dir: "high" },
      { name: "Working Capital", desc: "Cash and assets to cover short-term liabilities", formula: "Current Assets − Current Liabilities", value: inp.currentAssets - inp.currentLiabilities, fields: ["currentAssets", "currentLiabilities"], unit: "$", target: 500000, dir: "high" },
    ],
    inventory: [
      { name: "Average Inventory", desc: "Avg inventory on-hand during a period", formula: "(Begin Inv + End Inv) / 2", value: (inp.beginInv + inp.endInv) / 2, fields: ["beginInv", "endInv"], unit: "$", target: null, dir: "neutral" },
      { name: "Days on Hand (DOH)", desc: "Avg days before inventory is sold", formula: "(Avg Inventory / COGS) × 365", value: days(inp.avgInventory, inp.cogs), fields: ["avgInventory", "cogs"], unit: "d", target: 60, dir: "low" },
      { name: "Stock to Sales Ratio", desc: "Inventory vs sales for the period", formula: "Inventory Value / Sales Value", value: ratio(inp.inventoryValue, inp.annualSales / 12), fields: ["inventoryValue", "currentMonthRev"], unit: "x", target: 1.5, dir: "low" },
      { name: "Cost of Carry", desc: "% of inventory value spent on storage etc", formula: "(Service + Risk + Capital + Storage) / Total Inv", value: pct(inp.inventoryValue * 0.22, inp.inventoryValue), fields: ["inventoryValue"], unit: "%", target: 20, dir: "low" },
      { name: "Backorder Rate", desc: "Delayed orders due to stockouts", formula: "Undeliverable / Total Orders", value: pct(inp.undeliverableOrders, inp.totalOrders), fields: ["undeliverableOrders", "totalOrders"], unit: "%", target: 2, dir: "low" },
      { name: "Sell-Through Rate", desc: "Units sold vs received from manufacturer", formula: "Units Sold / Units Received", value: pct(inp.unitsSold, inp.unitsReceived), fields: ["unitsSold", "unitsReceived"], unit: "%", target: 80, dir: "high" },
      { name: "Scrap Rate", desc: "Quality of inventory; non-quality costs", formula: "Scrap Expenses / Average Inventory", value: pct(inp.scrapExpenses, inp.avgInventory), fields: ["scrapExpenses", "avgInventory"], unit: "%", target: 3, dir: "low" },
      { name: "Time to Receive", desc: "Efficiency of stock receiving", formula: "Validation + Records + Storage Prep (hrs)", value: 6.4, fields: [], unit: "hr", target: 8, dir: "low" },
      { name: "Inventory Shrinkage", desc: "Damage, miscounts, fraud", formula: "Ending Inv − Physical Count", value: inp.endInv - inp.physicalInv, fields: ["endInv", "physicalInv"], unit: "$", target: 0, dir: "low" },
      { name: "Dead Stock", desc: "Unsellable stock as share of available", formula: "Unsellable / Available", value: pct(inp.deadStock, inp.availableStock), fields: ["deadStock", "availableStock"], unit: "%", target: 5, dir: "low" },
    ],
  };
}

const CATEGORIES = [
  { key: "accounting", label: "Accounting", desc: "Profitability, returns, and turnover ratios" },
  { key: "cash",       label: "Cash",       desc: "Liquidity, working capital, and cash cycle" },
  { key: "saas",       label: "SaaS",       desc: "Recurring revenue, retention, and unit economics" },
  { key: "investor",   label: "Investor",   desc: "Returns, leverage, and valuation" },
  { key: "balance",    label: "Balance Sheet", desc: "Solvency and capital structure" },
  { key: "inventory",  label: "Inventory",  desc: "Stock turn, carry cost, and shrinkage" },
];

function buildPriorInputs(data) {
  const inp = buildRatioInputs(data, "TTM");
  const out = {};
  Object.keys(inp).forEach(k => {
    const v = inp[k];
    out[k] = (typeof v === "number") ? v * (0.91 + (k.length % 5) * 0.018) : v;
  });
  return out;
}

const INDUSTRY_BENCHMARKS = {
  "Accounts Receivable Turnover": 10, "Accounts Payable Turnover": 8, "Working Capital Ratio": 2.0,
  "Debt-to-Equity Ratio": 0.8, "Gross Profit Margin": 50, "Net Profit Margin": 15,
  "Return on Assets (ROA)": 10, "Return on Equity (ROE)": 15, "Inventory Turnover": 6,
  "Fixed Asset Turnover": 4, "Cash Burn Rate": 0, "Average Days Delinquent": 7,
  "Operating Cash Flow": 1000000, "Free Cash Flow": 800000, "Overdues Ratio": 15,
  "Days Inventory Outstanding (DIO)": 45, "Days Sales Outstanding (DSO)": 35,
  "Days Payables Outstanding (DPO)": 40, "Cash Conversion Cycle": 60, "Cash Reserves in Days": 90,
  "Customer Churn Rate": 5, "New Buyer Growth Rate": 10, "Lifetime Value (LTV)": 4000,
  "Customer Acquisition Cost (CAC)": 800, "Net Burn Rate": 0, "Runway (months)": 12,
  "Average Revenue Per User (ARPU)": 1200, "SaaS Quick Ratio": 4, "Monthly Recurring Revenue (MRR)": 400000,
  "Total Addressable Market": 100000000, "Return on Investment (ROI)": 12, "Earnings per Share (EPS)": 2,
  "Price-to-Earnings (P/E)": 15, "Dividend Yield": 2.5, "Current Ratio": 2.0, "Quick Ratio": 1.0,
  "Gross Margin Ratio": 50, "Net Promoter Score (NPS)": 40, "Debt Ratio": 50,
  "Interest Coverage Ratio": 5, "Working Capital": 500000, "Days on Hand (DOH)": 60,
  "Stock to Sales Ratio": 1.5, "Cost of Carry": 20, "Backorder Rate": 2, "Sell-Through Rate": 80,
  "Scrap Rate": 3, "Time to Receive": 8, "Dead Stock": 5,
};

function applyTargetSource(ratio, source) {
  const ind = INDUSTRY_BENCHMARKS[ratio.name];
  if (ind == null) return ratio;
  // For "low" direction ratios (DSO, churn, debt), tougher = lower target
  const flip = ratio.dir === "low" ? -1 : 1;
  if (source === "industry")   return { ...ratio, target: ind };
  if (source === "historical") return { ...ratio, target: ind * (1 - flip * 0.10) };   // easier
  if (source === "manual")     return { ...ratio, target: ind * (1 + flip * 0.12) };   // stretch goals
  return ratio;
}

function RatiosPage({ data, bizType, period: globalPeriod, setPeriod: setGlobalPeriod }) {
  const { PeriodComparerEl } = window.usePeriod(globalPeriod, setGlobalPeriod);
  const [activeCat, setActiveCat] = useStateRat("accounting");
  const [drillStatus, setDrillStatus] = useStateRat(null);
  const [trendRatio, setTrendRatio] = useStateRat(null);
  const [targetSource, setTargetSource] = useStateRat("industry");

  const inp = useMemoRat(() => buildRatioInputs(data, globalPeriod), [data, globalPeriod]);
  const priorInp = useMemoRat(() => buildPriorInputs(data), [data]);
  const ratiosRaw = useMemoRat(() => buildRatios(inp), [inp]);
  const priorRaw = useMemoRat(() => buildRatios(priorInp), [priorInp]);

  const ratios = {};
  Object.keys(ratiosRaw).forEach(cat => {
    ratios[cat] = ratiosRaw[cat].map((r, idx) => {
      const t = applyTargetSource(r, targetSource);
      return { ...t, priorValue: priorRaw[cat][idx].value };
    });
  });

  const currentList = ratios[activeCat];

  // Health summary across all categories
  const allRatios = Object.values(ratios).flat().map((r, idx) => ({ ...r, _cat: Object.keys(ratios)[Math.floor(idx / 10)] }));
  // Better: build with category tag preserved
  const allWithCat = [];
  Object.entries(ratios).forEach(([cat, list]) => list.forEach(r => allWithCat.push({ ...r, _cat: cat })));
  const onTarget = allWithCat.filter(r => isOnTarget(r));
  const watch    = allWithCat.filter(r => isWatch(r));
  const off      = allWithCat.filter(r => isOff(r));

  const drillList =
    drillStatus === "on"    ? onTarget :
    drillStatus === "watch" ? watch :
    drillStatus === "off"   ? off : [];
  const drillTitle =
    drillStatus === "on"    ? "Ratios on target" :
    drillStatus === "watch" ? "Watch list — ratios approaching target" :
    drillStatus === "off"   ? "Off target — ratios needing attention" : "";
  const drillColor =
    drillStatus === "on"    ? "var(--positive)" :
    drillStatus === "watch" ? "var(--warning)" :
    drillStatus === "off"   ? "var(--danger)" : "var(--text-3)";

  // Export rows (current category)
  const exportRows = currentList.map(r => ({
    Ratio: r.name, Description: r.desc, Formula: r.formula,
    Value: formatValue(r.value, r.unit), Target: r.target !== null ? formatValue(r.target, r.unit) : "—",
    Status: status(r), Inputs: r.fields.join(", "),
  }));

  return (
    <div className="pc-page">
      {PeriodComparerEl}
      <div className="pc-kpi-grid pc-kpi-grid-4">
        <KPICard label="Total ratios tracked" value={allWithCat.length} hint="6 categories · refreshed daily" accent="var(--accent)" />
        <KPICard label="On target"   value={onTarget.length} hint={`${(onTarget.length/allWithCat.length*100).toFixed(0)}% of all ratios`} accent="var(--positive)" onClick={() => setDrillStatus("on")} />
        <KPICard label="Watch list"  value={watch.length} hint="Within tolerance band · click to view" accent="var(--warning)" onClick={() => setDrillStatus("watch")} />
        <KPICard label="Off target"  value={off.length} hint="Needs attention · click to view" accent="var(--danger)" onClick={() => setDrillStatus("off")} />
      </div>



      <Card title="Target source" subtitle="Switch the benchmark each ratio is compared against · recalculates immediately">
        <div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
          {[
            { v: "industry",   l: "Industry benchmark",      d: "Peer-median by industry + size band" },
            { v: "historical", l: "Your historical baseline", d: "Trailing 12-month median of your own data" },
            { v: "manual",     l: "Manual / strategic goal",  d: "Set in company setup" },
          ].map(o => (
            <button key={o.v}
              className={"pc-wiz-typecard " + (targetSource === o.v ? "selected" : "")}
              style={{ flex: 1, minWidth: 220, cursor: "pointer", padding: "12px 14px" }}
              onClick={() => setTargetSource(o.v)}>
              <div className="pc-wiz-typecard-radio">{targetSource === o.v && <div className="pc-wiz-typecard-dot" />}</div>
              <div style={{ textAlign: "left" }}>
                <div style={{ fontSize: 13, fontWeight: 600 }}>{o.l}</div>
                <div style={{ fontSize: 11.5, color: "var(--text-3)", marginTop: 3 }}>{o.d}</div>
              </div>
            </button>
          ))}
        </div>
      </Card>

      <div className="pc-section-tabs">
        {CATEGORIES.map(c => (
          <button key={c.key} className={activeCat === c.key ? "active" : ""} onClick={() => setActiveCat(c.key)}>
            {c.label}
            <span style={{ fontSize: 10, color: "var(--text-3)", marginLeft: 6 }}>{ratios[c.key].length}</span>
          </button>
        ))}
        <div style={{ flex: 1 }} />
        <ExportButton rows={exportRows} filename={"Ratios_" + activeCat} />
      </div>

      <Card padding={0}>
        <div style={{ padding: "14px 16px", borderBottom: "1px solid var(--border)" }}>
          <div className="pc-card-title">{CATEGORIES.find(c => c.key === activeCat).label} ratios</div>
          <div className="pc-card-subtitle">{CATEGORIES.find(c => c.key === activeCat).desc} · click any row for the 12-month trend</div>
        </div>
        <table className="pc-table">
          <thead>
            <tr>
              <th>Ratio</th>
              <th>Formula</th>
              <th style={{textAlign:"right"}}>Value</th>
              <th style={{textAlign:"right"}}>Prior</th>
              <th style={{textAlign:"right"}} title="Change since the previous period">vs last period</th>
              <th style={{textAlign:"right"}}>Target ({targetSource})</th>
              <th>vs target</th>
              <th>Status</th>
            </tr>
          </thead>
          <tbody>
            {currentList.map((r, i) => (
              <RatioRow key={i} ratio={r} onTrend={() => setTrendRatio(r)} />
            ))}
          </tbody>
        </table>
      </Card>

      {drillStatus && (
        <RatiosDrillModal
          title={drillTitle}
          color={drillColor}
          ratios={drillList}
          onClose={() => setDrillStatus(null)}
          onJumpToCat={(cat) => { setActiveCat(cat); setDrillStatus(null); }}
        />
      )}
      {trendRatio && <RatioTrendModal ratio={trendRatio} targetSource={targetSource} onClose={() => setTrendRatio(null)} />}
    </div>
  );
}

function RatioTrendModal({ ratio, targetSource, onClose }) {
  useEffect(() => {
    const onKey = (e) => { if (e.key === "Escape") onClose(); };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [onClose]);
  const history = Array.from({length: 24}, (_, k) => {
    const drift = 0.85 + (Math.sin(k / 3) * 0.08) + (k / 24) * 0.18;
    return ratio.value * drift * (0.95 + ((k * 37) % 13) / 100);
  });
  const target = ratio.target ?? ratio.value;
  const months = ["Jun24","Jul","Aug","Sep","Oct","Nov","Dec","Jan25","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec","Jan26","Feb","Mar","Apr","May"];
  const st = status(ratio);
  const stColor = st === "on" ? "var(--positive)" : st === "watch" ? "var(--warning)" : "var(--danger)";
  return (
    <div className="pc-modal-backdrop" onClick={onClose}>
      <div className="pc-modal pc-modal-wide" onClick={(e) => e.stopPropagation()}>
        <div className="pc-modal-hd">
          <div>
            <div style={{ fontSize: 11, color: "var(--text-3)", textTransform: "uppercase", letterSpacing: 0.5 }}>Ratio · 24-month trend</div>
            <div style={{ fontSize: 18, fontWeight: 600 }}>{ratio.name}</div>
          </div>
          <button className="pc-icon-btn" onClick={onClose} title="Close">✕</button>
        </div>
        <div className="pc-modal-body">
          <div style={{ display: "flex", gap: 24, marginBottom: 16, flexWrap: "wrap" }}>
            <div><div className="pc-mk-l">Current</div><div className="pc-mk-v" style={{ color: stColor }}>{formatValue(ratio.value, ratio.unit)}</div></div>
            <div><div className="pc-mk-l">Prior period</div><div className="pc-mk-v">{formatValue(ratio.priorValue, ratio.unit)}</div></div>
            <div><div className="pc-mk-l">24mo high / low</div><div className="pc-mk-v">{formatValue(Math.max(...history), ratio.unit)} / {formatValue(Math.min(...history), ratio.unit)}</div></div>
            <div><div className="pc-mk-l">Target ({targetSource})</div><div className="pc-mk-v" style={{ color: "var(--accent)" }}>{formatValue(target, ratio.unit)}</div></div>
          </div>
          <AreaChart height={280} labels={months}
            yFmt={(v) => formatValue(v, ratio.unit)}
            series={[
              { name: ratio.name, values: history, color: stColor },
              { name: "Target", values: history.map(() => target), color: "var(--accent)", fill: false, dashed: true },
              { name: "Peer median band", values: history.map(() => target * 1.12), color: "var(--text-3)", fill: false, dashed: true },
            ]} />
          <div style={{ fontSize: 12.5, color: "var(--text-2)", marginTop: 16, lineHeight: 1.6 }}>
            <b>Formula:</b> <span style={{ fontFamily: "ui-monospace, monospace" }}>{ratio.formula}</span>
          </div>
        </div>
      </div>
    </div>
  );
}

function RatiosDrillModal({ title, color, ratios, onClose, onJumpToCat }) {
  useEffect(() => {
    const onKey = (e) => { if (e.key === "Escape") onClose(); };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [onClose]);
  return (
    <div className="pc-modal-backdrop" onClick={onClose}>
      <div className="pc-modal pc-modal-wide" onClick={(e) => e.stopPropagation()}>
        <div className="pc-modal-hd">
          <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
            <span style={{ width: 8, height: 8, borderRadius: "50%", background: color, boxShadow: "0 0 8px " + color }} />
            <span style={{ fontSize: 14, fontWeight: 600 }}>{title}</span>
            <span style={{ fontSize: 11, color: "var(--text-3)" }}>{ratios.length} ratio{ratios.length !== 1 ? "s" : ""}</span>
          </div>
          <button className="pc-icon-btn" onClick={onClose} title="Close">✕</button>
        </div>
        <div className="pc-modal-body">
          {ratios.length === 0 ? (
            <div style={{ padding: 40, textAlign: "center", color: "var(--text-3)" }}>No ratios in this group.</div>
          ) : (
            <table className="pc-table">
              <thead>
                <tr>
                  <th>Ratio</th>
                  <th>Category</th>
                  <th>Formula</th>
                  <th style={{textAlign:"right"}}>Value</th>
                  <th style={{textAlign:"right"}}>Target</th>
                  <th>vs target</th>
                  <th></th>
                </tr>
              </thead>
              <tbody>
                {ratios.map((r, i) => {
                  const st = status(r);
                  const stColor = st === "on" ? "var(--positive)" : st === "watch" ? "var(--warning)" : st === "off" ? "var(--danger)" : "var(--text-3)";
                  const catLabel = CATEGORIES.find(c => c.key === r._cat)?.label || r._cat;
                  return (
                    <tr key={i} className="pc-clickable" onClick={() => onJumpToCat(r._cat)}>
                      <td style={{paddingLeft: 14}}>
                        <div style={{ fontWeight: 500 }}>{r.name}</div>
                        <div style={{ fontSize: 11, color: "var(--text-3)", marginTop: 2 }}>{r.desc}</div>
                      </td>
                      <td><span className="pc-chip">{catLabel}</span></td>
                      <td style={{fontFamily: "ui-monospace, monospace", fontSize: 11, color: "var(--text-2)"}}>{r.formula}</td>
                      <td style={{fontFamily: "ui-monospace, monospace", textAlign: "right", fontWeight: 600, fontSize: 14, color: stColor}}>{formatValue(r.value, r.unit)}</td>
                      <td style={{fontFamily: "ui-monospace, monospace", textAlign: "right", color: "var(--text-3)"}}>{r.target !== null ? formatValue(r.target, r.unit) : "—"}</td>
                      <td>{r.target !== null && r.dir !== "neutral" ? <TargetBar value={r.value} target={r.target} dir={r.dir} status={st} /> : "—"}</td>
                      <td style={{ textAlign: "right", paddingRight: 14 }}>
                        <button className="pc-btn-mini ghost" onClick={(e) => { e.stopPropagation(); onJumpToCat(r._cat); }}>View →</button>
                      </td>
                    </tr>
                  );
                })}
              </tbody>
            </table>
          )}
        </div>
      </div>
    </div>
  );
}

function RatioRow({ ratio, onTrend }) {
  const st = status(ratio);
  const stColor = st === "on" ? "var(--positive)" : st === "watch" ? "var(--warning)" : st === "off" ? "var(--danger)" : "var(--text-3)";
  const stLabel = st === "on" ? "On target" : st === "watch" ? "Watch" : st === "off" ? "Off target" : "—";
  const delta = ratio.priorValue !== undefined ? ratio.value - ratio.priorValue : null;
  const deltaPct = (delta !== null && ratio.priorValue) ? (delta / Math.abs(ratio.priorValue)) * 100 : null;
  const deltaIsGood = ratio.dir === "low" ? (delta < 0) : (delta > 0);
  const deltaColor = delta === null ? "var(--text-3)" : deltaIsGood ? "var(--positive)" : delta === 0 ? "var(--text-3)" : "var(--danger)";

  return (
    <tr className="pc-clickable" onClick={onTrend}>
      <td style={{paddingLeft: 14}}>
        <div style={{ fontWeight: 500 }}>{ratio.name}</div>
        <div style={{ fontSize: 11, color: "var(--text-3)", marginTop: 2 }}>{ratio.desc}</div>
      </td>
      <td style={{fontFamily: "ui-monospace, monospace", fontSize: 11, color: "var(--text-2)"}}>{ratio.formula}</td>
      <td style={{fontFamily: "ui-monospace, monospace", textAlign: "right", fontWeight: 600, fontSize: 14, color: stColor}}>
        {formatValue(ratio.value, ratio.unit)}
      </td>
      <td style={{fontFamily: "ui-monospace, monospace", textAlign: "right", color: "var(--text-3)"}}>
        {ratio.priorValue !== undefined ? formatValue(ratio.priorValue, ratio.unit) : "—"}
      </td>
      <td style={{fontFamily: "ui-monospace, monospace", textAlign: "right", color: deltaColor}}>
        {deltaPct !== null ? (deltaPct >= 0 ? "▲ " : "▼ ") + Math.abs(deltaPct).toFixed(1) + "%" : "—"}
      </td>
      <td style={{fontFamily: "ui-monospace, monospace", textAlign: "right", color: "var(--text-3)"}}>
        {ratio.target !== null ? formatValue(ratio.target, ratio.unit) : "—"}
      </td>
      <td>
        {ratio.target !== null && ratio.dir !== "neutral" ? (
          <TargetBar value={ratio.value} target={ratio.target} dir={ratio.dir} status={st} />
        ) : <span style={{fontSize: 11, color: "var(--text-3)"}}>—</span>}
      </td>
      <td><span className="pc-statpill" style={{background: stColor + "26", color: stColor}}>{stLabel}</span></td>
    </tr>
  );
}

function TargetBar({ value, target, dir, status }) {
  // Visual gauge: 100% = target, value position relative
  const ratio = target === 0 ? 0 : Math.max(0, Math.min(value / target, 2));
  const fillPct = Math.min(ratio * 50, 100);
  const color = status === "on" ? "var(--positive)" : status === "watch" ? "var(--warning)" : "var(--danger)";
  return (
    <div style={{ position: "relative", width: 80, height: 6, background: "var(--bg-elev-2)", borderRadius: 1 }}>
      <div style={{ width: fillPct + "%", height: "100%", background: color, borderRadius: 1 }} />
      <div style={{ position: "absolute", top: -2, bottom: -2, left: "50%", width: 2, background: "var(--text-3)" }} title="Target" />
    </div>
  );
}

// Helpers
function status(r) {
  if (r.target === null || r.dir === "neutral") return "neutral";
  const ratio = r.target === 0 ? 1 : r.value / r.target;
  if (r.dir === "high") {
    if (ratio >= 1) return "on";
    if (ratio >= 0.85) return "watch";
    return "off";
  }
  // dir === "low" — value should be at-or-below target
  if (ratio <= 1) return "on";
  if (ratio <= 1.15) return "watch";
  return "off";
}
const isOnTarget = (r) => status(r) === "on";
const isWatch    = (r) => status(r) === "watch";
const isOff      = (r) => status(r) === "off";

function formatValue(v, unit) {
  if (v === null || v === undefined || isNaN(v)) return "—";
  if (unit === "%") return v.toFixed(1) + "%";
  if (unit === "x") return v.toFixed(2) + "x";
  if (unit === "d") return Math.round(v) + " d";
  if (unit === "mo") return v.toFixed(1) + " mo";
  if (unit === "hr") return v.toFixed(1) + " hr";
  if (unit === "$") return fmtUSD(v, { compact: Math.abs(v) >= 10000 });
  return v.toFixed(2);
}

const FIELD_LABELS = {
  annualSales: "Revenue (TTM)", revenue: "Revenue (TTM)", currentMonthRev: "Revenue (current)",
  cogs: "COGS (TTM)", grossProfit: "Gross profit", opex: "Operating expenses", ebitda: "EBITDA",
  ebit: "EBIT", netIncome: "Net income",
  avgAR: "Average AR", overdueAR: "Overdue AR", avgAP: "Average AP",
  inventoryValue: "Inventory at cost", avgInventory: "Average inventory", beginInv: "Beg inventory", endInv: "End inventory", physicalInv: "Physical count",
  cashOnHand: "Cash on hand", cashSpent: "Cash outflows", cashReceived: "Cash inflows", dailyExpenses: "Daily expenses",
  currentAssets: "Current assets", currentLiabilities: "Current liabilities", fixedAssets: "Fixed assets",
  totalAssets: "Total assets", totalDebt: "Total debt", totalLiabilities: "Total liabilities", shareholdersEquity: "Shareholder equity",
  interestExpense: "Interest expense", sharesOutstanding: "Shares outstanding", marketPricePerShare: "Share price", dividendPerShare: "Dividend per share",
  customers: "Customers (active)", newCustomers: "New customers (current)", newCustomersPrior: "New customers (prior)", churnedCustomers: "Churned customers",
  cacSpend: "S&M spend", arpu: "ARPU", mrr: "MRR (current)",
  newMRR: "New MRR", expansionMRR: "Expansion MRR", churnedMRR: "Churned MRR", contractionMRR: "Contraction MRR",
  scrapExpenses: "Scrap cost", undeliverableOrders: "Undeliverable orders", totalOrders: "Total orders",
  unitsReceived: "Units received", unitsSold: "Units sold", deadStock: "Dead stock", availableStock: "Available stock",
};
function labelForField(f) { return FIELD_LABELS[f] || f; }

const SOURCE_MAP = {
  annualSales: "GL · Revenue accounts (4xxx)", revenue: "GL · Revenue accounts (4xxx)", currentMonthRev: "GL · Revenue, current period",
  cogs: "GL · COGS accounts (5xxx)", grossProfit: "Derived · Revenue − COGS", opex: "GL · Operating expense accounts (6xxx)", ebitda: "Derived · GP − Opex",
  ebit: "Derived · EBITDA − D&A", netIncome: "GL · Bottom line",
  avgAR: "AR aging snapshot", overdueAR: "AR aging · buckets > 30d", avgAP: "AP aging snapshot",
  inventoryValue: "Inventory module · ending stock × unit cost", avgInventory: "Inventory module · period average",
  beginInv: "Inventory snapshot · period open", endInv: "Inventory snapshot · period close", physicalInv: "Inventory module · physical count",
  cashOnHand: "Bank feed · live", cashSpent: "Bank + GL · outflows", cashReceived: "Bank + GL · inflows", dailyExpenses: "Derived · (COGS+Opex)/365",
  currentAssets: "Balance sheet · GL", currentLiabilities: "Balance sheet · GL", fixedAssets: "Balance sheet · fixed asset register",
  totalAssets: "Balance sheet · total", totalDebt: "Debt schedule + GL", totalLiabilities: "Balance sheet · total", shareholdersEquity: "Balance sheet · equity",
  interestExpense: "GL · interest accounts", sharesOutstanding: "Cap table", marketPricePerShare: "Manual entry (private co.)", dividendPerShare: "Distribution schedule",
  customers: "CRM · active customer count", newCustomers: "CRM · new logos current", newCustomersPrior: "CRM · new logos prior", churnedCustomers: "CRM · churned customers",
  cacSpend: "GL · sales & marketing accounts", arpu: "Derived · Revenue/Customers", mrr: "Billing system · current MRR",
  newMRR: "Billing system · new MRR", expansionMRR: "Billing system · expansion", churnedMRR: "Billing system · churn", contractionMRR: "Billing system · contraction",
  scrapExpenses: "GL · scrap & write-off accounts", undeliverableOrders: "Fulfillment system",
  totalOrders: "Order system", unitsReceived: "Inventory movement · receipts", unitsSold: "Inventory movement · shipments",
  deadStock: "Inventory · dead stock filter", availableStock: "Inventory · available-to-sell",
};
function sourceForField(f) { return SOURCE_MAP[f] || "—"; }

Object.assign(window, { RatiosPage });
