// Budget page — Zero-Based Budgeting, Rollover Balances, Predictive Spending.

// ── Helpers ────────────────────────────────────────────────────────────────────

const daysInMonth = (y, m) => new Date(y, m + 1, 0).getDate();

// Spending for a given month keyed by category
const spendingByCategory = (transactions, year, month) => {
  const map = {};
  transactions.forEach(t => {
    if (t.type !== 'expense') return;
    const d = new Date(t.date);
    if (d.getFullYear() === year && d.getMonth() === month) {
      map[t.category] = (map[t.category] || 0) + t.amount;
    }
  });
  return map;
};

// Income total for a given month
const incomeForMonth = (transactions, year, month) =>
  transactions
    .filter(t => {
      const d = new Date(t.date);
      return t.type === 'income' && d.getFullYear() === year && d.getMonth() === month;
    })
    .reduce((s, t) => s + t.amount, 0);

// ── Inline-editable budget amount ──────────────────────────────────────────────
const BudgetInput = ({ value, onCommit }) => {
  const [editing, setEditing] = React.useState(false);
  const [raw,     setRaw]     = React.useState('');
  const inputRef = React.useRef();

  const start = () => {
    setRaw(value > 0 ? String(value) : '');
    setEditing(true);
    setTimeout(() => inputRef.current?.select(), 0);
  };

  const commit = () => {
    const n = parseFloat(raw);
    onCommit(isNaN(n) || n < 0 ? 0 : n);
    setEditing(false);
  };

  const onKey = (e) => {
    if (e.key === 'Enter') commit();
    if (e.key === 'Escape') setEditing(false);
  };

  const sym = (window.CURRENCY_CONFIG?.[window.__currency]?.symbol) || 'R';

  if (editing) {
    return (
      <input
        ref={inputRef}
        type="number" min="0" step="0.01"
        value={raw}
        onChange={e => setRaw(e.target.value)}
        onBlur={commit}
        onKeyDown={onKey}
        style={{
          width: 100, padding: '3px 8px', borderRadius: 6,
          border: '1.5px solid var(--ink)', background: 'var(--surface)',
          fontSize: 13.5, fontWeight: 500, color: 'var(--ink)',
          fontFamily: 'var(--font-mono, monospace)', outline: 'none',
        }}
      />
    );
  }

  return (
    <button
      onClick={start}
      title="Click to edit budget"
      style={{
        background: 'none', border: '1px dashed transparent', borderRadius: 6,
        padding: '2px 6px', cursor: 'pointer', fontSize: 13.5, fontWeight: 500,
        color: value > 0 ? 'var(--ink)' : 'var(--ink-4)',
        fontFamily: 'var(--font-mono, monospace)',
        transition: 'border-color 0.15s',
      }}
      onMouseOver={e => e.currentTarget.style.borderColor = 'var(--line)'}
      onMouseOut={e => e.currentTarget.style.borderColor = 'transparent'}
    >
      {value > 0 ? fmtZARShort(value) : `${sym} — set budget`}
      <IEdit size={11} style={{ marginLeft: 4, opacity: 0.4, verticalAlign: 'middle' }}/>
    </button>
  );
};

