/* cst-data-price.jsx — content + math for "Price-It": price a job, or value a
   business. The numbers are computed locally (visible math); only the blunt
   takeaway is AI-written with a heuristic fallback. Exported to window.CST_PRICE. */

const _P = window.CST;

// ---- Mode A: price a job / product / service -------------------------------
// Charge basis bridges different businesses: a plumber thinks in hours, a
// consultant charges by project but counts cost by month, a shop sells items.
// Everything is normalised to a MONTH, and the universal output is $/hr kept.
const BASES = {
  hour:    { unit: 'hour',    priceUnit: '/hr',      noun: 'an hour',     volWord: 'billable hours' },
  project: { unit: 'project', priceUnit: '/project', noun: 'a project',   volWord: 'projects' },
  month:   { unit: 'client',  priceUnit: '/mo',      noun: 'a month',     volWord: 'paying clients' },
  item:    { unit: 'item',    priceUnit: '/item',    noun: 'an item',     volWord: 'units' },
};
const basisOf = (inp) => BASES[(inp && inp.basis) || 'project'] || BASES.project;

const JOB_STEPS = [
  { id: 'basis', kind: 'choice',
    prompt: 'How do you charge for this?',
    choices: [
      { value: 'hour',    label: 'By the hour',     desc: 'An hourly rate — trades, freelance, consulting' },
      { value: 'project', label: 'By the project',  desc: 'A fixed price per job or engagement' },
      { value: 'month',   label: 'By the month',    desc: 'A retainer or subscription' },
      { value: 'item',    label: 'By the item',     desc: 'A product or unit you sell' },
    ] },
  { id: 'price', kind: 'number', prefix: '$',
    unit: (a) => basisOf(a).priceUnit,
    prompt: (a) => `What do you charge ${basisOf(a).priceUnit === '/hr' ? 'per hour' : 'per ' + basisOf(a).unit}?`,
    hint: "Your best guess is fine if it's new — we'll tell you if it's right.",
    placeholder: '0' },
  { id: 'volume', kind: 'number',
    skipIf: (a) => a.basis === 'hour',
    unit: '/ mo',
    prompt: (a) => `In a typical month, how many ${basisOf(a).volWord} do you ${a.basis === 'item' ? 'sell' : a.basis === 'month' ? 'have' : 'take on'}?`,
    hint: 'Roughly is fine — we use it to work out your month.',
    placeholder: a => (a.basis === 'month' ? '1' : '0') },
  { id: 'hours', kind: 'number', unit: 'hrs / mo',
    prompt: (a) => a.basis === 'hour'
      ? 'How many hours do you actually bill in a month?'
      : 'Roughly how many hours a month do you spend on this?',
    hint: (a) => a.basis === 'hour'
      ? 'Your real billable hours — most people overestimate. Leave out admin you can’t charge for.'
      : 'All of your own time — delivery, admin, revisions, travel.',
    placeholder: '0' },
  { id: 'cost', kind: 'number', prefix: '$', unit: '/ mo',
    prompt: 'What does it cost you to run this per month?',
    hint: 'Materials, software, subcontractors, fees — everything except your own pay.',
    placeholder: '0' },
];

function priceJob(inp) {
  const B = basisOf(inp);
  const basis = (inp && inp.basis) || 'project';
  const price = +inp.price || 0;            // per unit
  const hours = +inp.hours || 0;            // own hours per month
  const monthlyCost = +inp.cost || 0;       // delivery cost per month
  const volume = basis === 'hour' ? hours : (basis === 'month' ? (+inp.volume || 1) : (+inp.volume || 0));

  const monthlyRevenue = price * volume;
  const monthlyHours = hours;
  const monthlyProfit = monthlyRevenue - monthlyCost;
  const marginPct = monthlyRevenue > 0 ? (monthlyProfit / monthlyRevenue) * 100 : 0;
  const effHourly = monthlyHours > 0 ? monthlyProfit / monthlyHours : monthlyProfit;
  const costPerUnit = volume > 0 ? monthlyCost / volume : monthlyCost;
  const profitPerUnit = price - costPerUnit;
  // honest what-if: a 20% price raise, holding volume/cost
  const scenarioPrice = price * 1.2;
  const scenarioProfit = scenarioPrice * volume - monthlyCost;
  const scenarioHourly = monthlyHours > 0 ? scenarioProfit / monthlyHours : scenarioProfit;

  // a comprehensible floor for "thin": below a livable effective rate OR <20% margin
  const HOURLY_FLOOR = 30; // a deliberately low sanity floor, not a target
  let flag, headline;
  const per = B.priceUnit === '/hr' ? '/hr' : ' per ' + B.unit;
  if (price <= 0 || (basis !== 'hour' && volume <= 0)) { flag = 'none'; headline = 'Fill in your numbers and I’ll tell you the truth about them.'; }
  else if (monthlyProfit < 0) { flag = 'losing'; headline = `You’re losing ${_P.moneyFull(-monthlyProfit)} a month on this.`; }
  else if (effHourly < HOURLY_FLOOR || marginPct < 20) { flag = 'under'; headline = `Your time is really earning ${_P.moneyFull(effHourly)}/hr — thinner than it looks.`; }
  else { flag = 'ok'; headline = `Solid — your time earns ${_P.moneyFull(effHourly)}/hr after costs.`; }

  return {
    mode: 'job', basis, unitLabel: B.unit, priceUnit: B.priceUnit, perWord: per,
    price, volume, hours: monthlyHours, monthlyCost,
    monthlyRevenue, monthlyProfit, marginPct, effHourly,
    costPerUnit, profitPerUnit,
    scenarioPrice, scenarioProfit, scenarioHourly,
    breakEven: costPerUnit, flag, headline,
  };
}

