/* Shared atoms + helpers for the FourJaw ROI calculator UI */

const { useState, useEffect, useRef, useMemo } = React;

// ============================================================
// Formatters
// ============================================================
const fmtGbp = (n, opts = {}) => {
  if (n == null || !isFinite(n)) return "—";
  const { decimals = 0, compact = false } = opts;
  const rounded = Math.round(n);
  if (compact && Math.abs(n) >= 1000) {
    if (Math.abs(n) >= 1_000_000) return "£" + (n / 1_000_000).toFixed(2) + "m";
    if (Math.abs(n) >= 100_000)   return "£" + Math.round(n / 1000) + "k";
    return "£" + (n / 1000).toFixed(1) + "k";
  }
  return "£" + rounded.toLocaleString("en-GB");
};
const fmtNum = (n, decimals = 0) => {
  if (n == null || !isFinite(n)) return "—";
  return Number(n).toLocaleString("en-GB", { maximumFractionDigits: decimals, minimumFractionDigits: decimals });
};
const fmtPct = (n, decimals = 0) => {
  if (n == null || !isFinite(n)) return "—";
  return Number(n).toLocaleString("en-GB", { maximumFractionDigits: decimals, minimumFractionDigits: decimals }) + "%";
};
const fmtMonths = (n) => {
  if (n == null || !isFinite(n)) return "—";
  return n.toFixed(1);
};

// ============================================================
// Sector lookup tables (for labels + icons)
// ============================================================
const SECTOR_META = {
  subcon_cnc:    { name: "Subcontract CNC",    desc: "Mixed-batch machine shops, contract milling/turning.", icon: "cogs.svg" },
  aerospace:     { name: "Aerospace",          desc: "Tier-1/2 component manufacture, tight QA regimes.",    icon: "robot-hand.svg" },
  automotive:    { name: "Automotive",         desc: "Series production, tier suppliers, line manufacture.", icon: "conveyor-belt.svg" },
  food_beverage: { name: "Food & Beverage",    desc: "Process lines, packaging, FMCG run rates.",            icon: "ramen.svg" },
  metals:        { name: "Metals",             desc: "Forging, casting, rolling, heat treatment.",           icon: "hammer.svg" },
  medical:       { name: "Medical Devices",    desc: "Precision component manufacture, validated processes.",icon: "shield.svg" },
  plastics:      { name: "Plastics",           desc: "Injection moulding, extrusion, mould tooling.",        icon: "shelves.svg" },
};

const DRIVER_META = {
  capacityUplift:     { label: "Capacity uplift",        sub: "Higher utilisation → more sellable hours" },
  capexDeferral:      { label: "Capex deferral",         sub: "Postpone or avoid a new-machine purchase" },
  downtimeRecovery:   { label: "Downtime recovery",      sub: "Reduce unplanned stops" },
  energySavings:      { label: "Energy savings",         sub: "Cut idle-shift consumption" },
  qualityReduction:   { label: "Quality (indirect)",     sub: "Surface conditions that correlate with scrap" },
  labourRedeployment: { label: "Labour redeployment",    sub: "Eliminate manual data collection" },
  otdAvoidance:       { label: "OTD penalty avoidance",  sub: "Avoid late-delivery penalties / contract risk" },
};

// Purple scale (light → dark) for stacked driver bar
const DRIVER_COLORS = ["#E8E0FF", "#C8B8FF", "#A698F2", "#8975EE", "#7053EA", "#563BC7", "#4731A8"];

// ============================================================
// NumberWithExplainer — wraps any £/number with click-to-explain
// ============================================================
function NumberWithExplainer({ children, driverKey, label, engineOutput, anchor = "bottom" }) {
  const [open, setOpen] = useState(false);
  const ref = useRef(null);
  return (
    <>
      <span
        ref={ref}
        className="num-explain"
        onClick={(e) => { e.stopPropagation(); setOpen(true); }}
        role="button"
        tabIndex={0}
        aria-label={`Explain ${label || driverKey}`}
        onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); setOpen(true); } }}
      >
        {children}
      </span>
      {open && (
        <ExplainerPopover
          driverKey={driverKey}
          label={label}
          engineOutput={engineOutput}
          anchorRef={ref}
          anchor={anchor}
          onClose={() => setOpen(false)}
        />
      )}
    </>
  );
}

