/* ============================================================
   IMTRACK — Budget module (WP&B waterfall + IO view)
   ============================================================ */
(function () {
  const E = window.IMTRACK_ENGINE;
  const { useState, useMemo, useEffect } = React;

  function num(v, opts) { return <Money v={v} dash={opts && opts.dash} />; }

  /* ---------------- WP&B waterfall ---------------- */
  function WpbView({ year, fxType, displayCurrency }) {
    const ctx = window.useStore();
    const { store, navigate } = ctx;
    const tree = useMemo(() => E.waterfallTree(store, year), [store, year]);
    const disp = (v) => displayCurrency === 'USD' ? v : E.convertFromUsd(store, v, displayCurrency, year);
    const [openLine, setOpenLine] = useState(() => { const o = {}; store.wpbLines.forEach((l) => { o[l.schedule + l.line] = true; }); return o; });
    const [openItem, setOpenItem] = useState({});

    useEffect(() => {
      const p = JSON.parse(localStorage.getItem('imtrack_nav') || '{}').params || {};
      if (p.focus) setOpenItem((o) => ({ ...o, [p.focus]: true }));
    }, []);

    const ItemDrill = ({ row }) => {
      const afs = row.afops;
      return (
        <tr><td colSpan={10} className="drill"><div className="drill-inner">
          <div className="muted mb8" style={{ fontSize: 11, textTransform: 'uppercase', letterSpacing: '.04em' }}>AFOP lines funding “{row.item.name}”</div>
          {afs.length === 0 ? <span className="muted">No AFOP lines linked yet.</span> : (
            <table className="tbl tbl-compact" style={{ border: '1px solid var(--border-default)', borderRadius: 6 }}>
              <thead><tr><th>Contract</th><th>AFOP Line</th><th>IO</th><th className="num-col">AFOP Amount</th><th></th></tr></thead>
              <tbody>{afs.map((a) => { const c = store.contracts.find((x) => x.id === a.contractId); const io = store.ios.find((x) => x.id === a.ioId); return (
                <tr key={a.id}><td className="mono">{a.contractNo}</td><td>{a.desc}</td><td className="mono" style={{ fontSize: 12 }}>{io ? io.io : '—'}</td>
                <td className="num-col"><Money v={a.amt} /></td>
                <td className="col-actions"><span className="link" onClick={() => navigate('contract', { id: a.contractId, tab: 'afop' })}>Open ↗</span></td></tr>
              ); })}</tbody>
            </table>
          )}
        </div></td></tr>
      );
    };

    const Cols = ({ t, light }) => (<>
      <td className="num-col">{num(disp(t.approved))}</td>
      <td className="num-col">{num(disp(t.totalAfop))}</td>
      <td className="num-col">{num(disp(t.headroom))}</td>
      <td className="num-col">{num(disp(t.poCommitted))}</td>
      <td className="num-col">{num(disp(t.acConsumed), { dash: true })}</td>
      <td className="num-col">{num(disp(t.remainingPo))}</td>
      <td className="num-col tnum">{E.fmtPct(t.pct)}</td>
      <td style={{ textAlign: 'center' }}>{light ? <TrafficLight light={light} label={false} /> : null}</td>
    </>);

    return (
      <div className="card" style={{ overflow: 'hidden' }}>
        <div className="tbl-wrap">
          <table className="tbl">
            <thead style="position: sticky; top: 0; z-index: 1; background: var(--bg-default)"><tr>
              <th style={{ width: 30 }}></th><th>Description</th>
              <th className="num-col">Approved (USD)</th><th className="num-col">AFOP Alloc</th>
              <th className="num-col">Headroom</th><th className="num-col">PO Committed</th>
              <th className="num-col">AC Consumed</th><th className="num-col">Remaining PO</th>
              <th className="num-col">% Used</th><th style={{ width: 56, textAlign: 'center' }}>Status</th>
            </tr></thead>
            <tbody>
              {tree.map((sch) => (
                <React.Fragment key={sch.schedule.code}>
                  <tr className="wf-schedule">
                    <td></td><td className="mono">{sch.schedule.code} · {sch.schedule.name}</td>
                    <Cols t={sch.totals} />
                  </tr>
                  {sch.lines.map((ln) => {
                    const key = ln.line.schedule + ln.line.line;
                    const isOpen = openLine[key];
                    const lt = ln.totals; const _wpbCfg = (store.cockpitActionsConfig || []).find((c) => c.id === 'cac_wpb_budget_risk'); const _wpbWarn = (_wpbCfg && _wpbCfg.warningThreshold != null) ? _wpbCfg.warningThreshold : 75; const lLight = lt.totalAfop > lt.approved ? 'critical' : lt.pct >= _wpbWarn ? 'warning' : 'green';
                    return (
                      <React.Fragment key={key}>
                        <tr className="wf-line wf-toggle" onClick={() => setOpenLine((o) => ({ ...o, [key]: !isOpen }))}>
                          <td><span className={'caret' + (isOpen ? ' open' : '')}><Icon name="chevron-right" size={14} /></span></td>
                          <td className="mono">Line {ln.line.line} · {ln.line.name}</td>
                          <Cols t={lt} light={lLight} />
                        </tr>
                        {isOpen && ln.rows.map((row) => {
                          const io = openItem[row.item.id];
                          return (
                            <React.Fragment key={row.item.id}>
                              <tr className="wf-item clickable" onClick={() => setOpenItem((o) => ({ ...o, [row.item.id]: !io }))}>
                                <td><span className={'caret' + (io ? ' open' : '')}><Icon name="chevron-right" size={13} /></span></td>
                                <td>{row.item.name}</td>
                                <td className="num-col">{num(row.item.approved)}</td>
                                <td className="num-col">{num(row.totalAfop)}</td>
                                <td className="num-col">{num(row.headroom)}</td>
                                <td className="num-col">{num(row.poCommitted)}</td>
                                <td className="num-col">{num(row.acConsumed, { dash: true })}</td>
                                <td className="num-col">{num(row.remainingPo)}</td>
                                <td className="num-col tnum">{E.fmtPct(row.pct)}</td>
                                <td style={{ textAlign: 'center' }}><TrafficLight light={row.light} label={false} size="sq" /></td>
                              </tr>
                              {io && <ItemDrill row={row} />}
                            </React.Fragment>
                          );
                        })}
                      </React.Fragment>
                    );
                  })}
                </React.Fragment>
              ))}
            </tbody>
          </table>
        </div>
      </div>
    );
  }

  /* ---------------- IO view ---------------- */
  function IoView({ year, fxType, displayCurrency }) {
    const ctx = window.useStore();
    const { store, navigate } = ctx;
    const rows = useMemo(() => store.ios.filter((io) => io.year === year).map((io) => E.ioRow(store, io, year)), [store, year]);
    const [open, setOpen] = useState({});
    const fx = store.fxRates.find((r) => r.year === year && r.pair === 'USD/IDR' && r.type === fxType) || store.fxRates.find((r) => r.pair === 'USD/IDR');
    const disp = (v) => displayCurrency === 'USD' ? v : E.convertFromUsd(store, v, displayCurrency, year);
    return (
      <div className="card" style={{ overflow: 'hidden' }}>
        <div className="tbl-wrap">
          <table className="tbl">
            <thead style="position: sticky; top: 0; z-index: 1; background: var(--bg-default)"><tr>
              <th style={{ width: 30 }}></th><th>Internal Order</th><th>Activities</th>
              <th className="num-col">Budget (USD)</th><th className="num-col">Budget (IDR)</th>
              <th className="num-col">AFOP Planned</th><th className="num-col">AC Actual</th>
              <th className="num-col">Variance</th><th className="num-col">% Used</th><th style={{ width: 56, textAlign: 'center' }}>Status</th>
            </tr></thead>
            <tbody>
              {rows.map((r) => {
                const isOpen = open[r.io.id];
                return (
                  <React.Fragment key={r.io.id}>
                    <tr className="clickable" onClick={() => setOpen((o) => ({ ...o, [r.io.id]: !isOpen }))}>
                      <td><span className={'caret' + (isOpen ? ' open' : '')}><Icon name="chevron-right" size={14} /></span></td>
                      <td className="mono" style={{ fontWeight: 600 }}>{r.io.io}</td>
                      <td>{r.io.activities}</td>
                      <td className="num-col"><Money v={disp(r.io.usd)} /></td>
                      <td className="num-col"><span className="num">{E.fmtMoney(displayCurrency === 'USD' ? E.convertFromUsd(store, r.io.usd, 'IDR', year) : r.io.idr, 'IDR')}</span></td>
                      <td className="num-col"><Money v={disp(r.totalAfop)} /></td>
                      <td className="num-col"><Money v={disp(r.acActual)} dash /></td>
                      <td className="num-col"><Money v={disp(r.variance)} /></td>
                      <td className="num-col tnum">{E.fmtPct(r.pct)}</td>
                      <td style={{ textAlign: 'center' }}><TrafficLight light={r.light} label={false} size="sq" /></td>
                    </tr>
                    {isOpen && (
                      <tr><td colSpan={10} className="drill"><div className="drill-inner">
                        <div className="muted mb8" style={{ fontSize: 11, textTransform: 'uppercase', letterSpacing: '.04em' }}>AFOP lines mapped to IO {r.io.io} · {r.remainPct.toFixed(1)}% remaining</div>
                        {r.afops.length === 0 ? <span className="muted">No AFOP lines linked.</span> : (
                          <table className="tbl tbl-compact" style={{ border: '1px solid var(--border-default)', borderRadius: 6 }}>
                            <thead><tr><th>Contract</th><th>AFOP Line</th><th>WP&amp;B</th><th className="num-col">AFOP Amount</th><th></th></tr></thead>
                            <tbody>{r.afops.map((a) => { const it = store.wpbItems.find((w) => w.id === a.wId); return (
                              <tr key={a.id}><td className="mono">{a.contractNo}</td><td>{a.desc}</td><td className="mono" style={{ fontSize: 12 }}>{it ? it.schedule + '-' + it.line : '—'}</td>
                              <td className="num-col"><Money v={a.amt} /></td><td className="col-actions"><span className="link" onClick={() => navigate('contract', { id: a.contractId, tab: 'afop' })}>Open ↗</span></td></tr>
                            ); })}</tbody>
                          </table>
                        )}
                      </div></td></tr>
                    )}
                  </React.Fragment>
                );
              })}
            </tbody>
          </table>
        </div>
        <div className="table-meta"><span>{rows.length} internal orders</span><span>IDR shown at {fxType} rate · USD/IDR {fx ? fx.rate.toLocaleString() : '—'} (FY{year})</span></div>
      </div>
    );
  }

  /* ---------------- IO↔WP&B Mappings View ---------------- */
  function IoWpbMappingsView({ year }) {
    const ctx = window.useStore();
    const { store } = ctx;
    const toast = window.useToast();
    const [newIoId, setNewIoId] = useState('');
    const [newWId, setNewWId] = useState('');
    const mappings = (store.ioWpbMappings || []).filter((m) => !m.year || m.year === year);
    const yearIos = store.ios.filter((x) => x.year === year);
    const yearWpb = store.wpbItems; // WP&B currently not per-year, all shown

    const add = () => {
      if (!newIoId || !newWId) { toast('Select both IO and WP&B', 'warn'); return; }
      const dup = mappings.find((m) => m.ioId === newIoId && m.wId === newWId && m.year === year);
      if (dup) { toast('This pairing already exists for FY' + year, 'warn'); return; }
      ctx.actions.addIoWpbMapping({ id: 'iowpb' + Date.now(), ioId: newIoId, wId: newWId, year, createdAt: new Date().toISOString(), createdBy: ctx.currentUser?.id || 'u1' });
      setNewIoId(''); setNewWId('');
      toast('Mapping added for FY' + year + ' — available for AFOP auto-fill', 'good');
    };

    return (
      <div className="card" style={{ overflow: 'hidden' }}>
        <div className="card-head">
          <span className="card-title">IO ↔ WP&B Mappings · FY{year}</span>
          <div className="row" style={{ gap: 8 }}>
            <select className="select" style={{ height: 30, fontSize: 12, width: 220 }} value={newIoId} onChange={(e) => setNewIoId(e.target.value)}>
              <option value="">Select IO…</option>
              {yearIos.map((x) => <option key={x.id} value={x.id}>{x.io + ' · ' + x.activities}</option>)}
            </select>
            <select className="select" style={{ height: 30, fontSize: 12, width: 200 }} value={newWId} onChange={(e) => setNewWId(e.target.value)}>
              <option value="">Select WP&B…</option>
              {yearWpb.map((w) => <option key={w.id} value={w.id}>{w.name}</option>)}
            </select>
            <button className="btn btn-primary btn-sm" onClick={add}><Icon name="plus" size={14} />Add Mapping</button>
          </div>
        </div>
        <div className="tbl-wrap">
          <table className="tbl tbl-compact">
            <thead><tr><th>IO</th><th>WP&B Line</th><th>Year</th><th>Created</th><th></th></tr></thead>
            <tbody>
              {mappings.map((m) => {
                const io = store.ios.find((x) => x.id === m.ioId);
                const it = store.wpbItems.find((w) => w.id === m.wId);
                return (
                  <tr key={m.id}>
                    <td className="mono" style={{ fontSize: 12 }}>{io ? io.io + ' · ' + io.activities : m.ioId}</td>
                    <td style={{ fontSize: 12 }}>{it ? it.name : m.wId}</td>
                    <td className="mono">{m.year || '—'}</td>
                    <td className="muted" style={{ fontSize: 11 }}>{E.fmtDateNice(m.createdAt)}</td>
                    <td className="col-actions">
                      <button className="icon-btn danger" title="Remove mapping" onClick={() => { ctx.actions.removeIoWpbMapping(m.id); toast('Mapping removed', 'info'); }}><Icon name="trash-2" size={13} /></button>
                    </td>
                  </tr>
                );
              })}
              {mappings.length === 0 && <tr><td colSpan={5} className="muted" style={{ textAlign: 'center', padding: 24 }}>No mappings for FY{year}. Add pairings above, or assign IO+WP&B on AFOP lines in Contracts.</td></tr>}
            </tbody>
          </table>
        </div>
        <div className="table-meta"><span>{mappings.length} mappings for FY{year}</span><span>Pairings here are available for auto-fill in AFOP tab (H3). Each year has independent IO budgets.</span></div>
      </div>
    );
  }

  /* ---------------- Budget shell ---------------- */
  function BudgetModule() {
    const ctx = window.useStore();
    const { store } = ctx;
    const toast = window.useToast();
    const [tab, setTab] = useState(() => (JSON.parse(localStorage.getItem('imtrack_nav') || '{}').params || {}).tab || 'wpb');
    const [year, setYear] = useState(E.CY);
    const [fxType, setFxType] = useState('Regulatory Authority');
    const [displayCurrency, setDisplayCurrency] = useState('USD');
    const currencies = ['USD', ...new Set((store.lookups.currency || []).filter((c) => c.active && c.id !== 'USD').map((c) => c.id))];

    const doExport = () => {
      if (tab === 'wpb') {
        const tree = E.waterfallTree(store, year); const out = [];
        tree.forEach((sch) => sch.lines.forEach((ln) => ln.rows.forEach((r) => out.push([sch.schedule.code, 'Line ' + ln.line.line, r.item.name, r.item.approved, r.totalAfop, r.headroom, r.poCommitted, r.acConsumed, r.remainingPo, r.pct.toFixed(1)]))));
        window.exportCsv('imtrack_wpb_budget_' + year + '.csv', ['BS Schedule', 'BS Line', 'WP&B Description', 'Approved USD', 'AFOP Allocated', 'Headroom', 'PO Committed', 'AC Consumed', 'Remaining PO', '% Used'], out);
      } else {
        const rows = store.ios.filter((io) => io.year === year).map((io) => E.ioRow(store, io, year));
        window.exportCsv('imtrack_io_budget_' + year + '.csv', ['IO', 'Activities', 'Budget USD', 'Budget IDR', 'AFOP Planned', 'AC Actual', 'Variance', '% Used'], rows.map((r) => [r.io.io, r.io.activities, r.io.usd, r.io.idr, r.totalAfop, r.acActual, r.variance, r.pct.toFixed(1)]));
      }
      toast('Budget exported to CSV', 'good');
    };

    return (
      <div className="page page-wide">
        <PageHeader title="Budget" subtitle={'Waterfall: WP&B → AFOP → PO → AC · FY' + year}>
          <button className="btn btn-secondary" onClick={doExport}><Icon name="download" size={16} />Export CSV</button>
        </PageHeader>
        <div className="filterbar" style={{ position: 'sticky', top: 0, zIndex: 10, background: 'var(--bg-page)', paddingBottom: 8 }}>
          <div className="subtabs">
            <button className={'subtab' + (tab === 'wpb' ? ' active' : '')} onClick={() => setTab('wpb')}>WP&amp;B View</button>
            <button className={'subtab' + (tab === 'io' ? ' active' : '')} onClick={() => setTab('io')}>IO View</button>
            <button className={'subtab' + (tab === 'mappings' ? ' active' : '')} onClick={() => setTab('mappings')}>Mappings</button>
          </div>
          <div className="spacer" />
          {tab === 'io' && <select className="select" value={fxType} onChange={(e) => setFxType(e.target.value)}><option>Regulatory Authority</option><option>Internal Planning</option></select>}
          <select className="select" value={displayCurrency} onChange={(e) => setDisplayCurrency(e.target.value)} style={{ width: 100 }}>{currencies.map((c) => <option key={c} value={c}>{c}</option>)}</select>
          <select className="select" value={String(year)} onChange={(e) => setYear(Number(e.target.value))}>{[...new Set(store.fxRates.map((r) => r.year))].sort().map((y) => <option key={y} value={String(y)}>FY{y}</option>)}</select>
        </div>
        {(() => {
          const yearAfops = store.afopLines.filter((a) => !a.deletedAt);
          const yearPos = store.poLines.filter((p) => !p.deletedAt && p.year === year);
          const planningTotal = yearAfops.reduce((s, a) => s + a.amt, 0);
          const reconciledTotal = yearPos.reduce((s, p) => s + p.amt, 0);
          const delta = planningTotal - reconciledTotal;
          return (<div className="card card-pad mb16" style={{ background: delta !== 0 ? 'var(--color-amber-50)' : 'var(--color-green-50)', border: '1px solid ' + (delta !== 0 ? 'var(--color-amber-200)' : 'var(--color-green-200)') }}>
            <div className="row" style={{ gap: 24, flexWrap: 'wrap', fontSize: 'var(--text-sm)', alignItems: 'center' }}>
              <div><span className="muted">Planning (AFOP): </span><span className="num" style={{ fontWeight: 600 }}>USD {E.fmtUSD0(planningTotal)}</span></div>
              <div><span className="muted">Reconciled (PO): </span><span className="num" style={{ fontWeight: 600 }}>USD {E.fmtUSD0(reconciledTotal)}</span></div>
              <div><span className="muted">Delta: </span><span className="num" style={{ fontWeight: 600, color: delta !== 0 ? 'var(--color-warning-700)' : 'var(--color-green-700)' }}>{delta > 0 ? '+' : ''}{E.fmtUSD0(delta)}</span></div>
              <span className="muted" style={{ marginLeft: 'auto', fontSize: 11 }}>{delta === 0 ? '✓ Matches' : '⚠ Differs — save reconciliation in AFOP tab'}</span>
            </div>
          </div>);
        })()}
        {(() => { const wpbLegendCfg = (store.cockpitActionsConfig || []).find((c) => c.id === 'cac_wpb_budget_risk'); const wpbWarnLegend = (wpbLegendCfg && wpbLegendCfg.warningThreshold != null) ? wpbLegendCfg.warningThreshold : 75; return (
        <div className="card card-pad mb16" style={{ background: 'var(--color-navy-50)', border: '1px solid var(--color-navy-100)', position: 'sticky', top: 48, zIndex: 9 }}>
          <div className="row" style={{ gap: 18, flexWrap: 'wrap', fontSize: 'var(--text-sm)' }}>
            <span className="row" style={{ gap: 6 }}><TrafficLight light="green" label={false} /> On track</span>
            <span className="row" style={{ gap: 6 }}><TrafficLight light="warning" label={false} /> ≥ {wpbWarnLegend}% consumed (warning)</span>
            <span className="row" style={{ gap: 6 }}><TrafficLight light="critical" label={false} /> AFOP exceeds approved budget (overrun)</span>
            <span className="muted" style={{ marginLeft: 'auto', fontSize: 12 }}>Tip: set an AC to <strong>05 Done</strong> in AC Log to watch consumption flow up here.</span>
          </div>
        </div>
        ); })()}
        {tab === 'wpb' && <WpbView year={year} fxType={fxType} displayCurrency={displayCurrency} />}
        {tab === 'io' && <IoView year={year} fxType={fxType} displayCurrency={displayCurrency} />}
        {tab === 'mappings' && <IoWpbMappingsView year={year} />}
      </div>
    );
  }

  window.BudgetModule = BudgetModule;
})();
