// financial/src/dashboard.jsx — the Dashboard (home) tab.
//
// Net-worth hero, sync status, data-quality banner, allocation + debt pies,
// monthly cashflow, budget tracker, spend analyzer, accounts + recent
// transactions. Takes setTab so it can deep-link Settings when Monarch is
// not connected. runQualityChecks lives here — Dashboard is its only
// consumer and renders its warnings.
//
// Slices run in their own Babel scope; src/shared.jsx runs first and
// publishes window.FinanceShared.

const { useState, useEffect, useContext, useMemo, useRef, useCallback } = React;
const { ResponsiveContainer, LineChart, Line, BarChart, Bar, AreaChart, Area,
        ComposedChart, PieChart, Pie, Cell, XAxis, YAxis, CartesianGrid,
        Tooltip, Legend, ReferenceLine } = window.Recharts;
const { DataContext, useToast, fmtCur, fmtCurFull, timeAgo,
        useMonarchSync } = window.FinanceShared;

const getCurrentYear = () => new Date().getFullYear();

// Basic quality checks — surfaced as warnings rather than hard errors so
// partial data still renders, but the user sees what's off.
const runQualityChecks = ({ compensation, accounts, netWorthHistory }) => {
  const warnings = [];
  const yr = getCurrentYear();
  if(!compensation?.[yr] && !compensation?.[yr-1]) warnings.push(`No compensation for ${yr} or ${yr-1}`);
  if(compensation?.[yr] && (compensation[yr].baseSalary||0) <= 0) warnings.push(`${yr} baseSalary is missing or zero`);
  if(accounts?.length){
    const ids = new Set(); let dups = 0;
    accounts.forEach(a=>{ if(ids.has(a.id)) dups++; ids.add(a.id); });
    if(dups) warnings.push(`${dups} duplicate account IDs detected`);
  }
  if(netWorthHistory?.length){
    const negatives = netWorthHistory.filter(s => (s.assetsBalance||0) < 0).length;
    if(negatives) warnings.push(`${negatives} net-worth snapshots have negative assets`);
  }
  return warnings;
};

