// financial/src/budget.jsx — the budget builder (M3).
//
// Monarch's budgets document has been empty since its sync query failed
// silently, and a budget built from zero is a wish list. This one is SEEDED
// from the trailing run-rate per canonical category (lib/budgetBuilder over
// lib/spendingTaxonomy), so the first draft is what the household actually
// does; the work is deciding what to change. Variance is then measured
// month over month against the plan, and any category running > 25% above
// its own trailing average is the insight engine's categorySpike.
//
// Stored at spending/budget (the DataProvider's budgetPlan). Rendered at the
// top of the Budget tab; the Monarch budget view stays below it.
//
// Slices run in their own Babel scope; src/shared.jsx runs first and
// publishes window.FinanceShared.

const { useState, useContext, useMemo } = React;
const { DataContext, useToast, fmtCur, fmtDate, timeAgo } = window.FinanceShared;

const Tag = window.FinanceShared.ProvenanceTag;

function BudgetPlanner({ setTab }) {
  const d = useContext(DataContext);
  const { show, Toast } = useToast();
  const ST = window.SpendingTaxonomy, BB = window.BudgetBuilder;
  const [draft, setDraft] = useState(null);
  const [monthSel, setMonthSel] = useState(null);
  const outSign = d.spendingTaxonomy?.outSign ?? (ST && d.transactions.length ? ST.detectOutSign(d.transactions).outSign : -1);
  const monthly = useMemo(() => ST && d.transactions.length ? ST.monthly(d.transactions, { months: 14, mapping: d.spendingTaxonomy?.mapping, outSign }) : [], [ST, d.transactions, d.spendingTaxonomy, outSign]);
  const seed = useMemo(() => BB && monthly.length ? BB.seed({ monthlyRows: monthly, protection: d.spendingTaxonomy?.protection }) : null, [BB, monthly, d.spendingTaxonomy]);
  const plan = d.budgetPlan && d.budgetPlan.categories ? d.budgetPlan : null;
  const variance = useMemo(() => BB && plan && monthly.length ? BB.variance({ plan, monthlyRows: monthly }) : null, [BB, plan, monthly]);
  const spikes = useMemo(() => BB && monthly.length ? BB.spikes({ monthlyRows: monthly }) : null, [BB, monthly]);
  const lastSync = d.monarchAuth && d.monarchAuth.lastSync;
  if (!ST || !BB) return null;
  if (!monthly.length) return (
    <div className="card" style={{ marginBottom: 16 }}>
      <div className="label">Budget builder</div>
      <div style={{ fontSize: 11, color: '#64748b' }}>No transactions synced yet — the builder seeds from the ledger's trailing run-rate and has nothing to read.</div>
    </div>
  );
  const editing = draft || (plan ? { ...plan.categories } : null);
  const save = async (categories, basis) => {
    try {
      await d.saveBudgetPlan({ categories, basis: basis || (plan && plan.basis) || 'edited', seededAt: (plan && plan.seededAt) || new Date().toISOString(), editedAt: new Date().toISOString() });
      setDraft(null); show('Budget saved');
    } catch (e) { show(e.message, 'error'); }
  };
  const shown = variance ? (monthSel ? variance.months.find(m => m.month === monthSel) : variance.latest) : null;
  const total = editing ? Object.values(editing).reduce((s, v) => s + (Number(v) || 0), 0) : 0;
  return (
    <div className="card" style={{ marginBottom: 16, borderColor: 'rgba(59,130,246,0.35)' }}>
      {Toast}
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', gap: 12, flexWrap: 'wrap', marginBottom: 8 }}>
        <div>
          <div className="label" style={{ marginBottom: 2 }}>Budget builder — seeded from what the household actually does</div>
          <div style={{ fontSize: 11, color: '#64748b' }}>Trailing {seed.months}-month run-rate per canonical category ({seed.window ? `${seed.window.from} → ${seed.window.to}` : ''}), non-spending excluded. Variance below is against the saved plan, month over month. <Tag kind="heuristic">transactions via spendingTaxonomy · Monarch sync {lastSync ? timeAgo(lastSync) : 'never'}</Tag></div>
        </div>
        <div style={{ textAlign: 'right' }}>
          <div style={{ fontSize: 22, fontWeight: 800, color: '#3b82f6', lineHeight: 1 }}>{fmtCur(plan ? variance.planTotal : seed.total)}<span style={{ fontSize: 10, color: '#64748b' }}>/mo</span></div>
          <div style={{ fontSize: 9, color: '#64748b', textTransform: 'uppercase', letterSpacing: '0.08em' }}>{plan ? `plan · ${plan.basis}${plan.editedAt ? ` · edited ${fmtDate(plan.editedAt)}` : ''}` : 'suggested from run-rate — not saved yet'}</div>
        </div>
      </div>
      {!plan && (
        <div style={{ background: 'rgba(59,130,246,0.08)', border: '1px solid rgba(59,130,246,0.3)', borderRadius: 8, padding: '8px 12px', fontSize: 11, marginBottom: 10, display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 10, flexWrap: 'wrap' }}>
          <span>No plan saved. The seed below is the run-rate rounded to $10 per category; protected categories (the hobbies) are seeded at their run-rate and never proposed as a cut.</span>
          <button className="btn-primary" style={{ fontSize: 11, padding: '4px 12px' }} onClick={() => save(seed.plan.categories, seed.plan.basis)}>Seed the plan from the run-rate</button>
        </div>
      )}
      <div className="grid-2" style={{ gap: 16, alignItems: 'start' }}>
        <div>
          <div className="label" style={{ marginBottom: 4 }}>{plan ? 'Plan by category (edit and save)' : 'Suggested by category'}</div>
          <table style={{ fontSize: 11 }}>
            <thead><tr><th>Category</th><th style={{ textAlign: 'right' }}>Trailing 12</th><th style={{ textAlign: 'right' }}>Trailing 3</th><th style={{ textAlign: 'right' }}>{plan ? 'Budget' : 'Seed'}</th></tr></thead>
            <tbody>
              {seed.categories.map(c => (
                <tr key={c.key}>
                  <td style={{ padding: '3px 8px', color: '#e2e8f0' }}>{c.label}{c.protected && <span title={c.reason || ''} style={{ fontSize: 9, color: '#10b981', marginLeft: 6 }}>protected</span>}</td>
                  <td style={{ padding: '3px 8px', textAlign: 'right', color: '#94a3b8' }}>{fmtCur(c.trailing12)}</td>
                  <td style={{ padding: '3px 8px', textAlign: 'right', color: c.trend != null && c.trend > 25 ? '#f59e0b' : '#94a3b8' }}>{fmtCur(c.trailing3)}{c.trend != null ? <span style={{ fontSize: 9, color: '#475569' }}> ({c.trend > 0 ? '+' : ''}{c.trend}%)</span> : null}</td>
                  <td style={{ padding: '3px 8px', textAlign: 'right' }}>
                    {plan ? <input type="number" value={editing[c.key] ?? ''} style={{ width: 90, textAlign: 'right', fontSize: 11, padding: '2px 6px' }} onChange={e => setDraft(x => ({ ...(x || { ...plan.categories }), [c.key]: Number(e.target.value) || 0 }))} /> : <span style={{ color: '#e2e8f0' }}>{fmtCur(c.suggested)}</span>}
                  </td>
                </tr>
              ))}
              <tr><td style={{ padding: '4px 8px', color: '#e2e8f0', fontWeight: 600 }}>Total</td><td style={{ padding: '4px 8px', textAlign: 'right' }}>{fmtCur(seed.categories.reduce((s, c) => s + c.trailing12, 0))}</td><td style={{ padding: '4px 8px', textAlign: 'right' }}>{fmtCur(seed.categories.reduce((s, c) => s + c.trailing3, 0))}</td><td style={{ padding: '4px 8px', textAlign: 'right', color: '#e2e8f0', fontWeight: 600 }}>{fmtCur(plan ? total : seed.total)}</td></tr>
            </tbody>
          </table>
          {plan && draft && <div style={{ marginTop: 8, display: 'flex', gap: 6 }}><button className="btn-primary" style={{ fontSize: 10, padding: '3px 10px' }} onClick={() => save(draft, 'edited from the run-rate seed')}>Save plan</button><button className="btn-secondary" style={{ fontSize: 10, padding: '3px 8px' }} onClick={() => setDraft(null)}>Discard</button></div>}
          {plan && !draft && <div style={{ marginTop: 8, display: 'flex', gap: 6 }}><button className="btn-secondary" style={{ fontSize: 10, padding: '3px 10px' }} onClick={() => save(seed.plan.categories, seed.plan.basis)}>Re-seed from the current run-rate</button>
            {variance && <window.FinanceShared.ExportCsv name="budget-variance" rows={variance.months.flatMap(m => m.categories.map(c => ({ month: m.month, category: c.label, budget: c.budget, actual: c.actual, variance: c.variance, variancePct: c.variancePct, momChange: c.mom, over: c.over })))} />}</div>}
        </div>
        <div>
          {variance && shown ? (
            <>
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 8, marginBottom: 4 }}>
                <div className="label" style={{ marginBottom: 0 }}>Variance — {shown.month}</div>
                <select value={shown.month} onChange={e => setMonthSel(e.target.value)} style={{ fontSize: 11, padding: '2px 6px' }}>{variance.months.slice().reverse().map(m => <option key={m.month} value={m.month}>{m.month}</option>)}</select>
              </div>
              <div style={{ fontSize: 11, color: '#94a3b8', marginBottom: 6 }}>
                Actual <strong style={{ color: '#e2e8f0' }}>{fmtCur(shown.actualTotal)}</strong> vs plan {fmtCur(shown.budgetTotal)} → <strong style={{ color: shown.variance > 0 ? '#ef4444' : '#10b981' }}>{shown.variance > 0 ? '+' : ''}{fmtCur(shown.variance)}</strong> ({shown.variancePct == null ? '—' : `${shown.variancePct > 0 ? '+' : ''}${shown.variancePct}%`}); {shown.overCount} categor{shown.overCount === 1 ? 'y' : 'ies'} over{shown.unbudgeted ? `; ${fmtCur(shown.unbudgeted)} spent in categories with no line` : ''}.
              </div>
              <table style={{ fontSize: 11 }}>
                <thead><tr><th>Category</th><th style={{ textAlign: 'right' }}>Budget</th><th style={{ textAlign: 'right' }}>Actual</th><th style={{ textAlign: 'right' }}>Variance</th><th style={{ textAlign: 'right' }}>vs prior month</th></tr></thead>
                <tbody>{shown.categories.filter(c => c.actual || c.budget).slice(0, 14).map(c => (
                  <tr key={c.key}>
                    <td style={{ padding: '3px 8px', color: '#e2e8f0' }}>{c.label}</td>
                    <td style={{ padding: '3px 8px', textAlign: 'right', color: '#94a3b8' }}>{fmtCur(c.budget)}</td>
                    <td style={{ padding: '3px 8px', textAlign: 'right', color: '#e2e8f0' }}>{fmtCur(c.actual)}</td>
                    <td style={{ padding: '3px 8px', textAlign: 'right', color: c.over ? '#ef4444' : '#10b981' }}>{c.variance > 0 ? '+' : ''}{fmtCur(c.variance)}{c.variancePct != null ? <span style={{ fontSize: 9, color: '#475569' }}> ({c.variancePct > 0 ? '+' : ''}{c.variancePct}%)</span> : null}</td>
                    <td style={{ padding: '3px 8px', textAlign: 'right', color: '#94a3b8' }}>{c.mom == null ? '—' : `${c.mom > 0 ? '+' : ''}${fmtCur(c.mom)}`}</td>
                  </tr>))}</tbody>
              </table>
            </>
          ) : <div style={{ fontSize: 11, color: '#64748b' }}>Variance appears once a plan is saved.</div>}
          {spikes && spikes.spikes.length > 0 && (
            <div style={{ marginTop: 10, background: 'rgba(245,158,11,0.06)', border: '1px solid rgba(245,158,11,0.25)', borderRadius: 8, padding: '8px 12px', fontSize: 11 }}>
              <div style={{ color: '#f59e0b', fontWeight: 600 }}>Above trailing average in {spikes.month} (&gt; {spikes.thresholdPct}%)</div>
              {spikes.spikes.map(s => <div key={s.key} style={{ color: '#94a3b8' }}>{s.label}: {fmtCur(s.actual)} vs {fmtCur(s.trailingAvg)} avg over {s.baselineMonths} mo{s.lumpy ? ' (non-zero months)' : ''} → <strong style={{ color: '#e2e8f0' }}>+{fmtCur(s.over)}</strong> ({s.overPct}%)</div>)}
              <div style={{ fontSize: 9, color: '#475569', marginTop: 2 }}>{spikes.source}</div>
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

window.FinanceViews = Object.assign(window.FinanceViews || {}, { BudgetPlanner });