// ── Category budget row ────────────────────────────────────────────────────────
const BudgetRow = ({ cat, budget, spent, lastMonthSpent, isCurrentMonth, daysElapsed, totalDays, onSetBudget, onDelete }) => {
  const base            = budget?.amount || 0;
  const rolloverOn      = budget?.rolloverEnabled !== false;

  // Rollover: only if budget is set and rollover is enabled
  const rollover        = (rolloverOn && base > 0) ? (base - lastMonthSpent) : 0;
  const effective       = base + rollover;

  const remaining       = effective - spent;
  const pct             = effective > 0 ? Math.min(spent / effective, 1.05) : 0;
  const over            = spent > effective && effective > 0;
  const urgent          = !over && pct > 0.8;

  // Predictive spend (current month only)
  const projected       = isCurrentMonth && daysElapsed > 0
    ? Math.round((spent / daysElapsed) * totalDays)
    : null;
  const projectedOver   = projected !== null && effective > 0 && projected > effective;

  const barColor = over ? 'var(--coral)' : urgent ? 'var(--amber)' : 'var(--mint)';

  const toggleRollover = () => onSetBudget(cat.id, base, !rolloverOn);

  return (
    <div className="card card-pad fade-up" style={{ marginBottom: 12 }}>
      {/* Header row */}
      <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 12 }}>
        <span className="dot" style={{ background: `var(${cat.cssVar})`, width: 10, height: 10, flexShrink: 0 }}/>
        <div style={{ flex: 1, fontWeight: 500, fontSize: 14 }}>{cat.label}</div>

        {/* Budget amount — click to edit */}
        <BudgetInput value={base} onCommit={val => onSetBudget(cat.id, val, rolloverOn)}/>

        {/* Delete button — custom categories only */}
        {onDelete && (
          <button
            onClick={onDelete}
            title="Delete this category"
            style={{ background: 'none', border: 'none', cursor: 'pointer', padding: '2px 4px', color: 'var(--ink-4)', fontSize: 16, lineHeight: 1, transition: 'color 0.15s' }}
            onMouseOver={e => e.currentTarget.style.color = 'var(--coral)'}
            onMouseOut={e => e.currentTarget.style.color = 'var(--ink-4)'}
          >×</button>
        )}
      </div>

      {/* Progress bar — only show if budget is set */}
      {effective > 0 && (
        <div style={{ height: 5, borderRadius: 3, background: 'var(--line)', marginBottom: 10, overflow: 'hidden' }}>
          <div style={{
            height: '100%', borderRadius: 3,
            width: `${Math.min(pct * 100, 100)}%`,
            background: barColor,
            transition: 'width 0.6s ease, background 0.3s',
          }}/>
        </div>
      )}

      {/* Stats row */}
      <div style={{ display: 'flex', flexWrap: 'wrap', gap: '6px 16px', fontSize: 12.5, color: 'var(--ink-3)' }}>
        <span>Spent <strong style={{ color: over ? 'var(--coral)' : 'var(--ink)', fontFamily: 'monospace' }}>{fmtZARShort(spent)}</strong></span>

        {effective > 0 && (
          <span>
            {over
              ? <span style={{ color: 'var(--coral)', fontWeight: 500 }}>Over by {fmtZARShort(Math.abs(remaining))}</span>
              : <span>Left <strong style={{ color: 'var(--ink)', fontFamily: 'monospace' }}>{fmtZARShort(remaining)}</strong></span>
            }
          </span>
        )}

        {/* Rollover pill */}
        {base > 0 && (
          <button
            onClick={toggleRollover}
            title={rolloverOn ? 'Rollover enabled — click to disable' : 'Rollover disabled — click to enable'}
            style={{
              display: 'inline-flex', alignItems: 'center', gap: 4,
              padding: '1px 8px', borderRadius: 20, fontSize: 11.5, fontWeight: 500,
              border: `1px solid ${rolloverOn ? 'var(--mint)' : 'var(--line)'}`,
              background: rolloverOn ? 'var(--mint-soft)' : 'var(--surface-2)',
              color: rolloverOn ? 'var(--mint-deep)' : 'var(--ink-4)',
              cursor: 'pointer', transition: 'all 0.15s',
            }}
          >
            {rolloverOn
              ? (rollover >= 0
                  ? `↻ +${fmtZARShort(rollover)} rolled over`
                  : `↻ ${fmtZARShort(rollover)} deficit`)
              : '↻ Rollover off'}
          </button>
        )}

        {/* Predictive spending */}
        {isCurrentMonth && projected !== null && effective > 0 && (
          <span style={{ marginLeft: 'auto' }}>
            {projectedOver
              ? <span style={{ color: 'var(--coral)' }}>⚠ Projected {fmtZARShort(projected)} — {fmtZARShort(projected - effective)} over</span>
              : <span style={{ color: 'var(--mint-deep)' }}>✓ Projected {fmtZARShort(projected)} — on track</span>
            }
          </span>
        )}
      </div>
    </div>
  );
};

// ── Colour options for custom categories ──────────────────────────────────────
const CAT_COLORS = [
  { name: 'mint',   cssVar: '--mint',   label: 'Green'  },
  { name: 'coral',  cssVar: '--coral',  label: 'Red'    },
  { name: 'amber',  cssVar: '--amber',  label: 'Yellow' },
  { name: 'violet', cssVar: '--violet', label: 'Purple' },
  { name: 'sky',    cssVar: '--sky',    label: 'Blue'   },
  { name: 'pink',   cssVar: '--pink',   label: 'Pink'   },
];

