// Trends page — month-over-month with range selector + per-category time-series
function TrendsPage() {
  const allMonths = D.months || [];
  const [range, setRange] = useState('12');  // '3' | '6' | '12' | 'all'
  const [selectedCat, setSelectedCat] = useState(null);
  const [a, setA] = useState(Math.max(0, allMonths.length - 2));
  const [b, setB] = useState(Math.max(0, allMonths.length - 1));

  // Filter months by selected range — always anchored to most recent
  const months = useMemo(() => {
    if (range === 'all') return allMonths;
    const n = parseInt(range, 10);
    return allMonths.slice(-n);
  }, [range, allMonths.length]);

  // Aggregate stats across the visible range
  const totalExp = months.reduce((s, m) => s + (m.summary?.expenses || 0), 0);
  const totalInc = months.reduce((s, m) => s + (m.summary?.income || 0), 0);
  const avgExp = months.length ? totalExp / months.length : 0;
  const maxMonth = months.reduce((max, m) => (!max || m.summary.expenses > max.summary.expenses ? m : max), null);
  const minMonth = months.reduce((min, m) => (!min || m.summary.expenses < min.summary.expenses ? m : min), null);

  // All categories that appear in the range, with totals
  const catTotals = useMemo(() => {
    const totals = {};
    months.forEach(m => {
      Object.entries(m.summary?.byCat || {}).forEach(([c, v]) => {
        totals[c] = (totals[c] || 0) + v;
      });
    });
    return Object.entries(totals).sort((a, b) => b[1] - a[1]);
  }, [months]);

  // Per-month series for selected category (or first cat by default)
  const activeCat = selectedCat || catTotals[0]?.[0];
  const catSeries = useMemo(() => {
    if (!activeCat) return [];
    return months.map(m => ({
      month: m,
      value: m.summary?.byCat?.[activeCat] || 0,
    }));
  }, [months, activeCat]);
  const catMax = Math.max(...catSeries.map(p => p.value), 1);
  const catAvg = catSeries.length ? catSeries.reduce((s, p) => s + p.value, 0) / catSeries.length : 0;

  // Comparison
  const mA = allMonths[a], mB = allMonths[b];
  const cats = useMemo(() => {
    if (!mA || !mB) return [];
    const all = new Set([...Object.keys(mA.summary?.byCat || {}), ...Object.keys(mB.summary?.byCat || {})]);
    return [...all].map(c => ({
      cat: c, a: mA.summary.byCat[c] || 0, b: mB.summary.byCat[c] || 0,
      delta: ((mB.summary.byCat[c]||0) - (mA.summary.byCat[c]||0)),
    })).sort((x,y) => Math.abs(y.delta) - Math.abs(x.delta));
  }, [a, b]);

  return (
    <div className="fade-in" style={{ padding: '32px 40px 60px', maxWidth: 1280, margin: '0 auto' }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-end', marginBottom: 18 }}>
        <div>
          <h1 style={{ fontSize: 26, fontWeight: 600, letterSpacing: '-0.02em', margin: '0 0 4px' }}>מגמות</h1>
          <div style={{ fontSize: 12.5, color: 'var(--ink-soft)' }}>השווה בין חודשים, חפש דפוסים, זהה שינויים.</div>
        </div>
        <Segmented
          options={[
            {value:'3', label:'3 חודשים'},
            {value:'6', label:'חצי שנה'},
            {value:'12', label:'שנה'},
            {value:'all', label:'הכל'},
          ]}
          value={range}
          onChange={setRange}
        />
      </div>

      {/* Hero — overview of the selected range */}
      <Surface style={{ marginBottom: 16, padding: 0, overflow: 'hidden' }} padded={false}>
        <div style={{ display: 'grid', gridTemplateColumns: '1.5fr 1fr 1fr', borderBottom: '0.5px solid var(--rule)' }}>
          <div style={{ padding: '22px 28px', borderInlineEnd: '0.5px solid var(--rule)' }}>
            <div style={{ fontSize: 11.5, color: 'var(--ink-soft)', fontWeight: 500, letterSpacing: '0.02em' }}>
              סך הוצאות · {months.length} חודשים
            </div>
            <Money value={totalExp} size={32} weight={700} />
            <div style={{ fontSize: 11, color: 'var(--ink-soft)', marginTop: 4 }}>ממוצע {fmtILS(avgExp)}/חודש</div>
          </div>
          <div style={{ padding: '22px 28px', borderInlineEnd: '0.5px solid var(--rule)' }}>
            <div style={{ fontSize: 11.5, color: 'var(--ink-soft)', fontWeight: 500, marginBottom: 6 }}>חודש הכי יקר</div>
            <div style={{ fontSize: 14, fontWeight: 600 }}>{maxMonth?.label || '—'}</div>
            <div className="tnum" style={{ fontSize: 12, color: 'var(--alert)', marginTop: 2, fontWeight: 600 }}>
              {fmtILS(maxMonth?.summary?.expenses || 0)}
            </div>
          </div>
          <div style={{ padding: '22px 28px' }}>
            <div style={{ fontSize: 11.5, color: 'var(--ink-soft)', fontWeight: 500, marginBottom: 6 }}>חודש הכי זול</div>
            <div style={{ fontSize: 14, fontWeight: 600 }}>{minMonth?.label || '—'}</div>
            <div className="tnum" style={{ fontSize: 12, color: 'var(--positive)', marginTop: 2, fontWeight: 600 }}>
              {fmtILS(minMonth?.summary?.expenses || 0)}
            </div>
          </div>
        </div>
        <div style={{ padding: '20px 28px' }}>
          <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 12 }}>
            <div style={{ fontSize: 13, fontWeight: 600 }}>הוצאות לאורך זמן</div>
          </div>
          {months.length > 1 ? (
            <MultiMonthChart months={months} accent="var(--accent)" height={170} />
          ) : (
            <div style={{ padding: 28, textAlign: 'center', color: 'var(--ink-soft)', fontSize: 12 }}>
              צריך לפחות 2 חודשי נתונים כדי להציג מגמה.
            </div>
          )}
        </div>
      </Surface>

      {/* Per-category time series */}
      <Surface style={{ marginBottom: 16 }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 14, flexWrap: 'wrap', gap: 12 }}>
          <div>
            <div style={{ fontSize: 13, fontWeight: 600 }}>קטגוריה לאורך זמן</div>
            <div style={{ fontSize: 11, color: 'var(--ink-soft)', marginTop: 2 }}>בחר קטגוריה כדי לראות את הוצאותיה בחודשים</div>
          </div>
          <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap', justifyContent: 'flex-end' }}>
            {catTotals.slice(0, 8).map(([c]) => {
              const meta = D.categories[c] || {};
              const active = c === activeCat;
              return (
                <button key={c} onClick={() => setSelectedCat(c)} style={{
                  padding: '5px 10px', borderRadius: 99,
                  background: active ? meta.color : 'var(--surface)',
                  color: active ? 'white' : 'var(--ink-2)',
                  border: active ? 'none' : '0.5px solid var(--rule)',
                  fontSize: 11.5, fontWeight: 500, cursor: 'pointer',
                  display: 'inline-flex', alignItems: 'center', gap: 5,
                }}>
                  <span style={{ fontSize: 11 }}>{meta.icon || '·'}</span>{c}
                </button>
              );
            })}
          </div>
        </div>

        {/* Bar chart of selected category over months */}
        {activeCat && (
          <>
            <div style={{
              display: 'flex', alignItems: 'flex-end', gap: 4,
              height: 140, marginBottom: 8,
              borderBottom: '0.5px solid var(--rule)',
            }}>
              {catSeries.map((p, i) => {
                const h = p.value > 0 ? Math.max(2, (p.value / catMax) * 130) : 0;
                const isMax = p.value === catMax && p.value > 0;
                const meta = D.categories[activeCat] || {};
                return (
                  <div key={i} style={{
                    flex: 1, display: 'flex', flexDirection: 'column',
                    alignItems: 'center', justifyContent: 'flex-end', gap: 4,
                    height: '100%',
                  }}>
                    <span className="tnum" style={{
                      fontSize: 9, color: 'var(--ink-soft)', fontWeight: 500,
                      opacity: p.value > 0 ? 1 : 0,
                    }}>{p.value >= 1000 ? Math.round(p.value/100)/10+'k' : Math.round(p.value)}</span>
                    <div title={`${p.month.label}: ${fmtILS(p.value)}`}
                      style={{
                        width: '100%', maxWidth: 50, minWidth: 16,
                        height: h,
                        background: isMax ? 'var(--alert)' : meta.color,
                        borderRadius: '6px 6px 0 0',
                        opacity: p.value > 0 ? 1 : 0.15,
                        cursor: 'help',
                        transition: 'background .2s',
                      }}
                    />
                  </div>
                );
              })}
            </div>
            <div style={{ display: 'flex', gap: 4, fontSize: 9.5, color: 'var(--ink-soft)', fontFamily: 'var(--font-mono)' }}>
              {catSeries.map((p, i) => (
                <div key={i} style={{ flex: 1, textAlign: 'center', minWidth: 16 }}>
                  {p.month.shortLabel}
                </div>
              ))}
            </div>
            <div style={{
              marginTop: 14, padding: 12, background: 'var(--bg-deep)', borderRadius: 10,
              display: 'flex', gap: 22, fontSize: 11.5,
            }}>
              <div>
                <div style={{ color: 'var(--ink-soft)', marginBottom: 2 }}>סך הכל</div>
                <span className="tnum" style={{ fontWeight: 600 }}>{fmtILS(catSeries.reduce((s,p) => s + p.value, 0))}</span>
              </div>
              <div>
                <div style={{ color: 'var(--ink-soft)', marginBottom: 2 }}>ממוצע חודשי</div>
                <span className="tnum" style={{ fontWeight: 600 }}>{fmtILS(catAvg)}</span>
              </div>
              <div>
                <div style={{ color: 'var(--ink-soft)', marginBottom: 2 }}>שיא</div>
                <span className="tnum" style={{ fontWeight: 600, color: 'var(--alert)' }}>{fmtILS(catMax)}</span>
              </div>
            </div>
          </>
        )}
      </Surface>

      {/* Month vs Month comparison */}
      <Surface>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16, flexWrap: 'wrap', gap: 10 }}>
          <div style={{ fontSize: 13, fontWeight: 600 }}>השוואה בין חודשים</div>
          <div style={{ display: 'flex', gap: 8, alignItems: 'center', fontSize: 12 }}>
            <select value={a} onChange={e => setA(+e.target.value)} style={{ padding: '6px 10px', borderRadius: 8, background: 'var(--bg-deep)', border: '0.5px solid var(--rule)', fontSize: 12 }}>
              {allMonths.map((m,i) => <option key={i} value={i} disabled={(m.txns?.length || 0) === 0}>{m.label}{(m.txns?.length || 0) === 0 ? ' (ריק)' : ''}</option>)}
            </select>
            <span style={{ color: 'var(--ink-soft)' }}>↔</span>
            <select value={b} onChange={e => setB(+e.target.value)} style={{ padding: '6px 10px', borderRadius: 8, background: 'var(--bg-deep)', border: '0.5px solid var(--rule)', fontSize: 12 }}>
              {allMonths.map((m,i) => <option key={i} value={i} disabled={(m.txns?.length || 0) === 0}>{m.label}{(m.txns?.length || 0) === 0 ? ' (ריק)' : ''}</option>)}
            </select>
          </div>
        </div>
        {cats.length === 0 ? (
          <div style={{ padding: 24, textAlign: 'center', color: 'var(--ink-soft)', fontSize: 12 }}>
            בחר שני חודשים עם נתונים כדי להשוות.
          </div>
        ) : (
          <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
            {cats.slice(0, 10).map(({ cat, a: av, b: bv, delta }) => {
              const max = Math.max(av, bv) || 1;
              const c = D.categories[cat] || {};
              return (
                <div key={cat} style={{ display: 'grid', gridTemplateColumns: '140px 1fr 90px', gap: 12, alignItems: 'center', fontSize: 12 }}>
                  <span style={{ display: 'flex', gap: 7, alignItems: 'center', fontWeight: 500 }}>
                    <span style={{ width: 8, height: 8, background: c.color, borderRadius: 2 }} />{cat}
                  </span>
                  <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
                    <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                      <div style={{ flex: 1, height: 6, background: 'var(--bg-deep)', borderRadius: 99 }}>
                        <div style={{ width: `${(av/max)*100}%`, height: '100%', background: 'var(--ink-faint)', borderRadius: 99 }} />
                      </div>
                      <span className="tnum" style={{ fontSize: 11, color: 'var(--ink-soft)', minWidth: 60, textAlign: 'left' }}>{fmtILS(av)}</span>
                    </div>
                    <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                      <div style={{ flex: 1, height: 6, background: 'var(--bg-deep)', borderRadius: 99 }}>
                        <div style={{ width: `${(bv/max)*100}%`, height: '100%', background: c.color, borderRadius: 99 }} />
                      </div>
                      <span className="tnum" style={{ fontSize: 11, fontWeight: 600, minWidth: 60, textAlign: 'left' }}>{fmtILS(bv)}</span>
                    </div>
                  </div>
                  <span className="tnum" style={{ fontSize: 12, fontWeight: 600, textAlign: 'left', color: delta > 0 ? 'var(--alert)' : 'var(--positive)' }}>
                    {delta > 0 ? '↑' : '↓'} {fmtILS(Math.abs(delta))}
                  </span>
                </div>
              );
            })}
          </div>
        )}
      </Surface>
    </div>
  );
}

