/* ══ OM Group ERP — Analysis Studio · Drivers & Root Cause ════════════════
   "What changed, and who changed it." A price × volume × mix bridge on
   revenue, the four-lever bridge on net profit, and a ranked contribution
   list per customer and material — every row drillable to the underlying
   ERP records via the existing drill stack.                              */
const { useState: dvSt, useMemo: dvMemo } = React;

function StBridge({ bridge, onRow }) {
  const [open, setOpen] = dvSt(null);
  const max = Math.max.apply(null, bridge.parts.map(p => Math.abs(p.value)).concat([1]));
  return (
    <div className="st-bridge">
      {bridge.parts.map((p, i) => {
        const w = Math.abs(p.value) / max * 48;
        const pos = p.value >= 0;
        const col = pos ? '#16A34A' : '#DC2626';
        return (
          <React.Fragment key={i}>
            <div className="st-br-row" onClick={() => setOpen(open === i ? null : i)}>
              <div className="st-br-l">{p.label}</div>
              <div className="st-br-track">
                <div className="st-br-bar" style={{ left: pos ? '50%' : (50 - w) + '%', width: w + '%', background: col, opacity: .88 }}></div>
              </div>
              <div className="st-br-v" style={{ color: col }}>{(pos ? '+' : '−') + window.IC.short(Math.abs(p.value))}</div>
            </div>
            {open === i && <div className="st-br-note">{p.note}</div>}
          </React.Fragment>
        );
      })}
      <div style={{ display: 'flex', justifyContent: 'space-between', gap: 10, marginTop: 8, paddingTop: 9, borderTop: '1px solid var(--iLine)', fontSize: 11 }}>
        <span style={{ color: 'var(--iInk2)', fontWeight: 600 }}>{window.IC.short(bridge.from)} → <b style={{ color: 'var(--iInk)', fontWeight: 800 }}>{window.IC.short(bridge.to)}</b></span>
        <span style={{ fontWeight: 800, color: bridge.to >= bridge.from ? '#15803D' : '#B91C1C' }}>
          {(bridge.to >= bridge.from ? '+' : '−') + window.IC.short(Math.abs(bridge.to - bridge.from))}
        </span>
      </div>
    </div>
  );
}

function StContrib({ rows, title, sub, ctx, dim }) {
  const list = rows.filter(r => Math.abs(r.delta) > 0).slice(0, 10);
  const max = Math.max.apply(null, list.map(r => Math.abs(r.delta)).concat([1]));
  if (!list.length) return <StEmpty title="No movement" msg="Nothing in this dimension changed between the two windows." />;
  return (
    <div className="ic-rank">
      {list.map((r, i) => {
        const col = r.delta >= 0 ? '#16A34A' : '#DC2626';
        return (
          <div className="ic-rank-row click" key={r.label + i} style={{ animationDelay: (i * 42) + 'ms' }}
            onClick={() => ctx.push({ kind: 'explore', label: r.label, period: ctx.period, cf: Object.assign({}, ctx.cf, dim === 'customer' ? { customerId: r.id } : { materialId: r.id }) })}>
            <div className="ic-rank-i">{i + 1}</div>
            <div className="ic-rank-main">
              <div className="ic-rank-top">
                <span className="ic-rank-lbl">{r.label}</span>
                <span className="ic-rank-val" style={{ color: col }}>{(r.delta >= 0 ? '+' : '−') + window.IC.short(Math.abs(r.delta))}</span>
              </div>
              <div className="ic-rank-track"><div className="ic-rank-fill" style={{ width: (Math.abs(r.delta) / max * 100) + '%', background: col }}></div></div>
              <div className="ic-rank-sub"><span>{window.IC.short(r.then)} → {window.IC.short(r.now)}</span><span className="ic-rank-right">{r.then === 0 ? 'new' : r.now === 0 ? 'stopped' : (r.delta / r.then * 100).toFixed(0) + '%'}</span></div>
            </div>
          </div>
        );
      })}
    </div>
  );
}