// ── Add custom category form ───────────────────────────────────────────────────
const AddCategoryForm = ({ onAdd, onCancel }) => {
  const [label, setLabel] = React.useState('');
  const [color, setColor] = React.useState(CAT_COLORS[0]);
  const inputRef = React.useRef();

  React.useEffect(() => { inputRef.current?.focus(); }, []);

  const submit = (e) => {
    e.preventDefault();
    const trimmed = label.trim();
    if (!trimmed) return;
    onAdd(trimmed, color.name, color.cssVar);
    setLabel('');
  };

  return (
    <form onSubmit={submit} className="card card-pad fade-up" style={{ marginBottom: 12, border: '1.5px dashed var(--mint)' }}>
      <div style={{ fontSize: 12, fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.07em', color: 'var(--ink-3)', marginBottom: 12 }}>
        New category
      </div>
      <div style={{ display: 'flex', gap: 10, alignItems: 'center', flexWrap: 'wrap' }}>
        {/* Name input */}
        <input
          ref={inputRef}
          type="text" value={label} onChange={e => setLabel(e.target.value)}
          placeholder="e.g. Pet care, Gym, Kids…"
          maxLength={32}
          style={{
            flex: '1 1 160px', padding: '9px 12px', borderRadius: 8,
            border: '1.5px solid var(--line)', background: 'var(--surface)',
            fontSize: 14, color: 'var(--ink)', outline: 'none',
          }}
          onFocus={e => e.target.style.borderColor = 'var(--ink)'}
          onBlur={e => e.target.style.borderColor = 'var(--line)'}
        />

        {/* Colour swatches */}
        <div style={{ display: 'flex', gap: 6, alignItems: 'center' }}>
          {CAT_COLORS.map(c => (
            <button
              key={c.name} type="button"
              onClick={() => setColor(c)}
              title={c.label}
              style={{
                width: 22, height: 22, borderRadius: '50%',
                background: `var(${c.cssVar})`,
                border: color.name === c.name ? '2.5px solid var(--ink)' : '2px solid transparent',
                cursor: 'pointer', transition: 'border 0.15s', flexShrink: 0,
              }}
            />
          ))}
        </div>

        {/* Actions */}
        <div style={{ display: 'flex', gap: 8, marginLeft: 'auto' }}>
          <button type="button" className="btn btn-ghost btn-sm" onClick={onCancel}>Cancel</button>
          <button type="submit" className="btn btn-primary btn-sm" disabled={!label.trim()}>Add</button>
        </div>
      </div>
    </form>
  );
};

// ── Main page ──────────────────────────────────────────────────────────────────
const PageBudget = () => {
  const { state, setBudget, addCustomCategory, deleteCustomCategory } = useAppState();

  // Month navigation
  const todayDate = today();
  const [viewYear,  setViewYear]  = React.useState(todayDate.getFullYear());
  const [viewMonth, setViewMonth] = React.useState(todayDate.getMonth());

  const prevMonth = () => {
    if (viewMonth === 0) { setViewYear(y => y - 1); setViewMonth(11); }
    else setViewMonth(m => m - 1);
  };
  const nextMonth = () => {
    if (viewMonth === 11) { setViewYear(y => y + 1); setViewMonth(0); }
    else setViewMonth(m => m + 1);
  };

  const isCurrentMonth = viewYear === todayDate.getFullYear() && viewMonth === todayDate.getMonth();

  // Days for predictive spending
  const totalDays   = daysInMonth(viewYear, viewMonth);
  const daysElapsed = isCurrentMonth ? todayDate.getDate() : totalDays;

  // Last month
  const lastY = viewMonth === 0 ? viewYear - 1 : viewYear;
  const lastM = viewMonth === 0 ? 11 : viewMonth - 1;

  // Merge built-in + custom categories
  const allCategories = React.useMemo(() => [
    ...CATEGORIES,
    ...(state.customCategories || []).map(c => ({ ...c, id: c.id, custom: true })),
  ], [state.customCategories]);

  // Spending data
  const thisSpend = React.useMemo(() => spendingByCategory(state.transactions, viewYear, viewMonth), [state.transactions, viewYear, viewMonth]);
  const lastSpend = React.useMemo(() => spendingByCategory(state.transactions, lastY, lastM),       [state.transactions, lastY, lastM]);

  // Income
  const monthIncome = React.useMemo(() => incomeForMonth(state.transactions, viewYear, viewMonth), [state.transactions, viewYear, viewMonth]);

  // Budget lookup
  const budgetMap = React.useMemo(() => {
    const m = {};
    state.budgets.forEach(b => { m[b.category] = b; });
    return m;
  }, [state.budgets]);

  // ZBB totals
  const { totalAllocated } = React.useMemo(() => {
    let totalAllocated = 0;
    allCategories.forEach(cat => {
      const b = budgetMap[cat.id];
      if (!b || b.amount === 0) return;
      totalAllocated += b.amount;
    });
    return { totalAllocated };
  }, [budgetMap, allCategories]);

  const unallocated   = monthIncome - totalAllocated;
  const allocPct      = monthIncome > 0 ? Math.min(totalAllocated / monthIncome, 1) : 0;
  const overAllocated = totalAllocated > monthIncome && monthIncome > 0;

  // Categories to show: all with a budget OR any spending this month
  const activeCategories = allCategories.filter(cat => budgetMap[cat.id] || thisSpend[cat.id]);
  const otherCategories  = allCategories.filter(cat => !budgetMap[cat.id] && !thisSpend[cat.id]);

  const [showAll,    setShowAll]    = React.useState(false);
  const [showForm,   setShowForm]   = React.useState(false);

  const handleAddCategory = (label, color, cssVar) => {
    addCustomCategory(label, color, cssVar);
    setShowForm(false);
  };

  const handleDeleteCustom = (cat) => {
    if (!confirm(`Delete "${cat.label}"? Any budget set for it will also be removed.`)) return;
    deleteCustomCategory(cat.id);
  };

  return (
    <div>
      {/* ── Header ── */}
      <div className="topbar">
        <div>
          <h1 className="page-title"><span className="serif">Budget</span></h1>
          <div className="page-sub">Zero-based · rollover · forecast</div>
        </div>
        {/* Month navigation */}
        <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
          <button className="btn btn-ghost btn-sm" onClick={prevMonth}>←</button>
          <div style={{ fontSize: 14, fontWeight: 500, minWidth: 96, textAlign: 'center' }}>
            {monthNames[viewMonth]} {viewYear}
          </div>
          <button className="btn btn-ghost btn-sm" onClick={nextMonth}>→</button>
        </div>
      </div>

      {/* ── ZBB Snapshot ── */}
      <div className="card card-pad-lg fade-up" style={{ marginBottom: 20 }}>
        <div className="card-title" style={{ marginBottom: 16 }}>Budget snapshot</div>
        <div style={{ display: 'flex', flexWrap: 'wrap', gap: '8px 32px', marginBottom: 14 }}>
          <div>
            <div style={{ fontSize: 11, fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.07em', color: 'var(--ink-3)', marginBottom: 3 }}>Income</div>
            <div className="kpi-value serif" style={{ fontSize: 24 }}>{fmtZARShort(monthIncome)}</div>
          </div>
          <div>
            <div style={{ fontSize: 11, fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.07em', color: 'var(--ink-3)', marginBottom: 3 }}>Allocated</div>
            <div className="kpi-value serif" style={{ fontSize: 24, color: overAllocated ? 'var(--coral)' : 'var(--ink)' }}>{fmtZARShort(totalAllocated)}</div>
          </div>
          <div>
            <div style={{ fontSize: 11, fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.07em', color: 'var(--ink-3)', marginBottom: 3 }}>
              {overAllocated ? 'Over-allocated' : 'Unallocated'}
            </div>
            <div className="kpi-value serif" style={{ fontSize: 24, color: overAllocated ? 'var(--coral)' : unallocated === 0 ? 'var(--mint-deep)' : 'var(--ink)' }}>
              {fmtZARShort(Math.abs(unallocated))}
            </div>
          </div>
        </div>

        {/* Allocation bar */}
        {monthIncome > 0 && (
          <div>
            <div style={{ height: 8, borderRadius: 4, background: 'var(--line)', overflow: 'hidden', marginBottom: 6 }}>
              <div style={{
                height: '100%', borderRadius: 4,
                width: `${allocPct * 100}%`,
                background: overAllocated ? 'var(--coral)' : unallocated < monthIncome * 0.05 ? 'var(--mint)' : 'var(--ink)',
                transition: 'width 0.6s ease',
              }}/>
            </div>
            <div style={{ fontSize: 12, color: 'var(--ink-3)' }}>
              {overAllocated
                ? `⚠ Allocated ${fmtZARShort(totalAllocated - monthIncome)} more than income`
                : unallocated === 0
                  ? '✓ Every rand is assigned — fully zero-based'
                  : `${fmtZARShort(unallocated)} left to assign across categories`}
            </div>
          </div>
        )}
        {monthIncome === 0 && (
          <div style={{ fontSize: 13, color: 'var(--ink-3)' }}>No income recorded for {monthNames[viewMonth]} — add transactions to see your ZBB balance.</div>
        )}
      </div>

      {/* ── Active category rows ── */}
      {activeCategories.length > 0 && (
        <>
          <div style={{ fontSize: 11, fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.07em', color: 'var(--ink-3)', marginBottom: 10 }}>
            Categories
          </div>
          {activeCategories.map(cat => (
            <BudgetRow
              key={cat.id}
              cat={cat}
              budget={budgetMap[cat.id]}
              spent={thisSpend[cat.id] || 0}
              lastMonthSpent={lastSpend[cat.id] || 0}
              isCurrentMonth={isCurrentMonth}
              daysElapsed={daysElapsed}
              totalDays={totalDays}
              onSetBudget={setBudget}
              onDelete={cat.custom ? () => handleDeleteCustom(cat) : null}
            />
          ))}
        </>
      )}

      {/* ── Unbudgeted categories ── */}
      {otherCategories.length > 0 && (
        <div style={{ marginTop: 8 }}>
          <button
            onClick={() => setShowAll(v => !v)}
            style={{ fontSize: 13, color: 'var(--ink-3)', background: 'none', border: 'none', cursor: 'pointer', padding: '4px 0', marginBottom: 10, display: 'flex', alignItems: 'center', gap: 6 }}
          >
            {showAll ? '▾' : '▸'} {showAll ? 'Hide' : 'Show'} {otherCategories.length} unbudgeted {otherCategories.length === 1 ? 'category' : 'categories'}
          </button>
          {showAll && otherCategories.map(cat => (
            <BudgetRow
              key={cat.id}
              cat={cat}
              budget={undefined}
              spent={0}
              lastMonthSpent={0}
              isCurrentMonth={isCurrentMonth}
              daysElapsed={daysElapsed}
              totalDays={totalDays}
              onSetBudget={setBudget}
              onDelete={cat.custom ? () => handleDeleteCustom(cat) : null}
            />
          ))}
        </div>
      )}

      {/* ── Add custom category ── */}
      <div style={{ marginTop: 16 }}>
        {showForm ? (
          <AddCategoryForm onAdd={handleAddCategory} onCancel={() => setShowForm(false)} />
        ) : (
          <button
            onClick={() => setShowForm(true)}
            style={{
              display: 'flex', alignItems: 'center', gap: 8,
              padding: '10px 16px', borderRadius: 10, width: '100%',
              border: '1.5px dashed var(--line)', background: 'none',
              fontSize: 13.5, fontWeight: 500, color: 'var(--ink-3)',
              cursor: 'pointer', transition: 'all 0.15s',
            }}
            onMouseOver={e => { e.currentTarget.style.borderColor = 'var(--ink)'; e.currentTarget.style.color = 'var(--ink)'; }}
            onMouseOut={e => { e.currentTarget.style.borderColor = 'var(--line)'; e.currentTarget.style.color = 'var(--ink-3)'; }}
          >
            <span style={{ fontSize: 18, lineHeight: 1 }}>+</span> Add custom category
          </button>
        )}
      </div>

      {/* ── How rollover works ── */}
      {state.budgets.length > 0 && (
        <div style={{ marginTop: 24, padding: '14px 16px', background: 'var(--surface-2)', borderRadius: 12, border: '1px solid var(--line)', fontSize: 12.5, color: 'var(--ink-3)', lineHeight: 1.7 }}>
          <strong style={{ color: 'var(--ink-2)' }}>↻ How rollover works</strong> — if you underspend a category last month, the surplus adds to this month's budget. Overspending deducts from it. Toggle rollover per category by clicking the pill. Predictive spending (✓ / ⚠) only appears for the current month.
        </div>
      )}
    </div>
  );
};

Object.assign(window, { PageBudget });