// AI Report page
function AIPage() {
  const { dataVersion } = useApp();
  const [busy, setBusy] = useState(false);
  const aiConfigured = D.aiStatus?.configured;

  // Month selector — defaults to most recent month with transactions, falls
  // back to current month. Same pattern as Dashboard / TxnsPage.
  const monthsAll = D.months || [];
  const initialIdx = useMemo(() => {
    for (let i = monthsAll.length - 1; i >= 0; i--) {
      if ((monthsAll[i].txns?.length || 0) > 0) return i;
    }
    return Math.max(0, monthsAll.length - 1);
  }, [dataVersion]);
  const [monthIdx, setMonthIdx] = useState(initialIdx);
  useEffect(() => {
    if (monthIdx >= monthsAll.length) setMonthIdx(Math.max(0, monthsAll.length - 1));
  }, [dataVersion]);
  const activeMonth = monthsAll[monthIdx];

  // Per-month insights cache. Initialized from D.insights for the current
  // month so the page doesn't double-fetch on first paint.
  const isCurrentMonth = activeMonth && D.current
    && activeMonth.year === D.current.year
    && activeMonth.month === D.current.month;
  const [byMonth, setByMonth] = useState(() => {
    const seed = {};
    if (D.current && D.insights) {
      seed[`${D.current.year}-${D.current.month}`] = D.insights;
    }
    return seed;
  });

  // Fetch insights for the selected month on demand. Backend serves from DB
  // cache when a report exists; this is a fast, non-AI call.
  useEffect(() => {
    if (!activeMonth) return;
    const key = `${activeMonth.year}-${activeMonth.month}`;
    if (byMonth[key] !== undefined) return; // cached
    let cancelled = false;
    (async () => {
      try {
        const r = await apiFetch(`/api/ai/insights?year=${activeMonth.year}&month=${activeMonth.month}`);
        if (!r.ok) throw new Error('fetch failed');
        const data = await r.json();
        if (cancelled) return;
        const insightObj = data.exists && data.content ? {
          summary: data.content.summary,
          forecast: data.content.forecast,
          bullets: data.content.bullets || [],
          model: data.model,
          createdAt: data.created_at,
        } : null;
        setByMonth(prev => ({ ...prev, [key]: insightObj }));
      } catch (e) {
        if (!cancelled) setByMonth(prev => ({ ...prev, [key]: null }));
      }
    })();
    return () => { cancelled = true; };
  }, [activeMonth?.year, activeMonth?.month]);

  const insights = activeMonth
    ? (byMonth[`${activeMonth.year}-${activeMonth.month}`] || null)
    : null;
  const hasReport = !!(insights && insights.summary && (insights.bullets?.length > 0 || insights.forecast));

  const generate = async () => {
    if (!aiConfigured) {
      alert('AI לא מוגדר. הגדר ANTHROPIC_API_KEY ב-environment והפעל את השרת מחדש.');
      return;
    }
    if (busy || !activeMonth) return;
    if (hasReport && !confirm(`דוח קיים עבור ${activeMonth.label} יוחלף בדוח חדש. להמשיך?`)) return;
    setBusy(true);
    try {
      const r = await apiFetch(`/api/ai/insights?year=${activeMonth.year}&month=${activeMonth.month}`, { method: 'POST' });
      if (!r.ok) {
        const err = await r.json().catch(() => ({}));
        throw new Error(err.detail?.message || 'יצירת הדוח נכשלה');
      }
      const data = await r.json();
      const key = `${activeMonth.year}-${activeMonth.month}`;
      setByMonth(prev => ({ ...prev, [key]: {
        summary: data.content.summary,
        forecast: data.content.forecast,
        bullets: data.content.bullets || [],
        model: data.model,
        createdAt: data.created_at,
      }}));
      // Also refresh top-level data so the dashboard's "featured insight" reflects new content
      if (isCurrentMonth) {
        await window.APP_DATA_REFRESH();
      }
    } catch (err) {
      alert('שגיאה ביצירת דוח: ' + err.message);
    } finally {
      setBusy(false);
    }
  };

  const updatedLabel = insights?.createdAt
    ? new Date(insights.createdAt).toLocaleString('he-IL', { dateStyle: 'short', timeStyle: 'short' })
    : null;
  const modelLabel = insights?.model || D.aiStatus?.model || 'Cai';

  // Empty-state summary text when no report exists for the selected month
  const placeholderSummary = aiConfigured
    ? `דוח Cai עוד לא נוצר עבור ${activeMonth?.label || 'חודש זה'}. לחץ "צור דוח" כדי לקבל סקירה אישית.`
    : 'AI לא מוגדר. הגדר ANTHROPIC_API_KEY כדי לקבל תובנות ודוחות חודשיים.';

  return (
    <div className="fade-in" style={{ padding: '32px 40px 60px', maxWidth: 920, margin: '0 auto' }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 14, marginBottom: 6 }}>
        <SparkBadge size={36} />
        <div>
          <h1 style={{ fontSize: 26, fontWeight: 600, letterSpacing: '-0.02em', margin: 0 }}>דוח Cai</h1>
          <div style={{ fontSize: 12, color: 'var(--ink-soft)', marginTop: 2 }}>
            {activeMonth?.label || 'סקירה חודשית'} · {updatedLabel ? `עודכן ${updatedLabel}` : 'לא נוצר עדיין'} · {modelLabel}
          </div>
        </div>
        <button
          onClick={generate}
          disabled={busy || !aiConfigured || !activeMonth}
          style={{
            marginInlineStart: 'auto',
            background: 'var(--surface)', color: 'var(--ink)',
            border: '0.5px solid var(--rule)', padding: '8px 14px',
            borderRadius: 10, fontSize: 12.5, cursor: busy ? 'wait' : (aiConfigured ? 'pointer' : 'not-allowed'),
            boxShadow: 'var(--shadow-sm)',
            opacity: !aiConfigured ? 0.5 : (busy ? 0.7 : 1),
          }}>
          {busy ? '⟳ Cai חושב…' : (hasReport ? '↻ צור דוח חדש' : '✦ צור דוח')}
        </button>
      </div>

      {/* Month picker — pick which month to view/generate the report for */}
      <div style={{
        display: 'flex', gap: 6, marginTop: 18, marginBottom: 18, overflowX: 'auto', paddingBottom: 4,
        scrollbarWidth: 'thin',
      }}>
        {monthsAll.map((m, i) => {
          const active = i === monthIdx;
          const isEmpty = (m.txns?.length || 0) === 0;
          const key = `${m.year}-${m.month}`;
          const reportEntry = byMonth[key];
          const hasReportHere = !!(reportEntry && reportEntry.summary);
          return (
            <button
              key={key}
              onClick={() => setMonthIdx(i)}
              disabled={isEmpty && !active}
              title={isEmpty ? 'אין עסקאות בחודש זה' : (hasReportHere ? `${m.label} · יש דוח` : m.label)}
              style={{
                padding: '6px 12px', borderRadius: 8,
                background: active ? 'var(--ink)' : 'var(--surface)',
                color: active ? 'var(--bg)' : (isEmpty ? 'var(--ink-soft)' : 'var(--ink)'),
                border: '0.5px solid var(--rule)',
                fontSize: 12, fontWeight: active ? 600 : 500,
                cursor: (isEmpty && !active) ? 'not-allowed' : 'pointer',
                opacity: (isEmpty && !active) ? 0.4 : 1,
                whiteSpace: 'nowrap',
                fontFeatureSettings: '"tnum"',
                position: 'relative',
              }}>
              {m.shortLabel} {String(m.year).slice(-2)}
              {hasReportHere && !active && (
                <span style={{
                  marginInlineStart: 6, fontSize: 9, color: 'var(--accent)',
                }}>✦</span>
              )}
            </button>
          );
        })}
      </div>

      <Surface style={{ marginBottom: 16, padding: 28 }}>
        <div style={{ fontSize: 11.5, color: 'var(--ink-soft)', fontWeight: 500, letterSpacing: '0.02em', marginBottom: 8 }}>תקציר</div>
        <div style={{ fontSize: 17, fontWeight: 500, lineHeight: 1.55, color: 'var(--ink)' }}>
          {insights?.summary || placeholderSummary}
        </div>
        {insights?.forecast && (
          <div style={{ marginTop: 16, padding: 14, background: 'var(--bg-deep)', borderRadius: 12, fontSize: 13, color: 'var(--ink-2)', lineHeight: 1.5 }}>
            <span style={{ fontWeight: 600, color: 'var(--ink)' }}>צפי: </span>{insights.forecast}
          </div>
        )}
      </Surface>

      <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
        {(insights?.bullets || []).map((b, i) => {
          const colors = { good: 'var(--positive)', warning: 'var(--alert)', info: 'var(--accent)' };
          const labels = { good: 'חיובי', warning: 'התראה', info: 'מידע' };
          return (
            <Surface key={i} className="slide-up" style={{ animationDelay: `${i*40}ms` }}>
              <div style={{ display: 'flex', alignItems: 'flex-start', gap: 14 }}>
                <span style={{ width: 28, height: 28, borderRadius: 8, background: `${colors[b.kind]}22`, color: colors[b.kind], display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 14, flexShrink: 0, fontWeight: 700 }}>
                  {b.kind === 'warning' ? '!' : b.kind === 'good' ? '✓' : 'i'}
                </span>
                <div style={{ flex: 1 }}>
                  <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 4 }}>
                    <span style={{ fontSize: 14, fontWeight: 600 }}>{b.title}</span>
                    <span style={{ fontSize: 10, fontWeight: 600, padding: '2px 7px', borderRadius: 4, background: `${colors[b.kind]}22`, color: colors[b.kind], textTransform: 'uppercase', letterSpacing: '0.04em' }}>{labels[b.kind]}</span>
                  </div>
                  <div style={{ fontSize: 13, lineHeight: 1.55, color: 'var(--ink-2)' }}>{b.text}</div>
                </div>
              </div>
            </Surface>
          );
        })}
      </div>
    </div>
  );
}