// ── Dashboard ─────────────────────────────────────────────────────────────────
function Dashboard({ setTab }) {
  const { accounts, cashflow, netWorthHistory, transactions, budgets, monarchAuth, reload, loading, compensation } = useContext(DataContext);
  const { show, Toast } = useToast();
  const { syncing, handleSync } = useMonarchSync({ reload, show });
  const hasMonarch = accounts.length > 0;
  const lastSectionErrors = monarchAuth?.lastSectionErrors || [];
  const qcWarnings = useMemo(()=>runQualityChecks({ compensation, accounts, netWorthHistory }),[compensation, accounts, netWorthHistory]);

  const now = new Date();
  const currentMonthKey = `${now.getFullYear()}-${String(now.getMonth()+1).padStart(2,'0')}`;
  const prevMonthKey = (()=>{ const d=new Date(now); d.setMonth(d.getMonth()-1); return `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}`; })();

  const netWorth = useMemo(()=> accounts.reduce((s,a)=> s + (a.currentBalance||0), 0), [accounts]);
  const totalAssets = useMemo(()=> accounts.filter(a=>a.isAsset).reduce((s,a)=>s+(a.currentBalance||0),0), [accounts]);
  const totalDebt = useMemo(()=> Math.abs(accounts.filter(a=>!a.isAsset).reduce((s,a)=>s+(a.currentBalance||0),0)), [accounts]);

  const thisMonth = cashflow?.byMonth?.[cashflow.byMonth.length-1] || null;
  const monthlyIncome = thisMonth?.sumIncome || 0;
  const monthlyExpenses = Math.abs(thisMonth?.sumExpense || 0);
  const savingsRate = monthlyIncome > 0 ? ((monthlyIncome - monthlyExpenses) / monthlyIncome * 100) : 0;
  const monthlySavings = monthlyIncome - monthlyExpenses;

  // Net worth delta (vs 1 month ago). The Monarch snapshot doc has shipped in
  // two shapes over time — split asset/liability (preferred) and net-only
  // (older schema fallback). Read both, then bucket to end-of-month so the
  // chart x-axis doesn't show 30 daily ticks all labelled with the same month.
  const monthlySnapshots = useMemo(() => {
    if (!netWorthHistory?.length) return [];
    const byMonth = new Map();
    const sorted = [...netWorthHistory].sort((a,b) => (a.date||'').localeCompare(b.date||''));
    for (const s of sorted) {
      if (!s?.date) continue;
      const month = s.date.substring(0,7);
      const assets = s.assetsBalance != null ? Number(s.assetsBalance) : null;
      const liabilities = s.liabilitiesBalance != null ? Math.abs(Number(s.liabilitiesBalance)) : null;
      // Older schema: only a single net-worth balance per row.
      const netFromOldShape = (assets == null && liabilities == null && s.balance != null) ? Number(s.balance) : null;
      const net = netFromOldShape != null ? netFromOldShape : ((assets || 0) - (liabilities || 0));
      byMonth.set(month, {
        date: s.date,
        month,
        assets: Math.round(assets || (netFromOldShape || 0)),
        liabilities: Math.round(liabilities || 0),
        net: Math.round(net),
      });
    }
    return [...byMonth.values()];
  }, [netWorthHistory]);
  const nwhData = monthlySnapshots.slice(-24).map(s => ({
    date: s.month,
    assets: s.assets,
    liabilities: s.liabilities,
    net: s.net,
  }));
  const nwDelta = nwhData.length>=2 ? nwhData[nwhData.length-1].net - nwhData[nwhData.length-2].net : 0;
  // Broader net-worth change windows so the dashboard remains insightful even
  // without transaction data (trend is derived purely from netWorthHistory).
  const nwChange = (monthsBack) => {
    if(nwhData.length < monthsBack+1) return null;
    const last = nwhData[nwhData.length-1].net;
    const prev = nwhData[nwhData.length-1-monthsBack].net;
    return { abs:last-prev, pct: prev!==0 ? ((last-prev)/Math.abs(prev))*100 : null };
  };
  const nw12m = nwChange(12);
  // YTD: first monthly snapshot in current calendar year
  const nwYtd = (()=>{
    if(!nwhData.length) return null;
    const yr = String(now.getFullYear());
    const yearStart = monthlySnapshots.find(s => (s.month||'').startsWith(yr));
    if(!yearStart) return null;
    const startNet = yearStart.net;
    const last = nwhData[nwhData.length-1].net;
    return { abs:last-startNet, pct: startNet!==0 ? ((last-startNet)/Math.abs(startNet))*100 : null };
  })();
  const debtToAssetPct = totalAssets>0 ? (totalDebt/totalAssets)*100 : 0;

  // ── Asset allocation (works without transactions) ──────────────────────────
  const allocationData = useMemo(()=>{
    const buckets = { Cash:0, Investments:0, 'Real Estate':0, Crypto:0, Other:0 };
    // Investment accounts under this balance are effectively cash — avoids a
    // handful of near-zero brokerage/HSA stubs inflating the Investments slice.
    const SMALL_INVEST_THRESHOLD = 1000;
    accounts.filter(a=>a.isAsset).forEach(a=>{
      const t = (a.type?.name||'').toLowerCase();
      const st = (a.subtype?.name||a.subtype?.display||'').toLowerCase();
      const bal = a.currentBalance||0;
      const isVehicle = t.includes('vehicle')||st.includes('vehicle')||st.includes('auto')||st.includes('car');
      const isRealEstate = !isVehicle && (t.includes('real')||st.includes('real')||st.includes('property')||st.includes('residence')||st.includes('home'));
      if(t==='checking'||t==='savings'||t==='cash'||st.includes('checking')||st.includes('savings')) buckets.Cash += bal;
      else if(t==='crypto'||st.includes('crypto')) buckets.Crypto += bal;
      else if(t==='investment'||t==='brokerage'||st.includes('brokerage')||st.includes('401')||st.includes('ira')||st.includes('roth')) {
        if (bal > 0 && bal < SMALL_INVEST_THRESHOLD) buckets.Cash += bal;
        else buckets.Investments += bal;
      }
      else if(isRealEstate) buckets['Real Estate'] += bal;
      else if(isVehicle) buckets.Other += bal;
      else buckets.Other += bal;
    });
    const palette = {Cash:'#3b82f6', Investments:'#10b981', 'Real Estate':'#8b5cf6', Crypto:'#f59e0b', Other:'#64748b'};
    return Object.entries(buckets).filter(([,v])=>v>0).map(([name,value])=>({name, value:Math.round(value), fill:palette[name]}));
  },[accounts]);

  const debtBreakdown = useMemo(()=>{
    return accounts.filter(a=>!a.isAsset && (a.currentBalance||0)!==0)
      .map(a=>({
        name: a.displayName || a.name || 'Account',
        value: Math.abs(a.currentBalance||0),
        type: (a.type?.name||'').toLowerCase(),
      }))
      .sort((a,b)=>b.value-a.value)
      .slice(0,8);
  },[accounts]);

  // Sync health: accounts synced but transactions empty → likely a backend
  // failure in the transactions pipeline rather than "no data exists".
  const syncDegraded = monarchAuth?.lastSync && accounts.length>0 && transactions.length===0;

  const cashflowData = (cashflow?.byMonth||[]).slice(-8).map(m=>({
    month: (m.month||'').substring(5,7)+'/'+((m.month||'').substring(2,4)),
    income: Math.round(m.sumIncome||0),
    expenses: Math.round(Math.abs(m.sumExpense||0)),
    savings: Math.round((m.sumIncome||0)-Math.abs(m.sumExpense||0))
  }));

  const acctGroups = useMemo(()=>{
    const g = { Cash:[], Investments:[], Credit:[], Loans:[], Other:[] };
    accounts.forEach(a=>{
      const t = (a.type?.name||'').toLowerCase();
      if(t==='checking'||t==='savings'||t==='cash') g.Cash.push(a);
      else if(t==='investment'||t==='brokerage'||t==='crypto') g.Investments.push(a);
      else if(t==='credit'||t==='credit_card') g.Credit.push(a);
      else if(t==='loan'||t==='mortgage'||t==='auto') g.Loans.push(a);
      else g.Other.push(a);
    });
    return g;
  },[accounts]);

  // Spending by category (this month from transactions)
  const spendByCat = useMemo(()=>{
    const cats = {};
    transactions.filter(t=>{
      const d = new Date(t.date);
      return d.getFullYear()===now.getFullYear() && d.getMonth()===now.getMonth() && t.amount>0 && !t.hideFromReports;
    }).forEach(t=>{ const c=t.category?.name||'Other'; cats[c]=(cats[c]||0)+t.amount; });
    return Object.entries(cats).sort((a,b)=>b[1]-a[1]).slice(0,8).map(([name,val],i)=>({
      name, value:Math.round(val),
      fill:['#10b981','#3b82f6','#f59e0b','#8b5cf6','#ef4444','#06b6d4','#f97316','#ec4899'][i%8]
    }));
  },[transactions, now]);

  const totalSpend = spendByCat.reduce((s,c)=>s+c.value,0);

  // Budget vs actual (current month)
  const budgetItems = useMemo(()=>{
    const bud = budgets?.[currentMonthKey]?.budgetItems || budgets?.[prevMonthKey]?.budgetItems || [];
    return bud.filter(b=>(b.budgetAmount?.amount||0)>0).sort((a,b)=>b.actualAmount?.amount-a.actualAmount?.amount).slice(0,8);
  },[budgets, currentMonthKey, prevMonthKey]);

  const totalBudgeted = budgetItems.reduce((s,b)=>s+(b.budgetAmount?.amount||0),0);
  const totalActual   = budgetItems.reduce((s,b)=>s+(b.actualAmount?.amount||0),0);

  // ── Spend Analyzer ──────────────────────────────────────────────────────────
  const today = now.getDate();
  const daysInMonth = new Date(now.getFullYear(), now.getMonth()+1, 0).getDate();
  const monthPct = today / daysInMonth;

  const thisMonthTx = useMemo(()=>transactions.filter(t=>{
    const d=new Date(t.date);
    return d.getFullYear()===now.getFullYear()&&d.getMonth()===now.getMonth()&&t.amount>0&&!t.hideFromReports;
  }),[transactions]);

  const lastMonthTx = useMemo(()=>transactions.filter(t=>{
    const d=new Date(t.date), lm=new Date(now);
    lm.setMonth(lm.getMonth()-1);
    return d.getFullYear()===lm.getFullYear()&&d.getMonth()===lm.getMonth()&&t.amount>0&&!t.hideFromReports;
  }),[transactions]);

  const totalSpendMTD = thisMonthTx.reduce((s,t)=>s+t.amount,0);
  const projectedSpend = today>0 ? Math.round(totalSpendMTD/today*daysInMonth) : 0;

  const catThisMonth = useMemo(()=>{
    const m={}; thisMonthTx.forEach(t=>{const c=t.category?.name||'Other';m[c]=(m[c]||0)+t.amount;}); return m;
  },[thisMonthTx]);

  const catLastMonth = useMemo(()=>{
    const m={}; lastMonthTx.forEach(t=>{const c=t.category?.name||'Other';m[c]=(m[c]||0)+t.amount;}); return m;
  },[lastMonthTx]);

  // Top merchants this month
  const topMerchants = useMemo(()=>{
    const m={}; thisMonthTx.forEach(t=>{const n=t.merchant?.name||'Unknown';m[n]=(m[n]||0)+t.amount;});
    return Object.entries(m).sort((a,b)=>b[1]-a[1]).slice(0,6).map(([name,amt])=>({name,amt:Math.round(amt)}));
  },[thisMonthTx]);

  // Day-by-day cumulative spend (for velocity chart)
  const velocityData = useMemo(()=>{
    const cumThis=new Array(today).fill(0);
    thisMonthTx.forEach(t=>{const d=new Date(t.date).getDate()-1; if(d<today) cumThis[d]=(cumThis[d]||0)+t.amount;});
    let sum=0; const thisLine=cumThis.map((v,i)=>({day:i+1,this:Math.round(sum+=v)}));

    const lm=new Date(now); lm.setMonth(lm.getMonth()-1);
    const lmDays=new Date(lm.getFullYear(),lm.getMonth()+1,0).getDate();
    const cumLast=new Array(lmDays).fill(0);
    lastMonthTx.forEach(t=>{const d=new Date(t.date).getDate()-1; if(d<lmDays) cumLast[d]=(cumLast[d]||0)+t.amount;});
    let lsum=0; const lastLine=cumLast.map((v,i)=>({day:i+1,last:Math.round(lsum+=v)}));

    const days=Math.max(thisLine.length,lastLine.length);
    return Array.from({length:days},(_,i)=>({
      day:i+1,
      this: thisLine[i]?.this ?? null,
      last: lastLine[i]?.last ?? null,
    }));
  },[thisMonthTx, lastMonthTx, today]);

  // Category MoM comparison (top 6 by this-month spend)
  const catCompare = useMemo(()=>{
    const cats=Object.keys(catThisMonth).sort((a,b)=>catThisMonth[b]-catThisMonth[a]).slice(0,6);
    return cats.map(c=>({name:c,this:Math.round(catThisMonth[c]||0),last:Math.round(catLastMonth[c]||0)}));
  },[catThisMonth, catLastMonth]);

  // Smart insights
  const insights = useMemo(()=>{
    const out=[];
    const burnPct=monthPct*100;
    // Budget burn rate
    if(totalBudgeted>0){
      const usedPct=totalActual/totalBudgeted*100;
      if(usedPct>100) out.push({type:'danger',title:'Over Budget',msg:`Spent ${fmtCur(totalActual-totalBudgeted)} over total budget`});
      else if(usedPct>burnPct+15) out.push({type:'warn',title:'Budget Burning Fast',msg:`${usedPct.toFixed(0)}% spent with only ${burnPct.toFixed(0)}% of month elapsed`});
      else if(usedPct<burnPct-20) out.push({type:'good',title:'Under Budget',msg:`On track — only ${usedPct.toFixed(0)}% of budget used`});
    }
    // Projected overspend vs last month
    const lastTotal=lastMonthTx.reduce((s,t)=>s+t.amount,0);
    if(lastTotal>0&&projectedSpend>lastTotal*1.2) out.push({type:'warn',title:'Spending Up',msg:`Projected ${fmtCur(projectedSpend)} vs ${fmtCur(Math.round(lastTotal))} last month (+${Math.round((projectedSpend/lastTotal-1)*100)}%)`});
    // Category spikes
    Object.entries(catThisMonth).forEach(([cat,amt])=>{
      const last=catLastMonth[cat]||0;
      if(last>30&&amt>last*1.4&&amt>50) out.push({type:'warn',title:cat,msg:`${Math.round((amt/last-1)*100)}% above last month (${fmtCur(Math.round(amt))} vs ${fmtCur(Math.round(last))})`});
    });
    // Specific over-budget categories
    budgetItems.forEach(b=>{
      const budget=b.budgetAmount?.amount||0, actual=b.actualAmount?.amount||0;
      if(budget>0&&actual>budget&&actual-budget>20) out.push({type:'danger',title:b.category?.name,msg:`Over budget by ${fmtCur(Math.round(actual-budget))}`});
    });
    // Positive: savings pace
    if(monthlyIncome>0&&totalSpendMTD/monthPct<monthlyIncome*0.8) out.push({type:'good',title:'Savings Pace',msg:`On track to save ${fmtCur(Math.round(monthlyIncome-projectedSpend))} this month`});
    // Large transactions
    const bigTx=thisMonthTx.filter(t=>t.amount>500).sort((a,b)=>b.amount-a.amount).slice(0,2);
    bigTx.forEach(t=>out.push({type:'info',title:'Large Transaction',msg:`${t.merchant?.name||'Unknown'} — ${fmtCur(Math.round(t.amount))} on ${t.date}`}));
    return out.slice(0,6);
  },[budgetItems,totalBudgeted,totalActual,monthPct,catThisMonth,catLastMonth,projectedSpend,lastMonthTx,totalSpendMTD,monthlyIncome]);

  if(!hasMonarch) return (
    <div className="page">
      <div className="empty-state" style={{marginTop:60}}>
        <div style={{fontSize:40,marginBottom:16}}>🔗</div>
        <h3>Connect Monarch Money</h3>
        <p style={{fontSize:12,marginBottom:20}}>Connect your Monarch account to see your financial overview, accounts, and spending.</p>
        <button className="btn-primary" onClick={()=>setTab('Settings')}>Connect in Settings</button>
      </div>
    </div>
  );

  const PICOLORS = ['#10b981','#3b82f6','#f59e0b','#8b5cf6','#ef4444','#06b6d4','#f97316','#ec4899'];

  return (
    <div className="page">
      {Toast}

      {/* Sync status bar */}
      <div style={{display:'flex',justifyContent:'space-between',alignItems:'center',marginBottom:16,flexWrap:'wrap',gap:8}}>
        <div style={{fontSize:11,color:'#475569'}}>
          {monarchAuth?.lastSync
            ? <>Last synced <span style={{color:'#64748b'}}>{timeAgo(monarchAuth.lastSync)}</span>
                {monarchAuth.transactionCount > 0 && <span style={{color:'#475569'}}> · {monarchAuth.transactionCount} transactions</span>}
                {monarchAuth.accountCount > 0 && <span style={{color:'#475569'}}> · {monarchAuth.accountCount} accounts</span>}
              </>
            : <span style={{color:'#ef4444'}}>Never synced — click Sync to load your data</span>
          }
        </div>
        <button onClick={handleSync} disabled={syncing}
          style={{padding:'4px 14px',fontSize:11,borderRadius:6,cursor:'pointer',
            background:'transparent',color:'#10b981',border:'1px solid rgba(16,185,129,0.4)'}}>
          {syncing ? 'Syncing…' : 'Sync Now'}
        </button>
      </div>

      {/* ── Row 1: Net Worth Hero ── */}
      <div className="card" style={{marginBottom:16}}>
        <div style={{display:'flex',justifyContent:'space-between',alignItems:'flex-start',flexWrap:'wrap',gap:12}}>
          <div>
            <div className="label">Net Worth</div>
            <div className={`val-lg ${netWorth>=0?'green':'red'}`}>{fmtCur(netWorth)}</div>
            {nwDelta!==0 && <div style={{fontSize:11,marginTop:2,color:nwDelta>=0?'#10b981':'#ef4444'}}>
              {nwDelta>=0?'▲':'▼'} {fmtCur(Math.abs(nwDelta))} this month
            </div>}
          </div>
          <div style={{display:'flex',gap:20,flexWrap:'wrap'}}>
            <div style={{textAlign:'right'}}>
              <div className="label">Assets</div>
              <div style={{fontSize:16,fontWeight:700,color:'#10b981'}}>{fmtCur(totalAssets)}</div>
            </div>
            <div style={{textAlign:'right'}}>
              <div className="label">Debt</div>
              <div style={{fontSize:16,fontWeight:700,color:'#ef4444'}}>{fmtCur(totalDebt)}</div>
            </div>
            <div style={{textAlign:'right'}}>
              <div className="label">Debt / Assets</div>
              <div style={{fontSize:16,fontWeight:700,color:debtToAssetPct<=20?'#10b981':debtToAssetPct<=40?'#f59e0b':'#ef4444'}}>{debtToAssetPct.toFixed(1)}%</div>
            </div>
            {nwYtd && <div style={{textAlign:'right'}}>
              <div className="label">YTD NW</div>
              <div style={{fontSize:16,fontWeight:700,color:nwYtd.abs>=0?'#10b981':'#ef4444'}}>
                {nwYtd.abs>=0?'+':''}{fmtCur(nwYtd.abs)}
                {nwYtd.pct!==null && <span style={{fontSize:10,color:'#64748b',marginLeft:4}}>({nwYtd.pct>=0?'+':''}{nwYtd.pct.toFixed(1)}%)</span>}
              </div>
            </div>}
            {nw12m && <div style={{textAlign:'right'}}>
              <div className="label">12M NW</div>
              <div style={{fontSize:16,fontWeight:700,color:nw12m.abs>=0?'#10b981':'#ef4444'}}>
                {nw12m.abs>=0?'+':''}{fmtCur(nw12m.abs)}
                {nw12m.pct!==null && <span style={{fontSize:10,color:'#64748b',marginLeft:4}}>({nw12m.pct>=0?'+':''}{nw12m.pct.toFixed(1)}%)</span>}
              </div>
            </div>}
            {monthlyIncome>0 && <div style={{textAlign:'right'}}>
              <div className="label">Savings Rate</div>
              <div style={{fontSize:16,fontWeight:700,color:savingsRate>=20?'#10b981':savingsRate>=10?'#f59e0b':'#ef4444'}}>{savingsRate.toFixed(1)}%</div>
            </div>}
          </div>
        </div>
        {nwhData.length>1 && (
          <div style={{marginTop:16,height:130}}>
            <ResponsiveContainer width="100%" height="100%">
              <AreaChart data={nwhData} margin={{top:4,right:0,left:0,bottom:0}}>
                <defs>
                  <linearGradient id="gAssets" x1="0" y1="0" x2="0" y2="1">
                    <stop offset="5%" stopColor="#10b981" stopOpacity={0.25}/><stop offset="95%" stopColor="#10b981" stopOpacity={0}/>
                  </linearGradient>
                  <linearGradient id="gLiab" x1="0" y1="0" x2="0" y2="1">
                    <stop offset="5%" stopColor="#ef4444" stopOpacity={0.2}/><stop offset="95%" stopColor="#ef4444" stopOpacity={0}/>
                  </linearGradient>
                </defs>
                <XAxis dataKey="date" tick={{fontSize:9,fill:'#475569'}} axisLine={false} tickLine={false} interval="preserveStartEnd"/>
                <YAxis hide/>
                <Tooltip formatter={v=>fmtCur(v)} contentStyle={{background:'#0f172a',border:'1px solid #334155',borderRadius:8,fontSize:11,color:'#e2e8f0'}}/>
                <Area type="monotone" dataKey="assets" stroke="#10b981" fill="url(#gAssets)" strokeWidth={1.5} dot={false} name="Assets"/>
                <Area type="monotone" dataKey="liabilities" stroke="#ef4444" fill="url(#gLiab)" strokeWidth={1.5} dot={false} name="Debt"/>
                <Area type="monotone" dataKey="net" stroke="#3b82f6" fill="none" strokeWidth={2.5} dot={false} name="Net Worth" strokeDasharray="0"/>
              </AreaChart>
            </ResponsiveContainer>
          </div>
        )}
      </div>

      {/* ── Sync health + data quality banner ── */}
      {(syncDegraded || lastSectionErrors.length>0 || qcWarnings.length>0) && (
        <div style={{background:'rgba(245,158,11,0.08)',border:'1px solid rgba(245,158,11,0.35)',borderRadius:8,padding:'10px 14px',marginBottom:16,display:'flex',gap:12,alignItems:'flex-start',flexWrap:'wrap'}}>
          <div style={{fontSize:16,lineHeight:1}}>⚠️</div>
          <div style={{flex:1,minWidth:220}}>
            {syncDegraded && (
              <>
                <div style={{fontSize:12,fontWeight:600,color:'#f59e0b',marginBottom:2}}>Transactions not synced</div>
                <div style={{fontSize:11,color:'#94a3b8',marginBottom:8}}>
                  {accounts.length} accounts synced but 0 transactions came back. Spending, cashflow, and budget widgets will stay empty until the transactions pipeline succeeds.
                </div>
              </>
            )}
            {lastSectionErrors.length>0 && (
              <div style={{marginBottom:qcWarnings.length?8:0}}>
                <div style={{fontSize:11,fontWeight:600,color:'#f59e0b',marginBottom:3}}>Last sync reported {lastSectionErrors.length} section error{lastSectionErrors.length===1?'':'s'}:</div>
                <div style={{fontSize:11,color:'#94a3b8',lineHeight:1.6}}>
                  {lastSectionErrors.map((e,i)=>(
                    <div key={i}><span style={{color:'#f59e0b'}}>{e.section}:</span> {e.error}</div>
                  ))}
                </div>
              </div>
            )}
            {qcWarnings.length>0 && (
              <div>
                <div style={{fontSize:11,fontWeight:600,color:'#f59e0b',marginBottom:3}}>Data quality:</div>
                <div style={{fontSize:11,color:'#94a3b8',lineHeight:1.6}}>
                  {qcWarnings.map((w,i)=><div key={i}>· {w}</div>)}
                </div>
              </div>
            )}
          </div>
          <button onClick={handleSync} disabled={syncing}
            style={{padding:'6px 14px',fontSize:11,borderRadius:6,cursor:'pointer',background:'rgba(245,158,11,0.15)',color:'#f59e0b',border:'1px solid rgba(245,158,11,0.5)',flexShrink:0}}>
            {syncing ? 'Syncing…' : 'Retry Sync'}
          </button>
        </div>
      )}

      {/* ── Insight row: Asset allocation + Debt breakdown (works without tx) ── */}
      {(allocationData.length>0 || debtBreakdown.length>0) && (
        <div className="grid-2" style={{marginBottom:16}}>
          <div className="card">
            <div className="label" style={{marginBottom:12}}>Asset Allocation</div>
            {allocationData.length>0 ? (
              <div style={{display:'flex',gap:12,alignItems:'center'}}>
                <ResponsiveContainer width={140} height={140}>
                  <PieChart>
                    <Pie data={allocationData} cx="50%" cy="50%" innerRadius={38} outerRadius={62} dataKey="value" paddingAngle={2}>
                      {allocationData.map((c,i)=><Cell key={i} fill={c.fill}/>)}
                    </Pie>
                    <Tooltip formatter={(v,n)=>[fmtCur(v),n]} contentStyle={{background:'#0f172a',border:'1px solid #334155',borderRadius:8,fontSize:11,color:'#e2e8f0'}}/>
                  </PieChart>
                </ResponsiveContainer>
                <div style={{flex:1,display:'flex',flexDirection:'column',gap:5}}>
                  {allocationData.map((c,i)=>{
                    const pct = totalAssets>0 ? (c.value/totalAssets*100) : 0;
                    return (
                      <div key={i} style={{display:'flex',alignItems:'center',gap:6}}>
                        <div style={{width:8,height:8,borderRadius:2,background:c.fill,flexShrink:0}}/>
                        <div style={{flex:1,fontSize:11,color:'#94a3b8'}}>{c.name}</div>
                        <div style={{fontSize:11,color:'#e2e8f0',fontWeight:600}}>{fmtCur(c.value)}</div>
                        <div style={{fontSize:10,color:'#64748b',width:34,textAlign:'right'}}>{pct.toFixed(0)}%</div>
                      </div>
                    );
                  })}
                </div>
              </div>
            ) : <div className="muted" style={{textAlign:'center',padding:30}}>No asset data</div>}
          </div>

          <div className="card">
            <div style={{display:'flex',justifyContent:'space-between',alignItems:'center',marginBottom:12}}>
              <div className="label" style={{margin:0}}>Debt Breakdown</div>
              <div style={{fontSize:11,fontWeight:600,color:'#ef4444'}}>{fmtCur(totalDebt)}</div>
            </div>
            {debtBreakdown.length>0 ? (
              <div style={{display:'flex',flexDirection:'column',gap:6}}>
                {debtBreakdown.map((d,i)=>{
                  const pct = totalDebt>0 ? (d.value/totalDebt*100) : 0;
                  const color = d.type.includes('mortgage')?'#8b5cf6':d.type.includes('credit')?'#f59e0b':d.type.includes('auto')?'#06b6d4':d.type.includes('student')?'#ec4899':'#ef4444';
                  return (
                    <div key={i}>
                      <div style={{display:'flex',justifyContent:'space-between',marginBottom:3}}>
                        <span style={{fontSize:11,color:'#e2e8f0',overflow:'hidden',textOverflow:'ellipsis',whiteSpace:'nowrap',maxWidth:180}}>{d.name}</span>
                        <span style={{fontSize:11,color:'#94a3b8'}}>{fmtCur(d.value)} <span style={{color:'#475569'}}>· {pct.toFixed(0)}%</span></span>
                      </div>
                      <div style={{height:4,background:'#1e2a3a',borderRadius:2,overflow:'hidden'}}>
                        <div style={{height:'100%',width:`${pct}%`,background:color,borderRadius:2}}/>
                      </div>
                    </div>
                  );
                })}
              </div>
            ) : <div className="muted" style={{textAlign:'center',padding:30}}>Debt-free 🎉</div>}
          </div>
        </div>
      )}

      {/* ── Row 2: Cashflow + Spending ── */}
      <div className="grid-2" style={{marginBottom:16}}>
        <div className="card">
          <div className="label" style={{marginBottom:12}}>Monthly Cashflow</div>
          {cashflowData.length > 0 ? (
            <ResponsiveContainer width="100%" height={200}>
              <ComposedChart data={cashflowData} margin={{top:4,right:4,left:0,bottom:0}}>
                <XAxis dataKey="month" tick={{fontSize:10,fill:'#64748b'}} axisLine={false} tickLine={false}/>
                <YAxis tick={{fontSize:10,fill:'#64748b'}} axisLine={false} tickLine={false} tickFormatter={v=>`$${(v/1000).toFixed(0)}k`}/>
                <CartesianGrid strokeDasharray="3 3" stroke="#1e2a3a"/>
                <Tooltip formatter={v=>fmtCur(v)} contentStyle={{background:'#0f172a',border:'1px solid #334155',borderRadius:8,fontSize:11,color:'#e2e8f0'}}/>
                <Bar dataKey="income" fill="#10b981" radius={[3,3,0,0]} name="Income" opacity={0.85}/>
                <Bar dataKey="expenses" fill="#ef4444" radius={[3,3,0,0]} name="Expenses" opacity={0.85}/>
                <Line type="monotone" dataKey="savings" stroke="#3b82f6" strokeWidth={2} dot={{fill:'#3b82f6',r:3}} name="Surplus"/>
              </ComposedChart>
            </ResponsiveContainer>
          ) : <div className="muted" style={{textAlign:'center',padding:40}}>No cashflow data</div>}
        </div>

        <div className="card">
          <div style={{display:'flex',justifyContent:'space-between',alignItems:'center',marginBottom:12}}>
            <div className="label" style={{margin:0}}>Spending This Month</div>
            <div style={{fontSize:12,fontWeight:600,color:'#e2e8f0'}}>{fmtCur(totalSpend)}</div>
          </div>
          {spendByCat.length > 0 ? (
            <div style={{display:'flex',gap:12,alignItems:'center'}}>
              <ResponsiveContainer width={130} height={130}>
                <PieChart>
                  <Pie data={spendByCat} cx="50%" cy="50%" innerRadius={36} outerRadius={58} dataKey="value" paddingAngle={2}>
                    {spendByCat.map((c,i)=><Cell key={i} fill={c.fill}/>)}
                  </Pie>
                  <Tooltip formatter={(v,n)=>[fmtCur(v),n]} contentStyle={{background:'#0f172a',border:'1px solid #334155',borderRadius:8,fontSize:11,color:'#e2e8f0'}}/>
                </PieChart>
              </ResponsiveContainer>
              <div style={{flex:1,display:'flex',flexDirection:'column',gap:5}}>
                {spendByCat.map((c,i)=>(
                  <div key={i} style={{display:'flex',alignItems:'center',gap:6}}>
                    <div style={{width:8,height:8,borderRadius:2,background:c.fill,flexShrink:0}}/>
                    <div style={{flex:1,fontSize:10,color:'#94a3b8',overflow:'hidden',textOverflow:'ellipsis',whiteSpace:'nowrap'}}>{c.name}</div>
                    <div style={{fontSize:10,color:'#e2e8f0',fontWeight:600}}>{fmtCur(c.value)}</div>
                  </div>
                ))}
              </div>
            </div>
          ) : <div className="muted" style={{textAlign:'center',padding:40}}>No transactions this month</div>}
        </div>
      </div>

      {/* ── Row 3: Budget Tracker ── */}
      {budgetItems.length > 0 && (
        <div className="card" style={{marginBottom:16}}>
          <div style={{display:'flex',justifyContent:'space-between',alignItems:'center',marginBottom:14}}>
            <div className="label" style={{margin:0}}>Budget — {currentMonthKey}</div>
            <div style={{display:'flex',gap:16,fontSize:11}}>
              <span style={{color:'#64748b'}}>Budgeted <span style={{color:'#e2e8f0',fontWeight:600}}>{fmtCur(totalBudgeted)}</span></span>
              <span style={{color:'#64748b'}}>Spent <span style={{color:totalActual>totalBudgeted?'#ef4444':'#10b981',fontWeight:600}}>{fmtCur(totalActual)}</span></span>
            </div>
          </div>
          <div className="grid-2" style={{gap:'8px 24px'}}>
            {budgetItems.map((b,i)=>{
              const budget=b.budgetAmount?.amount||0;
              const actual=b.actualAmount?.amount||0;
              const pct=budget>0?Math.min(actual/budget*100,100):0;
              const over=actual>budget;
              return (
                <div key={i}>
                  <div style={{display:'flex',justifyContent:'space-between',marginBottom:3}}>
                    <span style={{fontSize:11,color:'#94a3b8'}}>{b.category?.name||'Other'}</span>
                    <span style={{fontSize:11,color:over?'#ef4444':'#94a3b8'}}>{fmtCur(actual)}<span style={{color:'#475569'}}>/{fmtCur(budget)}</span></span>
                  </div>
                  <div style={{height:5,background:'#1e2a3a',borderRadius:3,overflow:'hidden'}}>
                    <div style={{height:'100%',width:`${pct}%`,background:over?'#ef4444':pct>80?'#f59e0b':'#10b981',borderRadius:3,transition:'width 0.3s'}}/>
                  </div>
                </div>
              );
            })}
          </div>
        </div>
      )}

      {/* ── Spend Analyzer ── */}
      {(insights.length>0||velocityData.length>0||topMerchants.length>0) && (
        <div className="card" style={{marginBottom:16}}>
          <div style={{display:'flex',justifyContent:'space-between',alignItems:'center',flexWrap:'wrap',gap:8,marginBottom:14}}>
            <div className="label" style={{margin:0}}>Spend Analyzer</div>
            <div style={{display:'flex',gap:16,fontSize:11,color:'#64748b'}}>
              <span>MTD <span style={{color:'#e2e8f0',fontWeight:600}}>{fmtCur(Math.round(totalSpendMTD))}</span></span>
              <span>Projected <span style={{color:projectedSpend>totalBudgeted&&totalBudgeted>0?'#ef4444':'#e2e8f0',fontWeight:600}}>{fmtCur(projectedSpend)}</span></span>
              <span>Day <span style={{color:'#e2e8f0',fontWeight:600}}>{today}/{daysInMonth}</span></span>
            </div>
          </div>

          {/* Smart insight chips */}
          {insights.length>0 && (
            <div style={{display:'flex',flexWrap:'wrap',gap:8,marginBottom:16}}>
              {insights.map((ins,i)=>{
                const cfg={
                  danger:{bg:'rgba(239,68,68,0.1)',border:'rgba(239,68,68,0.3)',dot:'#ef4444'},
                  warn:  {bg:'rgba(245,158,11,0.1)',border:'rgba(245,158,11,0.3)',dot:'#f59e0b'},
                  good:  {bg:'rgba(16,185,129,0.1)',border:'rgba(16,185,129,0.3)',dot:'#10b981'},
                  info:  {bg:'rgba(59,130,246,0.1)',border:'rgba(59,130,246,0.3)',dot:'#3b82f6'},
                }[ins.type]||{bg:'rgba(71,85,105,0.2)',border:'#334155',dot:'#64748b'};
                return (
                  <div key={i} style={{background:cfg.bg,border:`1px solid ${cfg.border}`,borderRadius:8,padding:'7px 12px',maxWidth:280}}>
                    <div style={{display:'flex',alignItems:'center',gap:6,marginBottom:2}}>
                      <div style={{width:6,height:6,borderRadius:'50%',background:cfg.dot,flexShrink:0}}/>
                      <span style={{fontSize:11,fontWeight:600,color:'#e2e8f0'}}>{ins.title}</span>
                    </div>
                    <div style={{fontSize:11,color:'#94a3b8',paddingLeft:12}}>{ins.msg}</div>
                  </div>
                );
              })}
            </div>
          )}

          <div className="grid-2" style={{gap:16}}>
            {/* Spending velocity: this month vs last month cumulative */}
            <div>
              <div style={{fontSize:11,color:'#64748b',marginBottom:8}}>Daily Spend Pace — vs Last Month</div>
              <ResponsiveContainer width="100%" height={170}>
                <LineChart data={velocityData} margin={{top:4,right:4,left:0,bottom:0}}>
                  <XAxis dataKey="day" tick={{fontSize:9,fill:'#475569'}} axisLine={false} tickLine={false} interval={4}/>
                  <YAxis tick={{fontSize:9,fill:'#475569'}} axisLine={false} tickLine={false} tickFormatter={v=>`$${(v/1000).toFixed(0)}k`} width={32}/>
                  <CartesianGrid strokeDasharray="3 3" stroke="#1e2a3a"/>
                  <Tooltip formatter={v=>fmtCur(v)} labelFormatter={d=>`Day ${d}`} contentStyle={{background:'#0f172a',border:'1px solid #334155',borderRadius:8,fontSize:11,color:'#e2e8f0'}}/>
                  <Line type="monotone" dataKey="last" stroke="#334155" strokeWidth={1.5} dot={false} name="Last Month" strokeDasharray="4 2" connectNulls/>
                  <Line type="monotone" dataKey="this" stroke="#10b981" strokeWidth={2} dot={false} name="This Month" connectNulls/>
                </LineChart>
              </ResponsiveContainer>
            </div>

            {/* Category MoM comparison */}
            <div>
              <div style={{fontSize:11,color:'#64748b',marginBottom:8}}>Category — This vs Last Month</div>
              <ResponsiveContainer width="100%" height={170}>
                <BarChart data={catCompare} layout="vertical" margin={{top:0,right:4,left:0,bottom:0}}>
                  <XAxis type="number" tick={{fontSize:9,fill:'#475569'}} axisLine={false} tickLine={false} tickFormatter={v=>`$${(v/1000).toFixed(0)}k`}/>
                  <YAxis type="category" dataKey="name" tick={{fontSize:9,fill:'#94a3b8'}} axisLine={false} tickLine={false} width={80}/>
                  <CartesianGrid strokeDasharray="3 3" stroke="#1e2a3a" horizontal={false}/>
                  <Tooltip formatter={v=>fmtCur(v)} contentStyle={{background:'#0f172a',border:'1px solid #334155',borderRadius:8,fontSize:11,color:'#e2e8f0'}}/>
                  <Bar dataKey="last" fill="#334155" radius={[0,2,2,0]} name="Last Month" barSize={6}/>
                  <Bar dataKey="this" fill="#10b981" radius={[0,2,2,0]} name="This Month" barSize={6}/>
                </BarChart>
              </ResponsiveContainer>
            </div>
          </div>

          {/* Top merchants */}
          {topMerchants.length>0 && (
            <div style={{marginTop:14}}>
              <div style={{fontSize:11,color:'#64748b',marginBottom:8}}>Top Merchants This Month</div>
              <div className="grid-3" style={{gap:'6px 16px'}}>
                {topMerchants.map((m,i)=>{
                  const pct=Math.round(totalSpendMTD>0?m.amt/totalSpendMTD*100:0);
                  return (
                    <div key={i} style={{display:'flex',alignItems:'center',gap:8}}>
                      <div style={{fontSize:11,color:'#475569',width:16,flexShrink:0}}>#{i+1}</div>
                      <div style={{flex:1,minWidth:0}}>
                        <div style={{fontSize:11,color:'#e2e8f0',overflow:'hidden',textOverflow:'ellipsis',whiteSpace:'nowrap'}}>{m.name}</div>
                        <div style={{height:3,background:'#1e2a3a',borderRadius:2,marginTop:2}}>
                          <div style={{width:`${pct}%`,height:'100%',background:'#3b82f6',borderRadius:2}}/>
                        </div>
                      </div>
                      <div style={{fontSize:11,fontWeight:600,color:'#94a3b8',flexShrink:0}}>{fmtCur(m.amt)}</div>
                    </div>
                  );
                })}
              </div>
            </div>
          )}
        </div>
      )}

      {/* ── Row 4: Account Balances + Recent Transactions ── */}
      <div className="grid-2" style={{marginBottom:16}}>
        <div className="card">
          <div className="label" style={{marginBottom:10}}>Accounts</div>
          {Object.entries(acctGroups).filter(([,v])=>v.length>0).map(([group,accts])=>{
            const total = accts.reduce((s,a)=>s+(a.currentBalance||0),0);
            const isDebt = group==='Credit'||group==='Loans';
            return (
              <div key={group} style={{display:'flex',justifyContent:'space-between',padding:'6px 0',borderBottom:'1px solid #161b22'}}>
                <span style={{color:'#94a3b8',fontSize:12}}>{group}</span>
                <span style={{fontWeight:600,fontSize:12,color:isDebt?'#ef4444':'#10b981'}}>{fmtCur(Math.abs(total))}</span>
              </div>
            );
          })}
        </div>

        <div className="card">
          <div className="label" style={{marginBottom:10}}>Recent Transactions</div>
          {transactions.length > 0 ? (
            <div style={{display:'flex',flexDirection:'column',gap:0}}>
              {transactions.slice(0,8).map(t=>(
                <div key={t.id} style={{display:'flex',alignItems:'center',gap:8,padding:'6px 0',borderBottom:'1px solid #161b22'}}>
                  <div style={{flex:1,minWidth:0}}>
                    <div style={{fontSize:12,color:'#e2e8f0',overflow:'hidden',textOverflow:'ellipsis',whiteSpace:'nowrap'}}>{t.merchant?.name||'—'}</div>
                    <div style={{fontSize:10,color:'#475569'}}>{t.date} · {t.category?.name||'—'}</div>
                  </div>
                  <div style={{fontSize:12,fontWeight:600,flexShrink:0,color:t.amount>0?'#ef4444':'#10b981'}}>{fmtCurFull(Math.abs(t.amount))}</div>
                </div>
              ))}
            </div>
          ) : <div className="muted" style={{textAlign:'center',padding:30}}>No recent transactions</div>}
        </div>
      </div>

    </div>
  );
}

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