// ============================================================
// ExplainerPopover — positioned popover with formula + plain-English
// (Streams content from window.claude.complete; falls back to canned
//  formula breakdown if the call fails or is unavailable.)
// ============================================================
function ExplainerPopover({ driverKey, label, engineOutput, anchorRef, anchor, onClose }) {
  const [text, setText] = useState("");
  const [loading, setLoading] = useState(true);
  const [showFormula, setShowFormula] = useState(false);
  const [pos, setPos] = useState({ top: 0, left: 0 });
  const popRef = useRef(null);

  // Position the popover near the anchor
  useEffect(() => {
    if (!anchorRef?.current) return;
    const r = anchorRef.current.getBoundingClientRect();
    const cont = anchorRef.current.closest(".artboard-content, body");
    const cr = cont ? cont.getBoundingClientRect() : { top: 0, left: 0 };
    const popW = 360;
    let left = r.left - cr.left;
    let top = anchor === "bottom" ? r.bottom - cr.top + 8 : r.top - cr.top - 8;
    // shift left if overflowing
    if (cont) {
      const maxLeft = cont.clientWidth - popW - 16;
      if (left > maxLeft) left = maxLeft;
    }
    setPos({ top, left });
  }, [anchorRef, anchor]);

  // Dismiss on outside click
  useEffect(() => {
    const onDown = (e) => {
      if (popRef.current && !popRef.current.contains(e.target)) onClose();
    };
    const onKey = (e) => { if (e.key === "Escape") onClose(); };
    setTimeout(() => document.addEventListener("mousedown", onDown), 0);
    document.addEventListener("keydown", onKey);
    return () => {
      document.removeEventListener("mousedown", onDown);
      document.removeEventListener("keydown", onKey);
    };
  }, [onClose]);

  // Fetch explanation
  useEffect(() => {
    let cancelled = false;
    const detail = engineOutput?.driverDetail?.[driverKey] ?? null;
    const value = engineOutput?.driverValues?.[driverKey] ?? null;

    async function fetchExplain() {
      try {
        if (!window.claude?.complete) throw new Error("no claude");
        const driverHuman = DRIVER_META[driverKey]?.label || driverKey;
        const prompt = `You are explaining one line of a FourJaw ROI calculation to a UK manufacturer. Be sober, technical, no marketing language. 80-110 words. No headings. Use British English. Use ONLY the figures provided below — do not estimate or recompute.

Driver: ${driverHuman}
Value this year: £${Math.round(value).toLocaleString("en-GB")}
Scenario: ${engineOutput.scenario}
Driver detail (JSON): ${JSON.stringify(detail, null, 2)}

Explain what this number is, the rough formula in plain English (then the actual values plugged in), and which input would move it most. ${driverKey === "qualityReduction" ? "Acknowledge FourJaw doesn't directly prevent scrap — it surfaces conditions that correlate with it." : ""}${driverKey === "downtimeRecovery" ? " Mention the overlapFactor — a guardrail to avoid double-counting with utilisation uplift." : ""}`;
        const r = await window.claude.complete(prompt);
        if (!cancelled) { setText(r); setLoading(false); }
      } catch (e) {
        if (!cancelled) {
          setText(fallbackExplanation(driverKey, engineOutput));
          setLoading(false);
        }
      }
    }
    fetchExplain();
    return () => { cancelled = true; };
  }, [driverKey, engineOutput]);

  const detail = engineOutput?.driverDetail?.[driverKey];
  const value = engineOutput?.driverValues?.[driverKey];
  const meta = DRIVER_META[driverKey] || { label: label || driverKey };

  return (
    <div className="popover-overlay" style={{ top: pos.top, left: pos.left }}>
      <div className="popover" ref={popRef} role="dialog" aria-label="Number explanation">
        <div className="popover-eyebrow">{meta.sub || "Driver explanation"}</div>
        <h4>{meta.label}</h4>
        <div className="popover-value">{fmtGbp(value)} <span style={{ fontSize: "13px", color: "var(--fg-2)", letterSpacing: 0, fontWeight: 400 }}>/ year</span></div>

        {loading ? (
          <div>
            <span className="skel w90" />
            <span className="skel w70" />
            <span className="skel w90" />
            <span className="skel w50" />
          </div>
        ) : (
          <p>{text}</p>
        )}

        {detail && (
          <>
            <button className="btn link" onClick={() => setShowFormula((v) => !v)} style={{ fontSize: 12 }}>
              {showFormula ? "− Hide formula" : "+ Show formula"}
            </button>
            {showFormula && <FormulaBlock driverKey={driverKey} detail={detail} />}
          </>
        )}

        <div className="ftr">
          <span>Engine spec v{engineOutput.specVersion}</span>
          <span>{engineOutput.scenario} scenario</span>
        </div>
      </div>
    </div>
  );
}