// Budgets, Subs, Goals, Settings — compact
function BudgetsPage() {
  const [editing, setEditing] = useState(false);
  const budgetEntries = Object.entries(D.budgets || {}).filter(([, v]) => v > 0);
  const isEmpty = budgetEntries.length === 0;
  return (
    <div className="fade-in" style={{ padding: '32px 40px 60px', maxWidth: 920, margin: '0 auto' }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-end', marginBottom: 22 }}>
        <h1 style={{ fontSize: 26, fontWeight: 600, letterSpacing: '-0.02em', margin: 0 }}>תקציבים</h1>
        <button onClick={() => setEditing(true)} style={{
          background: 'var(--ink)', color: 'var(--bg)', border: 'none', padding: '8px 14px',
          borderRadius: 10, fontSize: 12.5, fontWeight: 500, cursor: 'pointer',
        }}>{isEmpty ? '+ הגדר תקציבים' : 'ערוך'}</button>
      </div>
      <Surface padded={false}>
        {isEmpty && <EmptyState icon="◐" title="לא הוגדרו תקציבים עדיין"
          sub="הגדר מקסימום הוצאה חודשי לכל קטגוריה כדי לקבל פרוגרס בארים והתראות חריגה." />}
        {budgetEntries.map(([cat, lim], i) => {
          const spent = D.current.summary.byCat[cat] || 0;
          const pct = lim > 0 ? spent/lim : 0;
          const c = D.categories[cat] || {};
          const over = pct > 1;
          return (
            <div key={cat} style={{ padding: '16px 22px', borderTop: i ? '0.5px solid var(--rule)' : 'none' }}>
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8 }}>
                <span style={{ display: 'flex', gap: 10, alignItems: 'center' }}>
                  <span style={{ width: 28, height: 28, borderRadius: 8, background: `${c.color}22`, display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 13 }}>{c.icon}</span>
                  <span style={{ fontSize: 14, fontWeight: 500 }}>{cat}</span>
                </span>
                <span className="tnum" style={{ fontSize: 13 }}>
                  <span style={{ fontWeight: 600, color: over ? 'var(--alert)' : 'var(--ink)' }}>{fmtILS(spent)}</span>
                  <span style={{ color: 'var(--ink-soft)' }}> / {fmtILS(lim)}</span>
                </span>
              </div>
              <div style={{ height: 6, background: 'var(--bg-deep)', borderRadius: 99, overflow: 'hidden' }}>
                <div style={{ width: `${Math.min(100,pct*100)}%`, height: '100%', background: over ? 'var(--alert)' : c.color, borderRadius: 99 }} />
              </div>
            </div>
          );
        })}
      </Surface>
      {editing && <BudgetsEditModal onClose={() => setEditing(false)} />}
    </div>
  );
}

function BudgetsEditModal({ onClose }) {
  const cats = Object.keys(D.categories).filter(c => c !== 'הכנסה');
  const [values, setValues] = useState(() => {
    const obj = {};
    cats.forEach(c => { obj[c] = D.budgets[c] || ''; });
    return obj;
  });
  const [saving, setSaving] = useState(false);

  const save = async () => {
    setSaving(true);
    try {
      await Promise.all(cats.map(c => {
        const amt = parseFloat(values[c]) || 0;
        return apiFetch('/api/budgets', {
          method: 'PUT',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ category: c, amount: amt }),
        });
      }));
      await window.APP_DATA_REFRESH();
      onClose();
    } catch (err) {
      alert('שמירת התקציבים נכשלה: ' + err.message);
    } finally {
      setSaving(false);
    }
  };

  return (
    <div onClick={onClose} style={{
      position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.4)', backdropFilter: 'blur(4px)',
      zIndex: 90, display: 'flex', alignItems: 'flex-start', justifyContent: 'center', paddingTop: '8vh',
      animation: 'fade-in .2s',
    }}>
      <div onClick={e => e.stopPropagation()} className="slide-up" style={{
        width: 480, maxWidth: '90%', maxHeight: '80vh', background: 'var(--surface)',
        borderRadius: 16, boxShadow: 'var(--shadow-lg)', overflow: 'hidden',
        display: 'flex', flexDirection: 'column',
      }}>
        <div style={{ padding: '18px 22px', borderBottom: '0.5px solid var(--rule)', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
          <span style={{ fontSize: 15, fontWeight: 600 }}>תקציבים חודשיים</span>
          <span onClick={onClose} style={{ cursor: 'pointer', color: 'var(--ink-soft)', fontSize: 16 }}>✕</span>
        </div>
        <div className="thin-scroll" style={{ overflowY: 'auto', padding: '14px 22px' }}>
          <div style={{ fontSize: 12, color: 'var(--ink-soft)', marginBottom: 14, lineHeight: 1.5 }}>
            השאר ריק או 0 כדי שלא יהיה תקציב לקטגוריה. הסכומים בש"ח לחודש.
          </div>
          {cats.map(c => {
            const meta = D.categories[c] || {};
            return (
              <div key={c} style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '8px 0', borderBottom: '0.5px solid var(--rule)' }}>
                <span style={{ width: 26, height: 26, borderRadius: 7, background: `${meta.color}22`, display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 13 }}>{meta.icon}</span>
                <span style={{ flex: 1, fontSize: 13, fontWeight: 500 }}>{c}</span>
                <input type="number" min="0" step="50"
                  value={values[c]}
                  onChange={e => setValues(v => ({ ...v, [c]: e.target.value }))}
                  placeholder="ללא"
                  style={{ width: 110, padding: '6px 10px', borderRadius: 8, background: 'var(--bg-deep)',
                           border: '0.5px solid var(--rule)', fontSize: 13, outline: 'none', textAlign: 'right' }} />
                <span style={{ fontSize: 11, color: 'var(--ink-soft)' }}>₪</span>
              </div>
            );
          })}
        </div>
        <div style={{ padding: '14px 22px', borderTop: '0.5px solid var(--rule)', display: 'flex', gap: 8, justifyContent: 'flex-end' }}>
          <button onClick={onClose} style={{ padding: '9px 14px', borderRadius: 9, background: 'var(--surface)', border: '0.5px solid var(--rule)', cursor: 'pointer', fontSize: 13 }}>ביטול</button>
          <button onClick={save} disabled={saving} style={{ padding: '9px 18px', borderRadius: 9, background: 'var(--ink)', color: 'var(--bg)', border: 'none', cursor: saving ? 'not-allowed' : 'pointer', fontSize: 13, fontWeight: 600, opacity: saving ? 0.6 : 1 }}>
            {saving ? 'שומר…' : 'שמור'}
          </button>
        </div>
      </div>
    </div>
  );
}

// Per-provider metadata — UI labels, login URLs, brand colors
const PROVIDER_INFO = {
  max:      { label: 'Max',               sub: 'מקס (לשעבר לאומי קארד)',           url: 'https://www.max.co.il',          color: '#0a84ff' },
  visaCal:  { label: 'Visa Cal',          sub: 'ויזה כאל',                         url: 'https://www.cal-online.co.il',   color: '#5856d6' },
  isracard: { label: 'Isracard',          sub: 'ישראכרט (כולל Isracard Visa)',     url: 'https://www.isracard.co.il',     color: '#ff375f' },
  amex:     { label: 'American Express',  sub: 'אמריקן אקספרס (דרך ישראכרט)',      url: 'https://www.americanexpress.co.il', color: '#1a73e8' },
};