// ---- Mode B: value a business ----------------------------------------------
// Rule-of-thumb ranges only — a starting band, NOT a formal appraisal.
const INDUSTRIES = [
  { value: 'services', label: 'Services / agency',        sde: [2.0, 3.5], rev: [0.5, 1.2] },
  { value: 'ecom',     label: 'E-commerce / DTC',         sde: [2.5, 4.0], rev: [0.8, 2.0] },
  { value: 'saas',     label: 'SaaS / software',          sde: [3.5, 6.0], rev: [3.0, 8.0] },
  { value: 'local',    label: 'Local / brick-and-mortar', sde: [1.8, 3.0], rev: [0.3, 0.8] },
  { value: 'mfg',      label: 'Manufacturing / trades',   sde: [3.0, 5.0], rev: [0.5, 1.2] },
  { value: 'other',    label: 'Other / not sure',         sde: [2.0, 4.0], rev: [0.5, 1.5] },
];

const BIZ_STEPS = [
  { id: 'sde', kind: 'number', prefix: '$',
    prompt: "What's your yearly owner profit (SDE)?",
    hint: 'Take-home profit plus your salary and one-off add-backs. The number a buyer really cares about.',
    placeholder: '0' },
  { id: 'revenue', kind: 'number', prefix: '$',
    prompt: 'And your annual revenue?',
    hint: 'Top-line sales over the last 12 months.',
    placeholder: '0' },
  { id: 'industry', kind: 'choice',
    prompt: 'Which fits you best?',
    choices: INDUSTRIES.map((i) => ({ value: i.value, label: i.label })),
  },
];

function valueBusiness(inp) {
  const sde = +inp.sde || 0;
  const revenue = +inp.revenue || 0;
  const ind = INDUSTRIES.find((i) => i.value === inp.industry) || INDUSTRIES[5];
  const sdeLo = sde * ind.sde[0], sdeHi = sde * ind.sde[1];
  const sdeMid = (sdeLo + sdeHi) / 2;
  const revLo = revenue * ind.rev[0], revHi = revenue * ind.rev[1];
  const revMid = (revLo + revHi) / 2;

  // blend profit-based and revenue-based; profit-led for small operators
  const haveSde = sde > 0, haveRev = revenue > 0;
  const likely = haveSde && haveRev ? sdeMid * 0.65 + revMid * 0.35 : (haveSde ? sdeMid : revMid);
  const low = Math.min(sdeLo || Infinity, revLo || Infinity);
  const high = Math.max(sdeHi || 0, revHi || 0);
  const effMultiple = sde > 0 ? likely / sde : null;
  const profitMargin = revenue > 0 ? (sde / revenue) * 100 : null;

  let headline;
  if (!haveSde && !haveRev) headline = 'Enter your numbers and I’ll give you a ballpark band.';
  else if (haveSde && profitMargin != null && profitMargin < 10)
    headline = `Around ${_P.money(likely)} — but your ${Math.round(profitMargin)}% margin is dragging it down.`;
  else headline = `A realistic ballpark: ${_P.money(likely)}, with a ${_P.money(low)}–${_P.money(high)} band.`;

  return {
    mode: 'biz', sde, revenue, industry: ind,
    sdeLo, sdeHi, sdeMid, revLo, revHi, revMid,
    low: isFinite(low) ? low : 0, high, likely, effMultiple, profitMargin, headline,
  };
}

