// ============================================================
// DEMIURGE — SAVINGS CALCULATOR
//
// The site claims operators pay for themselves. This lets a visitor check that
// against their own business instead of taking it on faith. Every input is set
// by the visitor and every assumption is on screen — including the one that
// decides the answer, how much of the lost work operators actually recover.
//
// Money figures come from DGX_PRICING (pricing.jsx), which is the single
// source of truth for published numbers — so this cannot drift from /packages
// and /full. It models the real engagement: a setup fee scoped to the build
// (never a flat number, so never asserted here), then either a managed
// retainer or self-run with no monthly fee to us.
//
// The output is deliberately laid out as three labelled steps — what you lose
// today, what changes, what you keep — because the earlier single-column
// ledger led with the LOSS as the biggest gold number on the page and read as
// though it were a gain.
// ============================================================

const calcMoney = (n) =>
  new Intl.NumberFormat('en-GB', {
    style: 'currency',
    currency: 'GBP',
    maximumFractionDigits: 0
  }).format(Math.max(0, Math.round(n)));

// '£1,500' → 1500. Falls back only if pricing.jsx is not on the page.
const calcNum = (s, fallback) => {
  const n = Number(String(s || '').replace(/[^0-9.]/g, ''));
  return Number.isFinite(n) && n > 0 ? n : fallback;
};

const CALC_INPUTS = [
  {
    key: 'enquiries',
    label: 'Enquiries a month',
    hint: 'Calls, forms, DMs and emails from people who want to buy',
    min: 20, max: 1000, step: 10, initial: 150,
    fmt: (v) => String(v)
  },
  {
    key: 'missed',
    label: 'Share you miss or answer too late',
    hint: 'Rings out, replied to next day, chased once and forgotten',
    min: 0, max: 60, step: 1, initial: 30,
    fmt: (v) => v + '%'
  },
  {
    key: 'value',
    label: 'Average value of a customer',
    hint: 'What one won customer is worth to you, first order or first year',
    min: 100, max: 20000, step: 100, initial: 1200,
    fmt: (v) => calcMoney(v)
  },
  {
    key: 'close',
    label: 'Close rate on enquiries you do reach',
    hint: 'Of the enquiries you answer properly, how many become customers',
    min: 5, max: 60, step: 1, initial: 25,
    fmt: (v) => v + '%'
  },
  {
    key: 'recovery',
    label: 'How much of that the operators win back',
    hint: 'Of the work you currently lose, the share that gets answered and followed up instead',
    min: 20, max: 90, step: 5, initial: 50,
    fmt: (v) => v + '%'
  }
];

// The onboarding & setup fee is a COMPUTED total: each module carries its own
// setup fee and the customer pays the sum for the bundle they pick. Per-module
// fees are never shown — only the total — so this reads one number and never a
// breakdown.
//
// It is a STARTING price, not a quote. The size of the client's system moves it
// and the final figure is agreed on the call, so every sentence here must read
// as a floor ("starts at X"), never as a fixed total ("is X", "one-off of X").
// The page must not let a client tick six modules and hold us to the sum.
//
// The bundle picker lives in pricing.jsx. This reads the total it publishes on
// `window.DGX_SETUP_TOTAL` and re-reads it on the `dgx:setup-total` event, and
// falls back to the starter pack (phone + booking = £600, the figure published
// on /packages) when no picker is on the page. That fallback is deliberate: it
// means this file renders correctly both before and after the picker ships, so
// the two do not need a synchronised deploy.
const STARTER_SETUP = 600;

const readSetupTotal = () => {
  const n = typeof window !== 'undefined' ? Number(window.DGX_SETUP_TOTAL) : NaN;
  return Number.isFinite(n) && n >= 0 ? n : STARTER_SETUP;
};