// Scrape modal — triggered from Dashboard "סנכרון עכשיו" / per-provider Connect cards
function ScrapeModal({ onClose, defaultProvider = 'max' }) {
  const [provider, setProvider] = useState(defaultProvider);
  // Restore last-used username per provider (NOT password — password never persists)
  const [username, setUsername] = useState(() => {
    try { return localStorage.getItem(`clibank.user.${defaultProvider}`) || ''; } catch { return ''; }
  });
  const [password, setPassword] = useState('');
  const [months, setMonths] = useState(3);
  const [rememberUser, setRememberUser] = useState(true);
  const [status, setStatus] = useState(null); // null | 'running' | 'done' | 'error'
  const [message, setMessage] = useState('');

  // When user switches provider in the dropdown, swap the saved username for it
  const onProviderChange = (e) => {
    const p = e.target.value;
    setProvider(p);
    try { setUsername(localStorage.getItem(`clibank.user.${p}`) || ''); } catch { setUsername(''); }
    setPassword('');
  };

  const info = PROVIDER_INFO[provider] || PROVIDER_INFO.max;

  const submit = async (e) => {
    e.preventDefault();
    if (rememberUser) {
      try { localStorage.setItem(`clibank.user.${provider}`, username); } catch {}
    } else {
      try { localStorage.removeItem(`clibank.user.${provider}`); } catch {}
    }
    setStatus('running');
    setMessage(`מתחבר ל-${info.label} דרך Cli-Bank… זה יכול לקחת דקה-שתיים.`);
    try {
      // show_browser=false because the scraper runs on Railway with no
      // display server. CAPTCHA/2FA prompts (if the issuer requires them)
      // would have to be handled by the scraper itself, not the user.
      const r = await apiFetch('/api/scrape', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ provider, username, password, months: +months, show_browser: false }),
      });
      setPassword('');
      if (!r.ok) {
        const err = await r.json().catch(() => ({}));
        const t = err.detail?.errorType || 'UNKNOWN';
        const m = err.detail?.message || 'שגיאה במשיכה';
        throw new Error(`[${t}] ${m}`);
      }
      const data = await r.json();
      setStatus('done');
      setMessage(`נמשכו ${data.new_count} עסקאות חדשות מ-${info.label} (סך הכל ${data.total_seen}).`);
      await window.APP_DATA_REFRESH();
    } catch (err) {
      setStatus('error');
      setMessage(err.message || 'שגיאה');
    }
  };

  return (
    <div onClick={status === 'running' ? null : onClose} style={{
      position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.4)', backdropFilter: 'blur(4px)',
      zIndex: 95, display: 'flex', alignItems: 'flex-start', justifyContent: 'center', paddingTop: '8vh',
      animation: 'fade-in .2s',
    }}>
      <form onClick={e => e.stopPropagation()} onSubmit={submit} className="slide-up" style={{
        width: 460, maxWidth: '90%', background: 'var(--surface)',
        borderRadius: 16, boxShadow: 'var(--shadow-lg)', overflow: 'hidden',
      }}>
        <div style={{ padding: '18px 22px', borderBottom: '0.5px solid var(--rule)', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
          <span style={{ fontSize: 15, fontWeight: 600 }}>חיבור {info.label}</span>
          <span onClick={onClose} style={{ cursor: 'pointer', color: 'var(--ink-soft)', fontSize: 16 }}>✕</span>
        </div>
        <div style={{ padding: 22 }}>
          {!status && (
            <>
              {/* Brand strip */}
              <div style={{
                display: 'flex', alignItems: 'center', gap: 12,
                padding: '12px 14px', borderRadius: 12,
                background: `${info.color}11`, border: `0.5px solid ${info.color}33`,
                marginBottom: 14,
              }}>
                <div style={{
                  width: 38, height: 38, borderRadius: 10, background: info.color,
                  color: 'white', fontWeight: 700, fontSize: 11,
                  display: 'flex', alignItems: 'center', justifyContent: 'center',
                  letterSpacing: '0.04em',
                }}>{info.label.slice(0, 3).toUpperCase()}</div>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontSize: 13.5, fontWeight: 600 }}>{info.label}</div>
                  <div style={{ fontSize: 11, color: 'var(--ink-soft)', marginTop: 2 }}>{info.sub}</div>
                </div>
                <a href={info.url} target="_blank" rel="noopener noreferrer" style={{
                  fontSize: 11, color: info.color, fontWeight: 500,
                  textDecoration: 'none', whiteSpace: 'nowrap',
                }}>פתח באתר ↗</a>
              </div>

              {/* Explanation banner */}
              <div style={{ background: 'var(--bg-deep)', padding: 12, borderRadius: 10, fontSize: 11.5, color: 'var(--ink-2)', marginBottom: 14, lineHeight: 1.6 }}>
                <div style={{ marginBottom: 4 }}>
                  <span style={{ fontWeight: 600 }}>איך זה עובד:</span> כשתלחץ "התחבר", חלון Chrome אמיתי של אתר {info.label} ייפתח. הסקרייפר ינסה להיכנס אוטומטית; אם יש CAPTCHA או OTP — תשלים אותם בחלון בעצמך. הנתונים יישלפו ישירות מהאתר הרשמי.
                </div>
                <div style={{ color: 'var(--ink-soft)' }}>
                  🔒 הסיסמה לא נשמרת אצלנו אף פעם — היא עוברת רק לחלון הדפדפן ונמחקת מיד.
                </div>
              </div>

              <Field label="חברת אשראי">
                <select value={provider} onChange={onProviderChange} style={inpStyle}>
                  <option value="max">Max — מקס</option>
                  <option value="visaCal">Visa Cal — ויזה כאל</option>
                  <option value="isracard">Isracard — ישראכרט (כולל Visa)</option>
                  <option value="amex">American Express — אמריקן אקספרס</option>
                </select>
              </Field>
              <Field label="שם משתמש / ת.ז.">
                <input value={username} onChange={e => setUsername(e.target.value)} required autoComplete="off" style={inpStyle} placeholder="לרוב מספר ת.ז." />
              </Field>
              <Field label="סיסמה">
                <input type="password" value={password} onChange={e => setPassword(e.target.value)} required autoComplete="off" style={inpStyle} placeholder="הסיסמה לאתר חברת האשראי" />
              </Field>
              <Field label="טווח חודשים אחורה">
                <input type="number" min="1" max="24" value={months} onChange={e => setMonths(e.target.value)} style={inpStyle} />
              </Field>
              <label style={{ display: 'flex', alignItems: 'center', gap: 8, marginTop: 6, fontSize: 12.5, cursor: 'pointer', color: 'var(--ink-2)' }}>
                <input type="checkbox" checked={rememberUser} onChange={e => setRememberUser(e.target.checked)} />
                זכור את שם המשתמש בפעם הבאה <span style={{ color: 'var(--ink-soft)' }}>(הסיסמה לא נשמרת)</span>
              </label>
            </>
          )}
          {status === 'running' && (
            <div style={{ padding: '20px 0', textAlign: 'center' }}>
              <div className="splash-spinner" style={{ margin: '0 auto 14px', width: 26, height: 26, border: '3px solid var(--rule)', borderTopColor: 'var(--ink)', borderRadius: '50%', animation: 'spin .9s linear infinite' }} />
              <div style={{ fontSize: 13, color: 'var(--ink-2)' }}>{message}</div>
            </div>
          )}
          {status === 'done' && (
            <div style={{ padding: '12px 0', textAlign: 'center' }}>
              <div style={{ fontSize: 30, marginBottom: 6 }}>✓</div>
              <div style={{ fontSize: 13.5, fontWeight: 600 }}>{message}</div>
            </div>
          )}
          {status === 'error' && (
            <div style={{ padding: 14, background: 'rgba(216,58,58,0.08)', border: '0.5px solid rgba(216,58,58,0.2)', borderRadius: 10, fontSize: 13, color: 'var(--alert)', whiteSpace: 'pre-wrap' }}>
              {message}
            </div>
          )}
        </div>
        <div style={{ padding: '14px 22px', borderTop: '0.5px solid var(--rule)', display: 'flex', gap: 8, justifyContent: 'flex-end' }}>
          {(status === 'done' || status === 'error') && (
            <button type="button" onClick={onClose} style={{ padding: '9px 18px', borderRadius: 9, background: 'var(--ink)', color: 'var(--bg)', border: 'none', cursor: 'pointer', fontSize: 13, fontWeight: 600 }}>סגור</button>
          )}
          {status === 'error' && (
            <button type="button" onClick={() => setStatus(null)} style={{ padding: '9px 14px', borderRadius: 9, background: 'var(--surface)', border: '0.5px solid var(--rule)', cursor: 'pointer', fontSize: 13 }}>נסה שוב</button>
          )}
          {!status && (
            <>
              <button type="button" onClick={onClose} style={{ padding: '9px 14px', borderRadius: 9, background: 'var(--surface)', border: '0.5px solid var(--rule)', cursor: 'pointer', fontSize: 13 }}>ביטול</button>
              <button type="submit" style={{
                padding: '9px 18px', borderRadius: 9,
                background: info.color, color: 'white', border: 'none',
                cursor: 'pointer', fontSize: 13, fontWeight: 600,
                display: 'inline-flex', alignItems: 'center', gap: 6,
              }}>
                <span style={{ fontSize: 11 }}>↗</span> התחבר ל-{info.label}
              </button>
            </>
          )}
        </div>
      </form>
    </div>
  );
}

const inpStyle = { width: '100%', padding: '8px 10px', borderRadius: 9, background: 'var(--bg-deep)', border: '0.5px solid var(--rule)', fontSize: 13, outline: 'none', boxSizing: 'border-box' };
function Field({ label, children }) {
  return (
    <div style={{ marginBottom: 12 }}>
      <div style={{ fontSize: 11.5, color: 'var(--ink-soft)', marginBottom: 5, fontWeight: 500 }}>{label}</div>
      {children}
    </div>
  );
}