function FormulaBlock({ driverKey, detail }) {
  const fmt = (v) => typeof v === "number"
    ? (Math.abs(v) > 100 ? Math.round(v).toLocaleString("en-GB") : v.toFixed(2))
    : String(v);
  const rows = Object.entries(detail || {}).filter(([k, v]) => v != null && typeof v !== "object");
  return (
    <div className="formula">
      {rows.map(([k, v]) => (
        <div key={k}><strong>{k}</strong> = {fmt(v)}</div>
      ))}
    </div>
  );
}

function fallbackExplanation(driverKey, engineOutput) {
  const d = engineOutput.driverDetail?.[driverKey];
  if (!d) return "We couldn't generate a plain-English explanation just now. The raw working from the engine is shown below — every number traces back to the inputs you provided.";
  switch (driverKey) {
    case "capacityUplift":
      return `Recovered ${Math.round(d.recoveredHours).toLocaleString("en-GB")} machine-hours from a ${d.upliftPp} percentage-point utilisation uplift (the ${engineOutput.scenario} assumption). Each recovered hour is valued at the contribution margin per hour. Demand factor ${d.demandFactor} reflects whether those hours can be sold. The biggest input lever is your contribution margin per hour.`;
    case "downtimeRecovery":
      return `${Math.round(d.recoveredHours).toLocaleString("en-GB")} hrs of unplanned downtime recovered at ${d.downtimeReductionPct}% reduction, valued at contribution margin + operator labour. The ${d.overlapFactor.toFixed(2)} overlap factor strips the portion already counted in the capacity uplift line — a guardrail against double-counting.`;
    case "energySavings":
      return `Annual energy cost £${Math.round(d.annualEnergyCost).toLocaleString("en-GB")} × ${d.energyWasteReductionPct}% reduction. Computed over all available hours (not utilised hours) because the saving comes from idle/off-shift behaviour, not from cutting more.`;
    case "qualityReduction":
      return `Estimated rework cost £${Math.round(d.reworkCost).toLocaleString("en-GB")} × ${d.scrapReductionPct}% reduction. Indirect lever: FourJaw doesn't prevent scrap directly — it surfaces conditions that correlate with it. Treat this as the smallest of the levers.`;
    case "labourRedeployment":
      return `${Math.round(d.manualDataHoursPerYear).toLocaleString("en-GB")} hrs/yr of manual data collection × ${d.redeploymentPct}% redeployed at supervisor rate £${d.supervisorLabourCostPerHour}/hr. Scales with √(machine count / 10).`;
    case "capexDeferral":
      return `Planned machine price £${Math.round(d.price).toLocaleString("en-GB")} × ${(d.annualisedCostOfOwnership*100).toFixed(0)}% annualised cost of ownership × ${d.deferralYears} years deferral, amortised over the 3-year model period.`;
    case "otdAvoidance":
      return `${fmtGbp(d.penaltyValue)} from a ${d.otdImprovementPct}% reduction in OTD penalty exposure, plus ${fmtGbp(d.retentionValue)} from retaining ${d.contractRetentionUplift}% more of at-risk contract value.`;
    default:
      return "Engine output shown below.";
  }
}