const SavingsCalculator = () => {
  const pricing = typeof DGX_PRICING !== 'undefined' ? DGX_PRICING : null;
  const managedFrom = calcNum(pricing && pricing.managedFrom, 1500);

  // setupTotal === 0 means a picker IS on the page and nothing is selected —
  // distinguishable from "no picker", which falls back to STARTER_SETUP. An
  // empty selection is an exploration state, not a quote, so the setup sentence
  // is suppressed rather than asserting £0 (meaningless) or £600 (a figure the
  // client did not choose — a silent misquote).
  const [setupTotal, setSetupTotal] = React.useState(readSetupTotal);
  const hasSetup = setupTotal > 0;
  React.useEffect(() => {
    const sync = () => setSetupTotal(readSetupTotal());
    sync(); // the picker may have mounted after this component did
    window.addEventListener('dgx:setup-total', sync);
    return () => window.removeEventListener('dgx:setup-total', sync);
  }, []);

  const [v, setV] = React.useState(() => {
    const s = {};
    CALC_INPUTS.forEach((i) => { s[i.key] = i.initial; });
    return s;
  });
  // How it runs after setup: the managed retainer, or self-run at no monthly
  // fee to us. Mirrors the fork on /packages.
  const [mode, setMode] = React.useState('managed');

  const set = (key) => (e) => {
    const n = Number(e.target.value);
    setV((s) => Object.assign({}, s, { [key]: n }));
  };

  const monthlyCost = mode === 'managed' ? managedFrom : 0;
  const missedEnquiries = v.enquiries * (v.missed / 100);
  const lostPerMonth = missedEnquiries * (v.close / 100) * v.value;
  const recoveredPerMonth = lostPerMonth * (v.recovery / 100);
  const netPerMonth = recoveredPerMonth - monthlyCost;
  const netPerYear = netPerMonth * 12;
  const worthIt = netPerMonth > 0;
  // Exactly zero is neither a gain nor a loss — without this, Self-Run with
  // nothing recovered reads "you would be down £0".
  const breakEven = Math.round(netPerMonth) === 0;
  const recoveredCustomers = missedEnquiries * (v.recovery / 100) * (v.close / 100);

  return (
    <section className="lp-section" id="calculator" data-screen-label="Savings calculator">
      <div className="lp">
        <div className="lp-what-head">
          <div>
            <div className="lp-eyebrow">/ 03 · Run your own numbers</div>
            <h2 className="lp-h2" style={{marginTop: 18}}>
              What is slow follow-up<br/>
              <em className="lp-brass">already costing you</em>?
            </h2>
          </div>
          <p className="lp-lede lp-what-lede">
            Move the sliders to your business. Every assumption is yours to set, including how much
            of the lost work you think operators would actually win back.
          </p>
        </div>

        <div className="lp-calc">
          <div className="corner tl"></div>
          <div className="corner tr"></div>
          <div className="corner bl"></div>
          <div className="corner br"></div>

          <div className="lp-calc-grid">
            <div className="lp-calc-inputs">
              <div className="lp-calc-tag">Your numbers</div>
              {CALC_INPUTS.map((i) => (
                <div className="lp-calc-row" key={i.key}>
                  <label className="lp-calc-lbl" htmlFor={'calc-' + i.key}>
                    <span>{i.label}</span>
                    <output htmlFor={'calc-' + i.key} className="lp-calc-val">{i.fmt(v[i.key])}</output>
                  </label>
                  <input
                    id={'calc-' + i.key}
                    className="lp-calc-slider"
                    type="range"
                    min={i.min}
                    max={i.max}
                    step={i.step}
                    value={v[i.key]}
                    onChange={set(i.key)}
                    aria-valuetext={i.fmt(v[i.key])}
                    aria-describedby={'calc-' + i.key + '-hint'}
                    style={{ '--fill': (((v[i.key] - i.min) / (i.max - i.min)) * 100) + '%' }}
                  />
                  <div className="lp-calc-hint" id={'calc-' + i.key + '-hint'}>{i.hint}</div>
                </div>
              ))}

              <div className="lp-calc-row">
                <div className="lp-calc-lbl"><span>Who runs it once it is built</span></div>
                <div className="lp-calc-plans" role="radiogroup" aria-label="How it runs after setup">
                  <button
                    type="button" role="radio" aria-checked={mode === 'managed'}
                    className={'lp-calc-plan' + (mode === 'managed' ? ' on' : '')}
                    onClick={() => setMode('managed')}
                  >
                    <span className="n">Managed Operations</span>
                    <span className="c">from {calcMoney(managedFrom)}/mo</span>
                  </button>
                  <button
                    type="button" role="radio" aria-checked={mode === 'self'}
                    className={'lp-calc-plan' + (mode === 'self' ? ' on' : '')}
                    onClick={() => setMode('self')}
                  >
                    <span className="n">Self-Run</span>
                    <span className="c">no monthly fee to us</span>
                  </button>
                </div>
              </div>
            </div>

            <div className="lp-calc-out" aria-live="polite">
              <div className="lp-calc-tag">On these numbers</div>

              <div className="lp-calc-step">
                <div className="lp-calc-step-n">1</div>
                <div className="lp-calc-step-body">
                  <div className="lp-calc-step-lbl">What slow follow-up costs you today</div>
                  <div className="lp-calc-step-val loss">
                    &minus;{calcMoney(lostPerMonth)}<span className="per"> a month</span>
                  </div>
                  <div className="lp-calc-step-sub">
                    You get {Math.round(missedEnquiries).toLocaleString()} enquiries a month you never
                    properly answer. At your {v.close}% close rate that is{' '}
                    {(missedEnquiries * (v.close / 100)).toFixed(1)} customers a month you never win —
                    worth {calcMoney(lostPerMonth * 12)} over a year.
                  </div>
                </div>
              </div>

              <div className="lp-calc-step">
                <div className="lp-calc-step-n">2</div>
                <div className="lp-calc-step-body">
                  <div className="lp-calc-step-lbl">What the operators win back for you</div>
                  <div className="lp-calc-step-val gain">
                    +{calcMoney(recoveredPerMonth)}<span className="per"> a month</span>
                  </div>
                  <div className="lp-calc-step-sub">
                    They answer every enquiry and keep chasing it. Recovering {v.recovery}% of what you
                    lose is about {recoveredCustomers.toFixed(1)} extra customers a month.
                  </div>
                </div>
              </div>

              <div className="lp-calc-step">
                <div className="lp-calc-step-n">3</div>
                <div className="lp-calc-step-body">
                  <div className="lp-calc-step-lbl">What you pay us for that</div>
                  <div className="lp-calc-step-val cost">
                    {monthlyCost > 0 ? <>&minus;{calcMoney(monthlyCost)}<span className="per"> a month</span></>
                                     : <>{calcMoney(0)}<span className="per"> a month</span></>}
                  </div>
                  <div className="lp-calc-step-sub">
                    {mode === 'managed'
                      ? <>Managed Operations, from {calcMoney(managedFrom)} a month — we run the stack for you.
                          {hasSetup ? <> Building it starts at {calcMoney(setupTotal)} — the size of your system
                          moves it, and it is fixed in writing before anything starts.</> : null}</>
                      : <>Self-Run — no monthly fee to us. You run the stack yourself and pay only your own
                          platform subscriptions.
                          {hasSetup ? <> Building it starts at {calcMoney(setupTotal)}, scoped on the call.</> : null}</>}
                  </div>
                </div>
              </div>

              <div className={'lp-calc-bottom' + (worthIt || breakEven ? '' : ' negative')}>
                <div className="lp-calc-bottom-lbl">
                  {worthIt ? 'So you would be better off by'
                           : breakEven ? 'So on these numbers you would break even'
                                       : 'So on these numbers you would be down'}
                </div>
                <div className="lp-calc-bottom-val">
                  {netPerMonth < 0 && !breakEven ? '−' : ''}{calcMoney(Math.abs(netPerMonth))}
                  <span className="per"> a month</span>
                </div>
                <div className="lp-calc-bottom-sub">
                  {worthIt
                    ? <>That is <strong>{calcMoney(Math.abs(netPerYear))} a year</strong> you are currently
                        leaving on the table — what the operators win back, minus what you pay us.</>
                    : breakEven
                      ? <>These numbers say you would neither gain nor lose. Move the sliders to your real
                          figures — if you are not losing enquiries, operators have nothing to win back for you.</>
                      : <>What you would pay us is more than these numbers say you would win back. Self-Run may
                          fit better, or this may not be for you yet — book the call and we will tell you
                          straight rather than sell you something that will not pay back.</>}
                </div>
              </div>

              <div className="lp-calc-cta">
                <a href={clarityBookHref('calculator')} className="lp-btn primary">
                  Book a strategy call
                  <svg className="arrow" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" aria-hidden="true" focusable="false"><path d="M5 12h14M13 6l6 6-6 6"/></svg>
                </a>
                <a href="/packages" className="lp-btn">See how pricing works</a>
              </div>
            </div>
          </div>
        </div>

        <p className="lp-calc-note">
          An estimate built from the figures you entered, not a forecast or a promise of results.
          What you actually recover depends on your offer, your market, your data and how the
          operators are set up.
          {hasSetup
            ? <> The monthly figures above cover running the stack only — building it is a separate one-off
                starting at {calcMoney(setupTotal)} for the modules shown, onboarding and integration
                included. That is a starting price, not a quote: the size of your system moves it, and the
                final figure is scoped on the call and fixed in writing before anything starts.</>
            : <> The monthly figures above cover running the stack only; building it is scoped and quoted
                separately once you choose your modules.</>} Excludes VAT.
        </p>
      </div>
    </section>
  );
};

window.SavingsCalculator = SavingsCalculator;