function SubsPage() {
  const { dataVersion } = useApp();
  const [filter, setFilter] = useState('all'); // all | used | unused | high
  const [subs, setSubs] = useState(D.subscriptions || []);
  const [loading, setLoading] = useState(false);
  const [editing, setEditing] = useState(null); // currently-edited subscription object

  // Refetch when data refreshes — hidden items always filtered out
  useEffect(() => {
    let cancelled = false;
    setLoading(true);
    apiFetch(`/api/subscriptions?months_back=12&min_months=3&include_hidden=false`)
      .then(r => r.json())
      .then(data => { if (!cancelled) setSubs(data); })
      .catch(() => { if (!cancelled) setSubs(D.subscriptions || []); })
      .finally(() => { if (!cancelled) setLoading(false); });
    return () => { cancelled = true; };
  }, [dataVersion]);

  const visibleSubs = subs;

  const filtered = useMemo(() => {
    let arr = visibleSubs.slice();
    if (filter === 'used') arr = arr.filter(s => s.used);
    if (filter === 'unused') arr = arr.filter(s => !s.used);
    if (filter === 'high') arr = arr.filter(s => s.amount >= 100);
    return arr;
  }, [filter, visibleSubs.length]);

  const total = filtered.reduce((s,x) => s + x.amount, 0);
  const totalUsed = visibleSubs.filter(s => s.used).reduce((s,x) => s + x.amount, 0);
  const totalUnused = visibleSubs.filter(s => !s.used).reduce((s,x) => s + x.amount, 0);

  // Hard delete — sub disappears from the page; data is preserved server-side
  // as a hidden override so it never auto-detects again on future scrapes.
  const removeSub = async (sub) => {
    if (!confirm(`למחוק את "${sub.name}" מרשימת המנויים?\n\nהעסקאות עצמן לא יימחקו, אבל הספק לא יזוהה כמנוי שוב.`)) return;
    try {
      await apiFetch(`/api/subscriptions/${encodeURIComponent(sub.key)}`, {
        method: 'PATCH',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ hidden: true }),
      });
      // Remove from local state immediately
      setSubs(prev => prev.filter(s => s.key !== sub.key));
    } catch (e) {
      alert('מחיקה נכשלה: ' + e.message);
    }
  };

  return (
    <div className="fade-in" style={{ padding: '32px 40px 60px', maxWidth: 920, margin: '0 auto' }}>
      <div style={{ marginBottom: 18 }}>
        <h1 style={{ fontSize: 26, fontWeight: 600, letterSpacing: '-0.02em', margin: '0 0 4px' }}>מנויים</h1>
        <div style={{ fontSize: 12.5, color: 'var(--ink-soft)' }}>
          זוהו אוטומטית מהיסטוריית העסקאות · 12 חודשים אחרונים · גם כל מה שסיווגת ידנית כ-"חשבונות ומנויים"
        </div>
      </div>

      {subs.length === 0 ? (
        <Surface><EmptyState
          icon="↻"
          title="לא זוהו מנויים פעילים"
          sub="זוהו פחות מ-3 חיובים מאותו ספק בחודשים השונים. ככל שיהיו לך יותר נתונים היסטוריים — הזיהוי ישתפר."
        /></Surface>
      ) : (
        <>
          {/* Hero stats */}
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 12, marginBottom: 16 }}>
            <Surface>
              <div style={{ fontSize: 11.5, color: 'var(--ink-soft)', fontWeight: 500, marginBottom: 6 }}>סך כל החיובים</div>
              <Money value={subs.reduce((s,x) => s + x.amount, 0)} size={26} weight={700} />
              <div style={{ fontSize: 11, color: 'var(--ink-soft)', marginTop: 4 }}>{subs.length} ספקים · לחודש</div>
            </Surface>
            <Surface>
              <div style={{ fontSize: 11.5, color: 'var(--ink-soft)', fontWeight: 500, marginBottom: 6 }}>חיובים פעילים</div>
              <Money value={totalUsed} size={26} weight={700} color="var(--positive)" />
              <div style={{ fontSize: 11, color: 'var(--ink-soft)', marginTop: 4 }}>{subs.filter(s => s.used).length} ב-45 ימים אחרונים</div>
            </Surface>
            <Surface>
              <div style={{ fontSize: 11.5, color: 'var(--ink-soft)', fontWeight: 500, marginBottom: 6 }}>חיובים רדומים</div>
              <Money value={totalUnused} size={26} weight={700} color={totalUnused > 0 ? 'var(--alert)' : 'var(--ink-soft)'} />
              <div style={{ fontSize: 11, color: 'var(--ink-soft)', marginTop: 4 }}>{subs.filter(s => !s.used).length} שלא חויבו לאחרונה</div>
            </Surface>
          </div>

          {/* Filters */}
          <div style={{ display: 'flex', gap: 8, marginBottom: 14, alignItems: 'center', flexWrap: 'wrap' }}>
            <SubFilterChip label={`הכל · ${subs.length}`} on={filter==='all'} onClick={() => setFilter('all')} />
            <SubFilterChip label={`פעילים · ${subs.filter(s=>s.used).length}`} on={filter==='used'} onClick={() => setFilter('used')} />
            <SubFilterChip label={`רדומים · ${subs.filter(s=>!s.used).length}`} on={filter==='unused'} onClick={() => setFilter('unused')} />
            <SubFilterChip label="100₪+" on={filter==='high'} onClick={() => setFilter('high')} />
            <span style={{ flex: 1 }} />
            <span style={{ fontSize: 11, color: 'var(--ink-soft)' }}>
              סינון: {fmtILS(total)}/חודש · {fmtILS(total*12)}/שנה
            </span>
          </div>

          <Surface padded={false}>
            {filtered.map((s, i) => {
              const cat = D.categories[s.category] || { color: 'var(--ink-soft)', icon: '·' };
              const lastSeen = new Date(s.last_seen);
              const daysAgo = Math.round((Date.now() - lastSeen.getTime()) / (1000 * 60 * 60 * 24));
              const variance = s.variance_pct;
              const isStrict = variance < 5;   // very tight = recurring fixed bill
              const isLoose = variance > 25;   // wider = could be just frequent merchant
              return (
                <div key={s.id} style={{
                  padding: '14px 22px', display: 'flex', alignItems: 'center', gap: 14,
                  borderTop: i ? '0.5px solid var(--rule)' : 'none',
                  opacity: s.hidden ? 0.45 : (s.used ? 1 : 0.7),
                  background: s.hidden ? 'rgba(216,58,58,0.04)' : 'transparent',
                }}>
                  <div style={{
                    width: 36, height: 36, borderRadius: 9,
                    background: `${cat.color}22`, color: cat.color,
                    display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 14,
                    flexShrink: 0,
                  }}>{cat.icon}</div>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ fontSize: 13.5, fontWeight: 500, display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' }}>
                      <span style={{ whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', maxWidth: 320 }}>
                        {s.name}
                      </span>
                      {s.edited && <span title="ערוך ידנית" style={{ fontSize: 9, color: 'var(--accent)' }}>✎</span>}
                      {s.hidden && <span style={{ fontSize: 9, padding: '2px 6px', background: 'rgba(216,58,58,0.18)', color: 'var(--alert)', borderRadius: 4, fontWeight: 600 }}>מוסתר</span>}
                      {!s.hidden && isStrict && <span title="סכום קבוע — מנוי ודאי" style={{ fontSize: 9, padding: '2px 6px', background: 'rgba(48,164,108,0.12)', color: 'var(--positive)', borderRadius: 4, fontWeight: 600 }}>קבוע</span>}
                      {!s.hidden && isLoose && <span title="סכומים משתנים — אולי לא מנוי קבוע" style={{ fontSize: 9, padding: '2px 6px', background: 'rgba(216,58,58,0.12)', color: 'var(--alert)', borderRadius: 4, fontWeight: 600 }}>משתנה</span>}
                    </div>
                    <div style={{ fontSize: 11, color: 'var(--ink-soft)', marginTop: 2 }}>
                      {s.category} · {s.months} חודשים · {s.occurrences} חיובים
                      {' · '}
                      <span style={{ color: s.used ? 'var(--ink-soft)' : 'var(--alert)' }}>
                        {daysAgo === 0 ? 'חויב היום' : `לפני ${daysAgo} ימים`}
                      </span>
                    </div>
                  </div>
                  {!s.used && !s.hidden && (
                    <span style={{ fontSize: 10.5, padding: '3px 9px', background: 'rgba(216,58,58,0.12)', color: 'var(--alert)', borderRadius: 999, fontWeight: 600, whiteSpace: 'nowrap' }}>
                      רדום
                    </span>
                  )}
                  <div style={{ textAlign: 'left', flexShrink: 0 }}>
                    <div className="tnum" style={{ fontSize: 14, fontWeight: 600 }}>{fmtILS(-s.amount)}</div>
                    <div className="tnum" style={{ fontSize: 10, color: 'var(--ink-soft)', fontFamily: 'var(--font-mono)' }}>
                      {fmtILS(-s.annual_estimate)}/שנה
                    </div>
                  </div>
                  {/* Action buttons */}
                  <div style={{ display: 'flex', gap: 4, flexShrink: 0 }}>
                    <button
                      onClick={() => setEditing(s)}
                      title="ערוך"
                      style={{
                        padding: '6px 9px', borderRadius: 7,
                        background: 'transparent', color: 'var(--ink-soft)',
                        border: '0.5px solid var(--rule)', cursor: 'pointer',
                        fontSize: 12, fontWeight: 500,
                      }}>✎</button>
                    <button
                      onClick={() => removeSub(s)}
                      title="מחק מרשימת המנויים"
                      style={{
                        padding: '6px 9px', borderRadius: 7,
                        background: 'transparent', color: 'var(--alert)',
                        border: '0.5px solid rgba(216,58,58,0.3)', cursor: 'pointer',
                        fontSize: 12, fontWeight: 500,
                      }}>🗑</button>
                  </div>
                </div>
              );
            })}
            {filtered.length === 0 && (
              <EmptyState icon="⌕" title="אין תוצאות" sub="נסה פילטר אחר." />
            )}
          </Surface>

          <div style={{
            marginTop: 18, padding: 14, background: 'var(--bg-deep)', borderRadius: 12,
            fontSize: 11, color: 'var(--ink-soft)', lineHeight: 1.6,
          }}>
            <span style={{ fontWeight: 600, color: 'var(--ink-2)' }}>איך הזיהוי עובד:</span>{' '}
            סורק את 12 החודשים האחרונים. כל ספק שמחויב ב-3+ חודשים שונים נחשב חיוב חוזר. <b>קבוע</b> = פחות מ-5% שינוי בסכום (כמו Netflix). <b>משתנה</b> = יותר מ-25% (כמו תחנת דלק שאתה מבקר בה לעיתים קרובות). <b>רדום</b> = לא היה חיוב ב-45 הימים האחרונים — שווה לבדוק שאין תשלום ששכחת.
          </div>
        </>
      )}

      {editing && <SubscriptionEditModal
        sub={editing}
        onClose={() => setEditing(null)}
        onSaved={(updated) => {
          setSubs(prev => prev.map(s => s.key === updated.key ? updated : s));
          setEditing(null);
        }}
        onReset={(key) => {
          // Reset = removed all overrides; refetch to get clean detection
          apiFetch(`/api/subscriptions?months_back=12&min_months=3&include_hidden=false`)
            .then(r => r.json())
            .then(setSubs)
            .catch(() => {});
          setEditing(null);
        }}
      />}
    </div>
  );
}

