/* ══ OM Group ERP — Analysis Studio · Forecast Laboratory ═════════════════
   Several independent models over the same live series, side by side, each
   stating its own reasoning, confidence, variance and the records it used.
   No black boxes: every card explains itself in plain English.           */
const { useState: flSt, useMemo: flMemo, useEffect: flEf } = React;

const FL_METRICS = {
  sales: ['revenue', 'grossProfit', 'netProfit', 'qtySold', 'orders', 'asp', 'margin'],
  purchase: ['purchaseValue', 'qtyPurchased', 'app', 'pos', 'dieselCost', 'transportCost'],
};

function StudioForecast({ ctx }) {
  const DEng = window.DecisionEngine;
  const IEf = window.IntelEngine;
  const opts = FL_METRICS[ctx.mode].filter(k => IEf.METRICS[k]);
  const [metric, setMetric] = flSt(opts[0]);
  const [horizon, setHorizon] = flSt(3);
  const [off, setOff] = flSt({});
  const [openWhy, setOpenWhy] = flSt(null);
  flEf(() => { setMetric(FL_METRICS[ctx.mode][0]); }, [ctx.mode]);

  const meta = IEf.METRICS[metric] || IEf.METRICS.revenue;
  const season = ctx.ser.grain === 'month' ? 12 : ctx.ser.grain === 'cycle' ? 24 : ctx.ser.grain === 'quarter' ? 4 : ctx.ser.grain === 'week' ? 52 : 0;
  const lab = flMemo(() => DEng.forecastModels(ctx.ser.points, horizon, { valueKey: metric, season }),
    [ctx, metric, horizon, season]);

  const shown = lab.ready ? lab.models.filter(m => !off[m.id]) : [];
  const expected = lab.ready ? lab.models.find(m => m.id === 'expected') : null;
  const lo = lab.ready ? lab.models.find(m => m.id === 'conservative') : null;
  const hi = lab.ready ? lab.models.find(m => m.id === 'aggressive') : null;

  const chart = flMemo(() => {
    if (!lab.ready) return null;
    const n = lab.y.length, h = horizon;
    const labels = lab.labels.concat(Array.from({ length: h }, (_, i) => '+' + (i + 1)));
    const pad = (arr, before) => Array.from({ length: n + h }, (_, i) => i < before ? null : (i === before - 1 ? lab.y[n - 1] : arr[i - n] != null ? arr[i - n] : null));
    const series = [{ key: 'hist', label: 'Actual', color: window.OM_CHART_COLORS.historical, width: 2.6, values: lab.y.concat(Array.from({ length: h }, () => null)), area: false }];
    shown.filter(m => !m.envelope).forEach(m => {
      const v = Array.from({ length: n + h }, (_, i) => i < n - 1 ? null : i === n - 1 ? lab.y[n - 1] : m.values[i - n]);
      series.push({ key: m.id, label: m.label, color: m.color, dashed: m.id !== 'expected', width: m.id === 'expected' ? 2.8 : 1.9, values: v });
    });
    let band = null;
    if (lo && hi && !off.conservative && !off.aggressive) {
      band = {
        color: '#F97316',
        lo: Array.from({ length: n + h }, (_, i) => i < n - 1 ? null : i === n - 1 ? lab.y[n - 1] : lo.values[i - n]),
        hi: Array.from({ length: n + h }, (_, i) => i < n - 1 ? null : i === n - 1 ? lab.y[n - 1] : hi.values[i - n]),
      };
    }
    return { labels, series, band, splitAt: n };
  }, [lab, horizon, JSON.stringify(off)]);

  if (!lab.ready) return (
    <StEmpty title="Not enough history yet"
      msg={'Forecasting needs at least two buckets in scope. Widen the period or switch the grain — the models rebuild automatically as soon as the data supports them.'} />
  );

  const total = expected ? expected.values.reduce((s, v) => s + v, 0) : 0;
  const lastActual = lab.y[lab.y.length - 1];

  return (
    <div>
      <p className="st-lede">
        {lab.models.filter(m => !m.envelope).length} independent models run over the same <b>{lab.n} live {ctx.ser.grain} buckets</b>, projected {horizon} forward.
        Each card states what it assumes, what it used and how confident it is — read the disagreement between them as the real uncertainty.
      </p>

      <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', alignItems: 'center', marginBottom: 12 }}>
        <div style={{ display: 'flex', gap: 4, flexWrap: 'wrap' }}>
          {opts.map(o => <button key={o} className={'i-ctl sm' + (metric === o ? ' on' : '')} onClick={() => setMetric(o)}>{IEf.METRICS[o].label}</button>)}
        </div>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginLeft: 'auto' }}>
          <span style={{ fontSize: 9.5, fontWeight: 800, letterSpacing: '.08em', textTransform: 'uppercase', color: 'var(--iInk3)' }}>Horizon</span>
          {[1, 2, 3, 6, 9, 12].map(h => <button key={h} className={'i-ctl sm' + (horizon === h ? ' on' : '')} onClick={() => setHorizon(h)}>{h}</button>)}
        </div>
      </div>

      <div className="i-card" style={{ marginBottom: 10 }}>
        <div className="i-card-hd">
          <div><div className="i-card-t">{meta.label} — actual and projected</div>
            <div className="i-card-s">{lab.n} actual {ctx.ser.grain} buckets · {horizon} projected · shaded band is the full model envelope</div></div>
          <div style={{ display: 'flex', gap: 10, flexWrap: 'wrap', alignItems: 'center' }}>
            {shown.filter(m => !m.envelope).map(m => (
              <span key={m.id} style={{ display: 'inline-flex', alignItems: 'center', gap: 5, fontSize: 10, fontWeight: 700, color: 'var(--iInk2)' }}>
                <i style={{ width: 9, height: 2.5, borderRadius: 2, background: m.color, display: 'inline-block' }}></i>{m.label}
              </span>
            ))}
          </div>
        </div>
        <StPlot height={330} labels={chart.labels} series={chart.series} band={chart.band} splitAt={chart.splitAt} yKind={meta.fmt === 'cur' ? 'cur' : meta.fmt} cursor="default" />
      </div>

      {expected && (
        <div className="st-res" style={{ marginBottom: 12 }}>
          <StRes label={'Next ' + ctx.ser.grain} value={expected.values[0]} base={lastActual} kind={meta.fmt} color="#F97316" good="up" note={'vs last actual ' + window.IC.fmt(lastActual, meta.fmt)} />
          <StRes label={'Horizon total (' + horizon + ')'} value={total} kind={meta.fmt} color="#2563EB" note="consensus, summed" />
          <StRes label="Conservative" value={lo ? lo.values[0] : 0} kind={meta.fmt} color="#DC2626" note="downside boundary" />
          <StRes label="Aggressive" value={hi ? hi.values[0] : 0} kind={meta.fmt} color="#16A34A" note="upside boundary" />
          <StRes label="Consensus confidence" value={expected.confidence} kind="int" color="#7C3AED" note={'models disagree by ' + window.IC.short(expected.variance * 2)} />
        </div>
      )}

      <StSec title="The models" sub="Tap a card to switch it off the chart. Tap “Why” to read exactly how it reached its number and which records influenced it.">
        <div className="i-grid i-g3">
          {lab.models.map(m => {
            const isOff = !!off[m.id];
            return (
              <div key={m.id} className={'st-mod' + (isOff ? ' off' : ' on')} style={{ '--mc': m.color }}
                onClick={() => setOff(p => Object.assign({}, p, { [m.id]: !p[m.id] }))}>
                <div className="st-mod-hd">
                  <span className="st-mod-sw"></span>
                  <span className="st-mod-n">{m.label}</span>
                  <StTag level={m.risk === 'low' ? 'lo' : m.risk === 'medium' ? 'md' : 'hi'}>{m.risk} risk</StTag>
                </div>
                <div className="st-mod-v">{window.IC.fmt(m.values[0], meta.fmt)}</div>
                <div className="st-mod-m">
                  <span style={{ fontSize: 9.5, fontWeight: 800, color: 'var(--iInk3)' }}>{m.confidence}%</span>
                  <span className="st-conf"><i style={{ width: m.confidence + '%' }}></i></span>
                  <span style={{ fontSize: 9.5, fontWeight: 600, color: 'var(--iInk3)' }}>±{window.IC.short(m.variance)}</span>
                </div>
                <div style={{ fontSize: 10, color: 'var(--iInk3)', fontWeight: 600, marginTop: 6 }}>{m.basis}</div>
                <button className="i-crumb" style={{ marginTop: 9, padding: '3px 9px', fontSize: 9.5 }}
                  onClick={e => { e.stopPropagation(); setOpenWhy(openWhy === m.id ? null : m.id); }}>{openWhy === m.id ? 'Hide reasoning' : 'Why this number'}</button>
                {openWhy === m.id && <ul className="st-mod-why">{m.why.map((r, i) => <li key={i}>{r}</li>)}</ul>}
              </div>
            );
          })}
        </div>
      </StSec>

      <StSec title="Bucket-by-bucket projection" sub="The full table behind the chart — every model, every projected bucket. Exportable straight into a plan.">
        <div className="i-card" style={{ overflowX: 'auto' }}>
          <table className="i-dl-tbl" style={{ minWidth: 520 }}>
            <thead>
              <tr><th>Model</th>{Array.from({ length: horizon }, (_, i) => <th className="r" key={i}>+{i + 1}</th>)}<th className="r">Total</th><th className="r">Conf.</th></tr>
            </thead>
            <tbody>
              {lab.models.map(m => (
                <tr key={m.id} style={{ opacity: off[m.id] ? 0.45 : 1 }}>
                  <td><span style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}><i style={{ width: 8, height: 8, borderRadius: 2, background: m.color }}></i>{m.label}</span></td>
                  {m.values.map((v, i) => <td className="r" key={i}>{window.IC.fmt(v, meta.fmt)}</td>)}
                  <td className="r" style={{ fontWeight: 800 }}>{window.IC.fmt(m.values.reduce((s, v) => s + v, 0), meta.fmt)}</td>
                  <td className="r">{m.confidence}%</td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </StSec>
    </div>
  );
}

Object.assign(window, { StudioForecast });