function StudioDrivers({ ctx }) {
  const DEng = window.DecisionEngine;
  const dv = dvMemo(() => DEng.drivers(ctx), [ctx]);
  const [kpi, setKpi] = dvSt(null);

  if (!dv) return (
    <StEmpty title="Pick a comparison window"
      msg="Driver analysis is arithmetic between two periods. Choose anything in the Compare control above and the bridges build themselves." />
  );

  /* root-cause explorer: every metric with a delta, ranked by contribution */
  const rootRows = dvMemo(() => {
    const keys = window.IntelEngine.metricList(ctx.mode);
    return keys.map(k => {
      const m = window.IntelEngine.METRICS[k];
      const a = ctx.cur[k], b = ctx.prev[k];
      return { key: k, label: m.label, fmt: m.fmt, good: m.good, cur: a, prev: b, delta: a - b, pct: b ? (a - b) / Math.abs(b) * 100 : (a ? 100 : 0) };
    }).filter(r => Math.abs(r.pct) > 0.05).sort((a, b) => Math.abs(b.pct) - Math.abs(a.pct));
  }, [ctx]);

  return (
    <div>
      <p className="st-lede">
        {ctx.period.label} against {ctx.cmpPeriod.label}. Revenue moved{' '}
        <b>{(dv.delta >= 0 ? '+' : '−') + window.IC.short(Math.abs(dv.delta))} ({dv.deltaPct >= 0 ? '+' : ''}{dv.deltaPct.toFixed(1)}%)</b>.
        The bridges below split that arithmetically — rate, volume and mix are separated so no effect is counted twice.
      </p>

      <div className="i-grid i-g2">
        {dv.bridges.map(b => (
          <div className="i-card" key={b.key}>
            <div className="i-card-hd"><div><div className="i-card-t">{b.label} bridge</div><div className="i-card-s">Tap any row for the arithmetic behind it</div></div></div>
            <StBridge bridge={b} />
          </div>
        ))}
      </div>

      <StSec title="Who moved it" sub="The same delta, attributed to the entities that produced it. Every row opens the underlying transactions.">
        <div className="i-grid i-g2">
          <div className="i-card">
            <div className="i-card-hd"><div><div className="i-card-t">By {ctx.mode === 'purchase' ? 'vendor' : 'customer'}</div><div className="i-card-s">Largest swings first, including accounts that started or stopped</div></div></div>
            <StContrib rows={dv.contrib} ctx={ctx} dim="customer" />
          </div>
          <div className="i-card">
            <div className="i-card-hd"><div><div className="i-card-t">By material</div><div className="i-card-s">Grade-level contribution to the same movement</div></div></div>
            <StContrib rows={dv.matContrib} ctx={ctx} dim="material" />
          </div>
        </div>
      </StSec>

      <StSec title="Root cause explorer" sub="Every metric that moved, ranked by magnitude. Select one to see what sits underneath it.">
        <div className="i-grid i-g23">
          <div className="i-card" style={{ overflowX: 'auto' }}>
            <table className="i-cmp">
              <thead><tr><th>Metric</th><th>{ctx.cmpPeriod.label}</th><th>{ctx.period.label}</th><th>Change</th><th></th></tr></thead>
              <tbody>
                {rootRows.map(r => {
                  const better = r.good === 'down' ? r.delta < 0 : r.delta > 0;
                  const col = r.good === 'flat' ? '#6B7068' : better ? '#15803D' : '#B91C1C';
                  return (
                    <tr key={r.key} onClick={() => setKpi(r)} style={kpi && kpi.key === r.key ? { background: 'color-mix(in oklab,var(--iAccent) 6%,#fff)' } : null}>
                      <td>{r.label}</td>
                      <td>{window.IC.fmt(r.prev, r.fmt)}</td>
                      <td>{window.IC.fmt(r.cur, r.fmt)}</td>
                      <td style={{ color: col }}>{(r.pct >= 0 ? '+' : '') + r.pct.toFixed(1)}%</td>
                      <td style={{ width: 90 }}><div className="i-cmp-bar"><i style={{ width: Math.min(100, Math.abs(r.pct)) + '%', background: col }}></i></div></td>
                    </tr>
                  );
                })}
              </tbody>
            </table>
          </div>
          <div className="i-card">
            {!kpi ? <StEmpty title="Select a metric" msg="Pick any row on the left. The panel resolves what drove it, which entities are responsible and where the underlying records live." />
              : <RootCause r={kpi} ctx={ctx} dv={dv} />}
          </div>
        </div>
      </StSec>
    </div>
  );
}