function SubscriptionEditModal({ sub, onClose, onSaved, onReset }) {
  const [name, setName] = useState(sub.name);
  const [category, setCategory] = useState(sub.category);
  const [hidden, setHidden] = useState(!!sub.hidden);
  const [notes, setNotes] = useState(sub.notes || '');
  const [saving, setSaving] = useState(false);

  const categories = Object.keys(D.categories || {});
  const hasOverrides = sub.edited;

  const save = async (e) => {
    e?.preventDefault();
    setSaving(true);
    try {
      // Only send changed fields; null leaves them unchanged
      const body = {
        display_name: name === sub.original_name ? '' : name,
        category: category === sub.original_category ? '' : category,
        hidden,
        notes,
      };
      const r = await apiFetch(`/api/subscriptions/${encodeURIComponent(sub.key)}`, {
        method: 'PATCH',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(body),
      });
      if (!r.ok) {
        const err = await r.json().catch(() => ({}));
        throw new Error(err.detail || 'שמירה נכשלה');
      }
      onSaved({
        ...sub,
        name,
        category,
        hidden,
        notes,
        edited: true,
      });
    } catch (err) {
      alert('שגיאה: ' + err.message);
    } finally {
      setSaving(false);
    }
  };

  const resetOverrides = async () => {
    if (!confirm('לבטל את כל העריכות ולחזור לזיהוי האוטומטי המקורי?')) return;
    setSaving(true);
    try {
      await apiFetch(`/api/subscriptions/${encodeURIComponent(sub.key)}/override`, { method: 'DELETE' });
      onReset(sub.key);
    } catch (err) {
      alert('שגיאה: ' + err.message);
    } finally {
      setSaving(false);
    }
  };

  return (
    <div onClick={onClose} style={{
      position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.4)', backdropFilter: 'blur(4px)',
      zIndex: 95, display: 'flex', alignItems: 'flex-start', justifyContent: 'center', paddingTop: '8vh',
      animation: 'fade-in .2s',
    }}>
      <form onClick={e => e.stopPropagation()} onSubmit={save} className="slide-up" style={{
        width: 480, maxWidth: '90%', background: 'var(--surface)',
        borderRadius: 16, boxShadow: 'var(--shadow-lg)', overflow: 'hidden',
      }}>
        <div style={{ padding: '18px 22px', borderBottom: '0.5px solid var(--rule)', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
          <div>
            <div style={{ fontSize: 15, fontWeight: 600 }}>עריכת מנוי</div>
            <div style={{ fontSize: 11, color: 'var(--ink-soft)', marginTop: 2, fontFamily: 'var(--font-mono)' }}>{sub.key}</div>
          </div>
          <span onClick={onClose} style={{ cursor: 'pointer', color: 'var(--ink-soft)', fontSize: 16 }}>✕</span>
        </div>
        <div style={{ padding: 22 }}>
          {/* Read-only metadata */}
          <div style={{
            background: 'var(--bg-deep)', padding: 12, borderRadius: 10,
            display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 10,
            marginBottom: 16, fontSize: 11.5,
          }}>
            <div>
              <div style={{ color: 'var(--ink-soft)', marginBottom: 2 }}>סכום ממוצע</div>
              <span className="tnum" style={{ fontWeight: 600 }}>{fmtILS(sub.amount)}</span>
            </div>
            <div>
              <div style={{ color: 'var(--ink-soft)', marginBottom: 2 }}>חיובים</div>
              <span className="tnum" style={{ fontWeight: 600 }}>{sub.occurrences} ב-{sub.months} חודשים</span>
            </div>
            <div>
              <div style={{ color: 'var(--ink-soft)', marginBottom: 2 }}>שונות</div>
              <span className="tnum" style={{ fontWeight: 600 }}>{sub.variance_pct}%</span>
            </div>
          </div>

          <Field label="שם תצוגה">
            <input value={name} onChange={e => setName(e.target.value)} style={inpStyle} />
          </Field>
          {sub.original_name !== name && (
            <div style={{ fontSize: 10.5, color: 'var(--ink-soft)', marginTop: -8, marginBottom: 12 }}>
              מקורי: <span style={{ fontFamily: 'var(--font-mono)' }}>{sub.original_name}</span>
            </div>
          )}

          <Field label="קטגוריה">
            <select value={category} onChange={e => setCategory(e.target.value)} style={inpStyle}>
              {categories.map(c => <option key={c} value={c}>{c}</option>)}
            </select>
          </Field>

          <Field label="הערות (אופציונלי)">
            <textarea value={notes} onChange={e => setNotes(e.target.value)}
              placeholder="למשל: 'ביטלתי ב-1.5', 'מנוי משפחתי', 'לבטל בעוד 3 חודשים'…"
              rows={2}
              style={{ ...inpStyle, resize: 'vertical', minHeight: 50, fontFamily: 'inherit' }} />
          </Field>

          <label style={{
            display: 'flex', alignItems: 'flex-start', gap: 10, marginTop: 4,
            padding: 12, background: hidden ? 'rgba(216,58,58,0.08)' : 'var(--bg-deep)',
            borderRadius: 10, cursor: 'pointer',
          }}>
            <input type="checkbox" checked={hidden} onChange={e => setHidden(e.target.checked)} style={{ marginTop: 2 }} />
            <div>
              <div style={{ fontSize: 12.5, fontWeight: 500 }}>הסתר מהרשימה</div>
              <div style={{ fontSize: 11, color: 'var(--ink-soft)', marginTop: 3, lineHeight: 1.5 }}>
                למקרים שזו לא באמת חיוב חוזר (כמו תחנת דלק שמופיעה כל חודש). תוכל להציג מוסתרים שוב דרך ה-checkbox למעלה.
              </div>
            </div>
          </label>
        </div>
        <div style={{ padding: '14px 22px', borderTop: '0.5px solid var(--rule)', display: 'flex', gap: 8, justifyContent: 'space-between', alignItems: 'center' }}>
          {hasOverrides ? (
            <button type="button" onClick={resetOverrides} disabled={saving} style={{
              padding: '8px 12px', borderRadius: 9, background: 'transparent',
              border: 'none', color: 'var(--ink-soft)', fontSize: 11.5, cursor: saving ? 'wait' : 'pointer',
            }}>↩ אפס לזיהוי אוטומטי</button>
          ) : <span />}
          <div style={{ display: 'flex', gap: 8 }}>
            <button type="button" onClick={onClose} disabled={saving} style={{ padding: '9px 14px', borderRadius: 9, background: 'var(--surface)', border: '0.5px solid var(--rule)', cursor: saving ? 'wait' : 'pointer', fontSize: 13 }}>ביטול</button>
            <button type="submit" disabled={saving} style={{ padding: '9px 18px', borderRadius: 9, background: 'var(--ink)', color: 'var(--bg)', border: 'none', cursor: saving ? 'wait' : 'pointer', fontSize: 13, fontWeight: 600 }}>
              {saving ? 'שומר…' : 'שמור'}
            </button>
          </div>
        </div>
      </form>
    </div>
  );
}

function SubFilterChip({ label, on, onClick }) {
  return (
    <button onClick={onClick} style={{
      padding: '6px 12px', borderRadius: 99,
      background: on ? 'var(--ink)' : 'var(--surface)',
      color: on ? 'var(--bg)' : 'var(--ink-2)',
      border: on ? 'none' : '0.5px solid var(--rule)',
      fontSize: 12, fontWeight: 500, cursor: 'pointer',
      boxShadow: on ? 'none' : 'var(--shadow-sm)',
    }}>{label}</button>
  );
}

function GoalsPage() {
  const goals = D.goals || [];
  const [editing, setEditing] = useState(null); // null | 'new' | <goal_object>

  const totalTarget = goals.reduce((s, g) => s + (g.target_amount || 0), 0);
  const totalSaved = goals.reduce((s, g) => s + (g.saved_amount || 0), 0);

  return (
    <div className="fade-in" style={{ padding: '32px 40px 60px', maxWidth: 1100, margin: '0 auto' }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-end', marginBottom: 22 }}>
        <div>
          <h1 style={{ fontSize: 26, fontWeight: 600, letterSpacing: '-0.02em', margin: '0 0 4px' }}>יעדים</h1>
          <div style={{ fontSize: 12.5, color: 'var(--ink-soft)' }}>
            {goals.length === 0 ? 'אין יעדים פעילים — הוסף יעד חיסכון כדי לעקוב אחריו' :
              `${goals.length} יעדים · ${fmtILS(totalSaved)} מתוך ${fmtILS(totalTarget)} (${Math.round((totalSaved/totalTarget)*100)}%)`}
          </div>
        </div>
        <button onClick={() => setEditing('new')} style={{
          background: 'var(--ink)', color: 'var(--bg)', border: 'none', padding: '8px 14px',
          borderRadius: 10, fontSize: 12.5, fontWeight: 500, cursor: 'pointer',
        }}>+ יעד חדש</button>
      </div>

      {goals.length === 0 ? (
        <Surface>
          <EmptyState
            icon="★"
            title="עוד אין לך יעדים"
            sub="הגדר יעדי חיסכון — דירה ראשונה, טיול ליפן, קופת חירום — וקבל התקדמות חזותית בכל פעם שתפקיד כסף לחיסכון."
            action={<button onClick={() => setEditing('new')} style={{
              padding: '9px 18px', borderRadius: 9, background: 'var(--ink)',
              color: 'var(--bg)', border: 'none', cursor: 'pointer',
              fontSize: 13, fontWeight: 600,
            }}>+ צור יעד ראשון</button>}
          />
        </Surface>
      ) : (
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(280px, 1fr))', gap: 14 }}>
          {goals.map(g => {
            const pct = (g.target_amount > 0) ? (g.saved_amount / g.target_amount) : 0;
            const cappedPct = Math.min(1, pct);
            const remaining = Math.max(0, g.target_amount - g.saved_amount);
            const color = g.color || 'var(--accent)';
            return (
              <Surface key={g.id} onClick={() => setEditing(g)} style={{ cursor: 'pointer', position: 'relative' }}>
                <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 6 }}>
                  <div style={{ fontSize: 14, fontWeight: 600 }}>{g.name}</div>
                  {pct >= 1 && <span style={{ fontSize: 10, padding: '2px 7px', borderRadius: 4, background: 'rgba(48,164,108,0.15)', color: 'var(--positive)', fontWeight: 600 }}>הושג ✓</span>}
                </div>
                {g.deadline && (
                  <div style={{ fontSize: 11, color: 'var(--ink-soft)', marginBottom: 14 }}>
                    עד {fmtDate(g.deadline.slice(0, 10))}
                  </div>
                )}
                <div className="tnum" style={{ fontSize: 26, fontWeight: 700, letterSpacing: '-0.02em' }}>{fmtILS(g.saved_amount)}</div>
                <div style={{ fontSize: 11.5, color: 'var(--ink-soft)', marginBottom: 10 }}>
                  מתוך {fmtILS(g.target_amount)} · {Math.round(pct*100)}%
                  {remaining > 0 && <> · נותרו {fmtILS(remaining)}</>}
                </div>
                <div style={{ height: 8, background: 'var(--bg-deep)', borderRadius: 99, overflow: 'hidden' }}>
                  <div style={{
                    width: `${cappedPct*100}%`, height: '100%', background: color,
                    borderRadius: 99, transition: 'width .5s cubic-bezier(.2,.8,.3,1)',
                  }} />
                </div>
                {g.notes && (
                  <div style={{ fontSize: 11, color: 'var(--ink-soft)', marginTop: 12, lineHeight: 1.5, fontStyle: 'italic' }}>
                    {g.notes}
                  </div>
                )}
              </Surface>
            );
          })}
        </div>
      )}

      {editing && <GoalEditModal
        goal={editing === 'new' ? null : editing}
        onClose={() => setEditing(null)}
        onSaved={async () => {
          await window.APP_DATA_REFRESH();
          setEditing(null);
        }}
      />}
    </div>
  );
}