// ---- AI takeaway (blunt one-liner + levers) with heuristic fallback --------
function takeawayPrompt(result) {
  if (result.mode === 'job') {
    return `You are a blunt, numbers-first pricing advisor. Given this priced work, write the hard truth in plain words.
DATA: charge basis ${result.basis} (price ${result.price} per ${result.unitLabel}), volume ${result.volume}/month, own hours ${result.hours}/month, monthly cost ${result.monthlyCost}, monthly revenue ${Math.round(result.monthlyRevenue)}, monthly profit ${Math.round(result.monthlyProfit)}, margin ${Math.round(result.marginPct)}%, EFFECTIVE HOURLY ${Math.round(result.effHourly)} (what their own time really earns after costs). A 20% price rise would lift effective hourly to ${Math.round(result.scenarioHourly)}.
Return ONLY JSON: { "verdict": "<one blunt sentence; lead with the effective hourly>", "levers": ["<a specific move>", "<another>", "<another>"] }
Rules: Direct, concrete, kind. Lead with the effective hourly rate — it's the universal number. Use the actual figures. No jargon. If a number isn't given, use a [placeholder] — never invent one.`;
  }
  return `You are a blunt, numbers-first valuation advisor. Given this business, write the hard truth in plain words.
DATA: SDE ${result.sde}, revenue ${result.revenue}, industry ${result.industry.label}, likely value ${Math.round(result.likely)}, band ${Math.round(result.low)}-${Math.round(result.high)}, profit margin ${result.profitMargin == null ? 'n/a' : Math.round(result.profitMargin) + '%'}.
Return ONLY JSON: { "verdict": "<one blunt sentence about what really drives this valuation>", "levers": ["<a specific move to raise value>", "<another>", "<another>"] }
Rules: Direct, concrete, kind. Remind them this is a ballpark, not an appraisal. Use the actual numbers. No invented figures — use [placeholder] if needed.`;
}

function parseTakeaway(raw) {
  const o = _P.parseJSONLoose(raw);
  if (!o || !o.verdict || !Array.isArray(o.levers)) return null;
  return { verdict: String(o.verdict), levers: o.levers.filter(Boolean).map(String).slice(0, 3) };
}

function heuristicTakeaway(r) {
  if (r.mode === 'job') {
    const perUnit = r.basis === 'hour' ? '' : ` (${_P.moneyFull(r.profitPerUnit)} per ${r.unitLabel})`;
    if (r.flag === 'losing') return {
      verdict: `You're losing ${_P.moneyFull(-r.monthlyProfit)} a month — this is costing you money to run.`,
      levers: [`Your costs (${_P.moneyFull(r.monthlyCost)}/mo) outrun revenue (${_P.moneyFull(r.monthlyRevenue)}/mo) — raise price or cut cost now.`,
               'Drop or rework the offer if the math can’t turn positive.',
               'Never discount a line that already loses money.'],
    };
    if (r.flag === 'under') return {
      verdict: `Your time is really earning ${_P.moneyFull(r.effHourly)}/hr after costs${perUnit} — thinner than it looks.`,
      levers: [`A 20% raise (to ${_P.moneyFull(r.scenarioPrice)} per ${r.unitLabel}) lifts your time to ${_P.moneyFull(r.scenarioHourly)}/hr.`,
               'Raise new clients first; grandfather existing ones for a month.',
               'Sell the outcome, not the hours — it breaks the time-for-money ceiling.'],
    };
    return {
      verdict: `Solid — your time earns ${_P.moneyFull(r.effHourly)}/hr after costs, at a ${Math.round(r.marginPct)}% margin.`,
      levers: ['Hold the line; don’t discount for price shoppers.',
               `Test a tier 20–30% above this — it’d push you toward ${_P.moneyFull(r.scenarioHourly)}/hr.`,
               'Track effective hourly per client — protect the high ones, fire the low ones.'],
    };
  }
  const lev = [];
  if (r.profitMargin != null && r.profitMargin < 15) lev.push('Lift margin before chasing revenue — profit is what buyers pay for.');
  lev.push('Document the business so it runs without you — that alone raises the multiple.');
  lev.push('Reduce customer concentration; no single client should be a make-or-break.');
  return {
    verdict: `A ballpark of ${_P.money(r.likely)} — not an appraisal. Profit, not revenue, moves this number most.`,
    levers: lev.slice(0, 3),
  };
}

window.CST_PRICE = {
  JOB_STEPS, BIZ_STEPS, INDUSTRIES, priceJob, valueBusiness,
  takeawayPrompt, parseTakeaway, heuristicTakeaway,
};