// ============================================================
// ScenarioToggle
// ============================================================
function ScenarioToggle({ value, onChange, dark }) {
  const opts = [
    { v: "conservative", label: "Conservative" },
    { v: "realistic",    label: "Realistic" },
    { v: "aspirational", label: "Aspirational" },
  ];
  return (
    <div className="scenarios" role="tablist" aria-label="ROI scenario">
      {opts.map((o) => (
        <button
          key={o.v}
          role="tab"
          aria-selected={value === o.v}
          className={value === o.v ? "active" : ""}
          onClick={() => onChange(o.v)}
        >
          <span className="tick" />
          {o.label}
        </button>
      ))}
    </div>
  );
}

// ============================================================
// AppliedDefaults — expandable list of what the engine defaulted
// ============================================================
const APPLIED_DEFAULT_LABELS = {
  hoursPerShift:                  "Hours per shift",
  daysPerWeek:                    "Days per week",
  weeksPerYear:                   "Weeks per year",
  siteCount:                      "Number of sites",
  scenario:                       "Scenario",
  currentUtilisationPct:          "Current utilisation",
  contributionMarginPerHour:      "Contribution margin / hr",
  "contributionMarginPerHour (derived via chargeOutRate × haircut)": "Margin (derived from charge-out × sector haircut)",
  electricityCostPerKwh:          "Electricity cost / kWh",
  machineKw:                      "Machine power draw",
  scrapRatePct:                   "Scrap rate",
  manualDataHoursPerWeek:         "Manual data collection / week",
  supervisorLabourCostPerHour:    "Supervisor labour rate",
  operatorLabourCostPerHour:      "Operator labour rate",
  unplannedDowntimePct:           "Unplanned downtime %",
  term:                           "Subscription term",
  internalImplHours:              "Internal implementation hrs",
  internalRatePerHour:            "Internal rate / hr",
};

function AppliedDefaults({ engineOutput, defaultOpen = false }) {
  const r = engineOutput.resolvedInputs;
  const defaults = engineOutput.appliedDefaults || [];

  const valueFor = (key) => {
    const base = key.split(" ")[0]; // strip the "(derived…)" suffix
    const v = r[base];
    if (v == null) return "—";
    if (base === "term" || base === "scenario") return v;
    if (base.endsWith("Pct")) return `${typeof v === "number" ? v.toFixed(1) : v}%`;
    if (base === "unplannedDowntimePct") return `${(v * 100).toFixed(1)}%`;
    if (base.includes("Cost") || base.includes("Margin") || base.includes("Rate")) return `£${typeof v === "number" ? v.toFixed(2) : v}`;
    if (base.includes("Hours") || base === "hoursPerShift" || base === "manualDataHoursPerWeek") {
      return typeof v === "number" ? `${v.toFixed(1)} hrs` : v;
    }
    if (typeof v === "number") return Math.round(v).toLocaleString("en-GB");
    return String(v);
  };

  return (
    <details className="defaults" open={defaultOpen}>
      <summary>
        <span className="lbl">We've assumed the following — tap to review &amp; refine</span>
        <span className="count">{defaults.length} defaults</span>
        <span className="chev" />
      </summary>
      <div className="defaults-body">
        {defaults.map((d) => (
          <div className="def" key={d}>
            <div className="k">
              <div>{APPLIED_DEFAULT_LABELS[d] || d}</div>
              <code style={{ fontSize: 11, color: "var(--fg-3)" }}>{d}</code>
            </div>
            <div className="v">{valueFor(d)}</div>
          </div>
        ))}
        {defaults.length === 0 && (
          <div style={{ fontSize: 13, color: "var(--fg-2)", padding: "8px 0" }}>
            No defaults applied — every value was supplied directly.
          </div>
        )}
      </div>
    </details>
  );
}

// ============================================================
// useUrlEncodedState — keep one state object in the URL as base64 JSON
// (No-op in artboard contexts; included for parity with the brief.)
// ============================================================
function useUrlEncodedState(initial) {
  return useState(initial);
}

// Expose globally for other Babel files
Object.assign(window, {
  fmtGbp, fmtNum, fmtPct, fmtMonths,
  SECTOR_META, DRIVER_META, DRIVER_COLORS,
  NumberWithExplainer, ExplainerPopover, ScenarioToggle, AppliedDefaults,
  useUrlEncodedState,
});