function AccountEditModal({ account, onClose, onSaved }) {
  const [name, setName] = useState(account.name || '');
  const [holder, setHolder] = useState((account.holder || '').replace(/^•+\s*/, ''));
  const [color, setColor] = useState(account.color || '');
  const [saving, setSaving] = useState(false);

  const colors = ['#0a84ff','#5856d6','#ff375f','#1a73e8','#30a46c','#f59e0b','#7c5cff','#ec4899'];

  const submit = async (e) => {
    e.preventDefault();
    if (!name.trim()) { alert('שם נדרש'); return; }
    setSaving(true);
    try {
      const r = await apiFetch(`/api/accounts/${account.account_id}`, {
        method: 'PATCH',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          display_name: name.trim(),
          holder_name: holder.trim() || null,
          color: color || null,
        }),
      });
      if (!r.ok) {
        const err = await r.json().catch(() => ({}));
        throw new Error(err.detail || 'שמירה נכשלה');
      }
      await onSaved();
    } catch (err) {
      alert('שגיאה: ' + err.message);
    } finally {
      setSaving(false);
    }
  };

  return (
    <div onClick={onClose} style={{
      position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.4)', backdropFilter: 'blur(4px)',
      zIndex: 95, display: 'flex', alignItems: 'flex-start', justifyContent: 'center', paddingTop: '8vh',
      animation: 'fade-in .2s',
    }}>
      <form onClick={e => e.stopPropagation()} onSubmit={submit} className="slide-up" style={{
        width: 440, maxWidth: '90%', background: 'var(--surface)',
        borderRadius: 16, boxShadow: 'var(--shadow-lg)', overflow: 'hidden',
      }}>
        <div style={{ padding: '18px 22px', borderBottom: '0.5px solid var(--rule)', display: 'flex', justifyContent: 'space-between' }}>
          <span style={{ fontSize: 15, fontWeight: 600 }}>עריכת כרטיס</span>
          <span onClick={onClose} style={{ cursor: 'pointer', color: 'var(--ink-soft)', fontSize: 16 }}>✕</span>
        </div>
        <div style={{ padding: 22 }}>
          <Field label="שם תצוגה">
            <input value={name} onChange={e => setName(e.target.value)} required
              style={inpStyle} placeholder="כינוי לכרטיס" />
          </Field>
          <Field label="בעל הכרטיס (אופציונלי)">
            <input value={holder} onChange={e => setHolder(e.target.value)}
              style={inpStyle} placeholder="דניאל / מירי / משותף" />
          </Field>
          <Field label="צבע">
            <div style={{ display: 'flex', gap: 6 }}>
              {colors.map(c => (
                <button key={c} type="button" onClick={() => setColor(c)} style={{
                  width: 28, height: 28, borderRadius: 8, background: c,
                  border: color === c ? '2px solid var(--ink)' : '0.5px solid var(--rule)',
                  cursor: 'pointer',
                }} />
              ))}
            </div>
          </Field>
        </div>
        <div style={{ padding: '14px 22px', borderTop: '0.5px solid var(--rule)', display: 'flex', gap: 8, justifyContent: 'flex-end' }}>
          <button type="button" onClick={onClose} disabled={saving} style={{ padding: '9px 14px', borderRadius: 9, background: 'var(--surface)', border: '0.5px solid var(--rule)', cursor: saving ? 'wait' : 'pointer', fontSize: 13 }}>ביטול</button>
          <button type="submit" disabled={saving} style={{ padding: '9px 18px', borderRadius: 9, background: 'var(--ink)', color: 'var(--bg)', border: 'none', cursor: saving ? 'wait' : 'pointer', fontSize: 13, fontWeight: 600 }}>
            {saving ? 'שומר…' : 'שמור'}
          </button>
        </div>
      </form>
    </div>
  );
}

function GoalEditModal({ goal, onClose, onSaved }) {
  const isNew = !goal;
  const [name, setName] = useState(goal?.name || '');
  const [target, setTarget] = useState(goal?.target_amount || '');
  const [saved, setSaved] = useState(goal?.saved_amount || 0);
  const [deadline, setDeadline] = useState(goal?.deadline ? goal.deadline.slice(0, 10) : '');
  const [color, setColor] = useState(goal?.color || '#0a84ff');
  const [notes, setNotes] = useState(goal?.notes || '');
  const [saving, setSaving] = useState(false);

  const colors = ['#0a84ff','#5856d6','#ff375f','#30a46c','#f59e0b','#7c5cff','#ec4899','#14b8a6'];

  const submit = async (e) => {
    e.preventDefault();
    if (!name.trim()) { alert('שם יעד נדרש'); return; }
    const targetNum = parseFloat(target);
    if (!targetNum || targetNum <= 0) { alert('סכום יעד חייב להיות חיובי'); return; }
    setSaving(true);
    try {
      const body = {
        name: name.trim(),
        target_amount: targetNum,
        saved_amount: parseFloat(saved) || 0,
        deadline: deadline || null,
        color,
        notes: notes.trim() || null,
      };
      const url = isNew ? '/api/goals' : `/api/goals/${goal.id}`;
      const method = isNew ? 'POST' : 'PATCH';
      const r = await apiFetch(url, {
        method, headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(body),
      });
      if (!r.ok) {
        const err = await r.json().catch(() => ({}));
        throw new Error(err.detail || 'שמירה נכשלה');
      }
      await onSaved();
    } catch (err) {
      alert('שגיאה: ' + err.message);
    } finally {
      setSaving(false);
    }
  };

  const remove = async () => {
    if (!confirm(`למחוק את היעד "${goal.name}"?`)) return;
    setSaving(true);
    try {
      await apiFetch(`/api/goals/${goal.id}`, { method: 'DELETE' });
      await onSaved();
    } catch (err) {
      alert('מחיקה נכשלה: ' + err.message);
    } finally {
      setSaving(false);
    }
  };

  return (
    <div onClick={onClose} style={{
      position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.4)', backdropFilter: 'blur(4px)',
      zIndex: 95, display: 'flex', alignItems: 'flex-start', justifyContent: 'center', paddingTop: '8vh',
      animation: 'fade-in .2s',
    }}>
      <form onClick={e => e.stopPropagation()} onSubmit={submit} className="slide-up" style={{
        width: 480, maxWidth: '90%', background: 'var(--surface)',
        borderRadius: 16, boxShadow: 'var(--shadow-lg)', overflow: 'hidden',
      }}>
        <div style={{ padding: '18px 22px', borderBottom: '0.5px solid var(--rule)', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
          <span style={{ fontSize: 15, fontWeight: 600 }}>{isNew ? 'יעד חדש' : 'עריכת יעד'}</span>
          <span onClick={onClose} style={{ cursor: 'pointer', color: 'var(--ink-soft)', fontSize: 16 }}>✕</span>
        </div>
        <div style={{ padding: 22 }}>
          <Field label="שם היעד">
            <input value={name} onChange={e => setName(e.target.value)} required
              placeholder="טיול ליפן 2027 / קופת חירום / דירה" style={inpStyle} />
          </Field>
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
            <Field label="סכום יעד (₪)">
              <input type="number" value={target} onChange={e => setTarget(e.target.value)}
                min="0" step="100" required style={inpStyle} />
            </Field>
            <Field label="נחסך עד כה (₪)">
              <input type="number" value={saved} onChange={e => setSaved(e.target.value)}
                min="0" step="100" style={inpStyle} />
            </Field>
          </div>
          <Field label="יעד תאריך (אופציונלי)">
            <input type="date" value={deadline} onChange={e => setDeadline(e.target.value)} style={inpStyle} />
          </Field>
          <Field label="צבע">
            <div style={{ display: 'flex', gap: 6 }}>
              {colors.map(c => (
                <button key={c} type="button" onClick={() => setColor(c)} style={{
                  width: 28, height: 28, borderRadius: 8, background: c,
                  border: color === c ? '2px solid var(--ink)' : '0.5px solid var(--rule)',
                  cursor: 'pointer',
                }} />
              ))}
            </div>
          </Field>
          <Field label="הערות (אופציונלי)">
            <textarea value={notes} onChange={e => setNotes(e.target.value)} rows={2}
              placeholder="למה היעד הזה חשוב לך?"
              style={{ ...inpStyle, minHeight: 50, resize: 'vertical', fontFamily: 'inherit' }} />
          </Field>
        </div>
        <div style={{ padding: '14px 22px', borderTop: '0.5px solid var(--rule)', display: 'flex', gap: 8, justifyContent: 'space-between' }}>
          {!isNew ? (
            <button type="button" onClick={remove} disabled={saving} style={{
              padding: '8px 12px', borderRadius: 9, background: 'transparent', border: 'none',
              color: 'var(--alert)', fontSize: 12, cursor: saving ? 'wait' : 'pointer',
            }}>🗑 מחק</button>
          ) : <span />}
          <div style={{ display: 'flex', gap: 8 }}>
            <button type="button" onClick={onClose} disabled={saving} style={{ padding: '9px 14px', borderRadius: 9, background: 'var(--surface)', border: '0.5px solid var(--rule)', cursor: saving ? 'wait' : 'pointer', fontSize: 13 }}>ביטול</button>
            <button type="submit" disabled={saving} style={{ padding: '9px 18px', borderRadius: 9, background: 'var(--ink)', color: 'var(--bg)', border: 'none', cursor: saving ? 'wait' : 'pointer', fontSize: 13, fontWeight: 600 }}>
              {saving ? 'שומר…' : 'שמור'}
            </button>
          </div>
        </div>
      </form>
    </div>
  );
}

function SettingsPage() {
  const { theme, setTheme, accent, setAccent, density, setDensity, setPage } = useApp();
  const [scrapeOpen, setScrapeOpen] = useState(false);
  const [editingAcct, setEditingAcct] = useState(null);

  const archiveAccount = async (a) => {
    if (!confirm(`לארכב את "${a.name}"? לא יופיע יותר ברשימה אבל הנתונים יישמרו.`)) return;
    try {
      const r = await apiFetch(`/api/accounts/${a.account_id}/archive`, { method: 'POST' });
      if (!r.ok) throw new Error('archive failed');
      await window.APP_DATA_REFRESH();
    } catch (err) {
      alert('שגיאה: ' + err.message);
    }
  };

  const fmtSync = (iso) => {
    if (!iso) return 'עדיין לא סונכרן';
    const diff = Math.max(0, Date.now() - new Date(iso).getTime());
    const m = Math.round(diff / 60000);
    if (m < 1) return 'סונכרן זה עתה';
    if (m < 60) return `סונכרן לפני ${m} דקות`;
    const h = Math.round(m / 60);
    if (h < 24) return `סונכרן לפני ${h} שעות`;
    const d = Math.round(h / 24);
    return d === 1 ? 'סונכרן אתמול' : `סונכרן לפני ${d} ימים`;
  };

  const accounts = D.accounts || [];

  return (
    <div className="fade-in" style={{ padding: '32px 40px 60px', maxWidth: 720, margin: '0 auto' }}>
      <h1 style={{ fontSize: 26, fontWeight: 600, letterSpacing: '-0.02em', margin: '0 0 22px' }}>הגדרות</h1>

      <Surface style={{ marginBottom: 14 }}>
        <div style={{ fontSize: 13, fontWeight: 600, marginBottom: 14 }}>כרטיסים מקושרים</div>
        {accounts.length === 0 && (
          <div style={{ padding: '16px 0', textAlign: 'center', color: 'var(--ink-soft)', fontSize: 12.5 }}>
            עדיין אין כרטיסים מקושרים.
          </div>
        )}
        {accounts.map((a, i) => (
          <div key={a.id} style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '10px 0', borderTop: i ? '0.5px solid var(--rule)' : 'none' }}>
            <div style={{ width: 36, height: 24, borderRadius: 5, background: a.color || 'var(--ink-soft)' }} />
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{ fontSize: 13, fontWeight: 500 }}>{a.name}</div>
              <div style={{ fontSize: 11, color: 'var(--ink-soft)', fontFamily: 'var(--font-mono)' }}>
                {a.holder} · {fmtSync(a.last_synced)}
              </div>
            </div>
            <button onClick={() => setEditingAcct(a)} title="ערוך" style={{
              background: 'transparent', border: '0.5px solid var(--rule)',
              padding: '5px 9px', borderRadius: 7, fontSize: 11, cursor: 'pointer',
            }}>✎</button>
            <button onClick={() => archiveAccount(a)} title="ארכב" style={{
              background: 'transparent', border: '0.5px solid rgba(216,58,58,0.3)',
              color: 'var(--alert)', padding: '5px 9px', borderRadius: 7, fontSize: 11, cursor: 'pointer',
            }}>🗑</button>
          </div>
        ))}
        <button onClick={() => setScrapeOpen(true)} style={{
          marginTop: 12, width: '100%', padding: '10px',
          background: 'var(--ink)', color: 'var(--bg)', border: 'none',
          borderRadius: 9, fontSize: 13, fontWeight: 500, cursor: 'pointer',
        }}>+ הוסף חשבון</button>
        {scrapeOpen && <ScrapeModal onClose={() => setScrapeOpen(false)} />}
        {editingAcct && <AccountEditModal
          account={editingAcct}
          onClose={() => setEditingAcct(null)}
          onSaved={async () => { await window.APP_DATA_REFRESH(); setEditingAcct(null); }}
        />}
      </Surface>

      <Surface style={{ marginBottom: 14 }}>
        <div style={{ fontSize: 13, fontWeight: 600, marginBottom: 14 }}>מראה</div>
        <Row label="ערכת נושא">
          <Segmented options={[{value:'light',label:'בהיר'},{value:'dark',label:'כהה'}]} value={theme} onChange={setTheme} />
        </Row>
        <Row label="צבע מבטא">
          <div style={{ display: 'flex', gap: 8 }}>
            {['#0a84ff','#5856d6','#ff375f','#34c759','#ff9500','#af52de'].map(c => (
              <span key={c} onClick={() => setAccent(c)} style={{
                width: 24, height: 24, borderRadius: '50%', background: c, cursor: 'pointer',
                boxShadow: accent === c ? `0 0 0 2px var(--bg), 0 0 0 4px ${c}` : 'none',
              }} />
            ))}
          </div>
        </Row>
        <Row label="צפיפות">
          <Segmented options={[{value:'compact',label:'צפוף'},{value:'comfortable',label:'נוח'}]} value={density} onChange={setDensity} />
        </Row>
      </Surface>

      <Surface>
        <div style={{ fontSize: 13, fontWeight: 600, marginBottom: 14 }}>Cai AI</div>
        <Row label="דוח אוטומטי">
          <Segmented options={[{value:'weekly',label:'שבועי'},{value:'monthly',label:'חודשי'},{value:'off',label:'כבוי'}]} value="monthly" onChange={()=>{}} />
        </Row>
        <Row label="זיהוי חריגות">
          <Toggle on />
        </Row>
        <Row label="הצעות חיסכון">
          <Toggle on />
        </Row>
      </Surface>
    </div>
  );
}