function RootCause({ r, ctx, dv }) {
  const IEr = window.IntelEngine;
  const c = ctx.cur, p = ctx.prev;
  const reasons = dvMemo(() => {
    const out = [];
    if (r.key === 'revenue' || r.key === 'salesRevenue') {
      dv.bridges[0].parts.slice().sort((a, b) => Math.abs(b.value) - Math.abs(a.value)).forEach(x => out.push(x.label + ' contributed ' + (x.value >= 0 ? '+' : '−') + window.IC.short(Math.abs(x.value)) + '. ' + x.note));
    } else if (r.key === 'grossProfit' || r.key === 'netProfit' || r.key === 'margin') {
      dv.bridges[1].parts.slice().sort((a, b) => Math.abs(b.value) - Math.abs(a.value)).forEach(x => out.push(x.label + ' contributed ' + (x.value >= 0 ? '+' : '−') + window.IC.short(Math.abs(x.value)) + '. ' + x.note));
    } else if (r.key === 'asp' || r.key === 'app') {
      out.push('Realised rate moved ' + window.IC.fmt(r.prev, 'rate') + ' → ' + window.IC.fmt(r.cur, 'rate') + ' per tonne across ' + (ctx.mode === 'purchase' ? ctx.ds.purchases.length : ctx.ds.sales.length) + ' records in scope.');
      out.push('At the earlier volume that rate change alone is worth ' + window.IC.short((r.cur - r.prev) * (ctx.mode === 'purchase' ? p.qtyPurchased : p.qtySold)) + '.');
      out.push('Mix matters here: a shift toward a higher-rate grade raises the average without any single rate changing. The material contribution list above shows whether that happened.');
    } else if (r.key === 'recovery' || r.key === 'dieselCost' || r.key === 'dieselMargin') {
      out.push('Diesel issued moved ' + window.IC.short(p.dieselCost) + ' → ' + window.IC.short(c.dieselCost) + ' across ' + ctx.ds.diesel.length + ' diesel records in scope.');
      out.push('Recovery rate moved ' + p.recoveryRate.toFixed(1) + '% → ' + c.recoveryRate.toFixed(1) + '%, leaving ' + window.IC.short(c.pendingRecovery) + ' open.');
      out.push('Diesel margin is the spread between the issue rate and the purchase rate; it moves with volume and with the rate gap independently.');
    } else if (r.key === 'transportCost') {
      out.push('Freight moved ' + window.IC.short(p.transportCost) + ' → ' + window.IC.short(c.transportCost) + ' across ' + ctx.ds.transport.length + ' transport entries.');
      out.push('Per tonne that is ' + window.IC.fmt(p.qtySold ? p.transportCost / p.qtySold : 0, 'rate') + ' → ' + window.IC.fmt(c.qtySold ? c.transportCost / c.qtySold : 0, 'rate') + '.');
      out.push('Average load moved ' + p.avgLoad.toFixed(1) + ' → ' + c.avgLoad.toFixed(1) + ' T per trip; freight billed per trip amplifies any drop in load.');
    } else if (r.key === 'receivable' || r.key === 'payable') {
      out.push(window.IC.short(r.cur) + ' outstanding against ' + window.IC.short(c.revenue) + ' billed — implied ' + window.DecisionEngine.dso(ctx).toFixed(0) + ' days.');
      out.push(ctx.ds.sales.filter(o => o.status === 'Pending').length + ' of ' + ctx.ds.sales.length + ' sales orders in scope are still pending.');
    } else {
      out.push(r.label + ' moved ' + window.IC.fmt(r.prev, r.fmt) + ' → ' + window.IC.fmt(r.cur, r.fmt) + ' (' + (r.pct >= 0 ? '+' : '') + r.pct.toFixed(1) + '%).');
      out.push('It is derived from ' + (ctx.ds.sales.length + ctx.ds.purchases.length) + ' live records in scope; the entity lists above show which of them moved most.');
    }
    return out;
  }, [r, ctx, dv]);

  const top = (r.key === 'revenue' || r.key === 'salesRevenue' || r.key === 'qtySold' || r.key === 'orders') ? dv.contrib : dv.matContrib;

  return (
    <div>
      <div className="i-card-hd"><div><div className="i-card-t">{r.label}</div>
        <div className="i-card-s">{window.IC.fmt(r.prev, r.fmt)} → {window.IC.fmt(r.cur, r.fmt)} · {(r.pct >= 0 ? '+' : '') + r.pct.toFixed(1)}%</div></div></div>
      <ul className="st-mod-why" style={{ borderTop: 0, marginTop: 0, paddingTop: 0, '--mc': 'var(--iAccent)' }}>
        {reasons.map((x, i) => <li key={i}>{x}</li>)}
      </ul>
      <div style={{ marginTop: 12, paddingTop: 10, borderTop: '1px dashed var(--iLine)' }}>
        <div className="st-lev-grp" style={{ marginTop: 0 }}>Top contributors</div>
        <div className="i-dl">
          {top.slice(0, 5).map((x, i) => (
            <div className="i-dl-row" key={i} onClick={() => ctx.push({ kind: 'explore', label: x.label, period: ctx.period, cf: Object.assign({}, ctx.cf, top === dv.contrib ? { customerId: x.id } : { materialId: x.id }) })}>
              <b>{x.label}</b>
              <i style={{ color: x.delta >= 0 ? '#15803D' : '#B91C1C' }}>{(x.delta >= 0 ? '+' : '−') + window.IC.short(Math.abs(x.delta))}</i>
              <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M9 6l6 6-6 6" /></svg>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { StudioDrivers, StBridge, StContrib, RootCause });