function Row({ label, children }) {
  return (
    <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '10px 0', borderTop: '0.5px solid var(--rule)' }}>
      <span style={{ fontSize: 13 }}>{label}</span>
      {children}
    </div>
  );
}
function Toggle({ on: initial }) {
  const [on, setOn] = useState(initial);
  return (
    <div onClick={() => setOn(!on)} style={{
      width: 38, height: 22, borderRadius: 99, background: on ? 'var(--accent)' : 'var(--ink-faint)',
      position: 'relative', cursor: 'pointer', transition: 'background .2s',
    }}>
      <div style={{
        position: 'absolute', top: 2, [on ? 'insetInlineStart' : 'insetInlineEnd']: 2,
        width: 18, height: 18, borderRadius: '50%', background: 'white', transition: 'all .2s',
        boxShadow: '0 1px 3px rgba(0,0,0,0.2)',
      }} />
    </div>
  );
}

// Transaction drawer
function TxnDrawer() {
  const { drawerTxn, setDrawerTxn } = useApp();
  // Local edit state — the form is only persisted when the user clicks "שמור"
  const [category, setCategory] = useState(drawerTxn?.category || '');
  const [note, setNote] = useState(drawerTxn?.notes || '');
  const [saving, setSaving] = useState(false);
  const [feedback, setFeedback] = useState(null); // {kind: 'ok'|'err', text}

  // Reset local state when the drawer opens with a different transaction
  useEffect(() => {
    if (drawerTxn) {
      setCategory(drawerTxn.category || '');
      setNote(drawerTxn.notes || '');
      setFeedback(null);
    }
  }, [drawerTxn?.id]);

  if (!drawerTxn) return null;
  const t = drawerTxn;
  const c = D.categories[category] || D.categories[t.category] || {};
  const noteOriginal = t.notes || '';
  const dirty = category !== t.category || (note || '') !== noteOriginal;

  const save = async () => {
    if (saving) return;
    if (!dirty) {
      setFeedback({ kind: 'ok', text: 'אין שינויים לשמור.' });
      return;
    }
    setSaving(true);
    setFeedback(null);
    try {
      let related = 0;

      // 1) Category change goes through merchant cascade
      if (category !== t.category) {
        const r = await apiFetch(`/api/transactions/${t.id}/category`, {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ category, learn: true }),
        });
        if (!r.ok) {
          const err = await r.json().catch(() => ({}));
          throw new Error(err.detail || 'שמירת הקטגוריה נכשלה');
        }
        const data = await r.json().catch(() => ({}));
        related = data.related_updated || 0;
      }

      // 2) Notes saved per-transaction via PATCH
      if ((note || '') !== noteOriginal) {
        const r2 = await apiFetch(`/api/transactions/${t.id}`, {
          method: 'PATCH',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ notes: note }),
        });
        if (!r2.ok) {
          const err = await r2.json().catch(() => ({}));
          throw new Error(err.detail || 'שמירת ההערה נכשלה');
        }
      }

      await window.APP_DATA_REFRESH();
      setDrawerTxn({ ...t, category, notes: note });
      setFeedback({
        kind: 'ok',
        text: related > 0
          ? `נשמר ✓ — ועוד ${related} עסקאות עם אותו ספק עודכנו בכל החודשים.`
          : 'נשמר ✓',
      });
    } catch (err) {
      setFeedback({ kind: 'err', text: 'שגיאה: ' + (err.message || 'נכשל') });
    } finally {
      setSaving(false);
    }
  };

  return (
    <div onClick={() => setDrawerTxn(null)} style={{
      position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.4)', backdropFilter: 'blur(4px)',
      zIndex: 80, display: 'flex', justifyContent: 'flex-start', animation: 'fade-in .2s',
    }}>
      <div onClick={e => e.stopPropagation()} style={{
        width: 420, height: '100%', background: 'var(--surface)',
        boxShadow: 'var(--shadow-lg)', padding: 28, overflowY: 'auto',
        animation: 'slide-in-end .3s cubic-bezier(.2,.8,.3,1)',
      }} className="thin-scroll">
        <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 22 }}>
          <span style={{ fontSize: 11, color: 'var(--ink-soft)', letterSpacing: '0.05em', textTransform: 'uppercase' }}>פרטי עסקה</span>
          <span onClick={() => setDrawerTxn(null)} style={{ cursor: 'pointer', color: 'var(--ink-soft)', fontSize: 16 }}>✕</span>
        </div>
        <div style={{ width: 56, height: 56, borderRadius: 16, background: `${c.color}22`, display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 26, marginBottom: 14 }}>{c.icon}</div>
        <div style={{ fontSize: 22, fontWeight: 600, letterSpacing: '-0.01em' }}>{t.description}</div>
        <div className="tnum" style={{ fontSize: 38, fontWeight: 700, marginTop: 8, letterSpacing: '-0.02em', color: t.amount > 0 ? 'var(--positive)' : 'var(--ink)' }}>
          {fmtILS(t.amount)}
        </div>
        <div style={{ fontSize: 12.5, color: 'var(--ink-soft)', marginTop: 4 }}>{fmtDate(t.date)} · {t.source.toUpperCase()}</div>

        <div style={{ marginTop: 22 }}>
          <div style={{ fontSize: 11, color: 'var(--ink-soft)', marginBottom: 8, letterSpacing: '0.02em', fontWeight: 500 }}>קטגוריה</div>
          <select
            value={category}
            onChange={(e) => setCategory(e.target.value)}
            style={{ width: '100%', padding: '10px 12px', borderRadius: 10, background: 'var(--bg-deep)', border: '0.5px solid var(--rule)', fontSize: 13, outline: 'none' }}>
            {Object.keys(D.categories).map(c => <option key={c} value={c}>{c}</option>)}
          </select>
          {dirty && (
            <div style={{ fontSize: 10.5, color: 'var(--accent)', marginTop: 6, fontWeight: 500 }}>
              ✱ יש שינוי שעוד לא נשמר. לחץ "שמור" להחיל גם על עסקאות דומות בכל החודשים.
            </div>
          )}
        </div>

        <div style={{ marginTop: 14 }}>
          <div style={{ fontSize: 11, color: 'var(--ink-soft)', marginBottom: 8, letterSpacing: '0.02em', fontWeight: 500 }}>הערה</div>
          <textarea
            value={note}
            onChange={e => setNote(e.target.value)}
            placeholder="הוסף הערה אישית…"
            style={{ width: '100%', padding: '10px 12px', borderRadius: 10, background: 'var(--bg-deep)', border: '0.5px solid var(--rule)', fontSize: 13, outline: 'none', minHeight: 70, resize: 'vertical', fontFamily: 'inherit' }} />
        </div>

        {feedback && (
          <div style={{
            marginTop: 14, padding: 12, borderRadius: 10, fontSize: 12.5, lineHeight: 1.5,
            background: feedback.kind === 'ok' ? 'rgba(48,164,108,0.1)' : 'rgba(216,58,58,0.1)',
            color: feedback.kind === 'ok' ? 'var(--positive)' : 'var(--alert)',
            border: `0.5px solid ${feedback.kind === 'ok' ? 'rgba(48,164,108,0.3)' : 'rgba(216,58,58,0.3)'}`,
          }}>{feedback.text}</div>
        )}

        <div style={{ display: 'flex', gap: 8, marginTop: 22 }}>
          <button onClick={save} disabled={saving} style={{
            flex: 1, padding: '11px', borderRadius: 10, border: 'none',
            background: dirty ? 'var(--ink)' : 'var(--ink-soft)',
            color: 'var(--bg)', fontWeight: 600, fontSize: 13,
            cursor: saving ? 'wait' : (dirty ? 'pointer' : 'default'),
            opacity: saving ? 0.6 : 1,
          }}>{saving ? 'שומר…' : (dirty ? 'שמור' : 'נשמר')}</button>
        </div>
      </div>
    </div>
  );
}

window.TrendsPage = TrendsPage;
window.AIPage = AIPage;
window.BudgetsPage = BudgetsPage;
window.BudgetsEditModal = BudgetsEditModal;
window.SubsPage = SubsPage;
window.GoalsPage = GoalsPage;
window.SettingsPage = SettingsPage;
window.TxnDrawer = TxnDrawer;
window.ScrapeModal = ScrapeModal;
