// Mobile (iPhone) dashboard — 390 × 844 portrait.

const { useState: mbState, useMemo: mbMemo, useEffect: mbEffect } = React;

const MOB_TABS = [
  { id: "inicio",        label: "Inicio",        icon: "home" },
  { id: "transacciones", label: "Movimientos",   icon: "list" },
  { id: "add",           label: "",              icon: "plus" }, // FAB-style
  { id: "presupuestos",  label: "Presupuesto",   icon: "budget" },
  { id: "ajustes",       label: "Ajustes",       icon: "settings" },
];

function MobileApp({ fullscreen = false } = {}) {
  const Icon = window.FinanzasIcon;
  const [txs, setTxs] = window.usePersistedState(window.finanzasStorage.loadTx, window.finanzasStorage.saveTx);
  const [accs, setAccs] = window.usePersistedState(window.finanzasStorage.loadAccounts, window.finanzasStorage.saveAccounts);
  const [budgets, setBudgets] = window.usePersistedState(window.finanzasStorage.loadBudgets, window.finanzasStorage.saveBudgets);
  const [tab, setTab] = mbState("inicio");
  const [showAdd, setShowAdd] = mbState(false);
  const [showAddAcc, setShowAddAcc] = mbState(false);
  const [editAcc, setEditAcc] = mbState(null);
  const [showNotifs, setShowNotifs] = mbState(false);
  const [showSearch, setShowSearch] = mbState(false);
  const [editTx, setEditTx] = mbState(null);
  const [hide, setHide] = mbState(false);
  const [swipedId, setSwipedId] = mbState(null);
  const balance = window.totalBalance(accs);
  const spend = window.monthSpending(txs);
  const income = window.monthIncome(txs);
  const byCat = window.spendByCategory(txs);

  // Notifications derived from budgets near or over limit
  const notifs = mbMemo(() => {
    const monthCutoff = window.monthStart();
    return budgets.map(b => {
      const spent = txs
        .filter(t => t.cat === b.cat && t.amount < 0 && new Date(t.date) >= monthCutoff)
        .reduce((s, t) => s + Math.abs(t.amount), 0);
      const pct = b.limit > 0 ? spent / b.limit : 0;
      return { ...b, spent, pct, key: `${b.cat}:${pct >= 1 ? "over" : "near"}` };
    }).filter(b => b.pct >= 0.8);
  }, [budgets, txs]);

  const [seenNotifs, setSeenNotifs] = mbState(() => {
    try { return new Set(JSON.parse(localStorage.getItem("finanzas:notif:seen") || "[]")); }
    catch { return new Set(); }
  });
  const unseenCount = notifs.filter(n => !seenNotifs.has(n.key)).length;
  const markNotifsSeen = () => {
    const next = new Set([...seenNotifs, ...notifs.map(n => n.key)]);
    setSeenNotifs(next);
    try { localStorage.setItem("finanzas:notif:seen", JSON.stringify([...next])); } catch {}
  };

  // Daily-spend trend for sparkline — last 14 days from real tx data
  const trend = mbMemo(() => {
    const NOW = new Date();
    const days = Array.from({ length: 14 }, () => 0);
    for (const t of txs) {
      if (t.amount >= 0) continue;
      if (t.cat === "transfer") continue;
      const d = Math.floor((NOW - new Date(t.date)) / (1000 * 60 * 60 * 24));
      if (d >= 0 && d < 14) days[13 - d] += Math.abs(t.amount);
    }
    return days;
  }, [txs]);

  // Month meta for dynamic labels
  const nowDate = new Date();
  const monthName = nowDate.toLocaleDateString("es-MX", { month: "long" });
  const dayOfMonth = nowDate.getDate();
  const daysInMonth = new Date(nowDate.getFullYear(), nowDate.getMonth() + 1, 0).getDate();
  const avgDaily = dayOfMonth > 0 ? spend / dayOfMonth : 0;

  // Month-over-month delta for daily-spend card
  const momPct = mbMemo(() => {
    const curStart = new Date(nowDate.getFullYear(), nowDate.getMonth(), 1);
    const prevStart = new Date(nowDate.getFullYear(), nowDate.getMonth() - 1, 1);
    const prevCutoff = new Date(nowDate.getFullYear(), nowDate.getMonth() - 1, dayOfMonth);
    let curSum = 0, prevSum = 0;
    for (const t of txs) {
      if (t.amount >= 0 || t.cat === "transfer") continue;
      const d = new Date(t.date);
      if (d >= curStart) curSum += Math.abs(t.amount);
      else if (d >= prevStart && d < prevCutoff) prevSum += Math.abs(t.amount);
    }
    if (prevSum === 0) return null;
    return ((curSum - prevSum) / prevSum) * 100;
  }, [txs, nowDate.getMonth()]);

  const handleSave = (tx) => {
    if (tx.kind === "transfer") {
      const when = tx.date || new Date().toISOString().slice(0, 16);
      const tid = "tr" + Date.now();
      setTxs(prev => [
        { id: "n" + Date.now() + "o", date: when, amount: -Math.abs(tx.amount),
          desc: tx.desc || `Transferencia a ${window.accBy(tx.to)?.name || ""}`,
          cat: "transfer", acc: tx.from, tags: ["transfer", `tr:${tid}`] },
        { id: "n" + Date.now() + "i", date: when, amount: Math.abs(tx.amount),
          desc: tx.desc || `Transferencia desde ${window.accBy(tx.from)?.name || ""}`,
          cat: "transfer", acc: tx.to, tags: ["transfer", `tr:${tid}`] },
        ...prev,
      ]);
      setShowAdd(false);
      setEditTx(null);
      return;
    }
    if (tx.id) {
      setTxs(prev => {
        const target = prev.find(t => t.id === tx.id);
        const tid = target && (target.tags || []).find(x => x.startsWith?.("tr:"));
        return prev.map(t => {
          if (t.id === tx.id) {
            const merged = { ...t, ...tx };
            if (tid) {
              const sign = t.amount < 0 ? -1 : 1;
              merged.amount = sign * Math.abs(tx.amount);
              merged.cat = "transfer";
              merged.tags = Array.from(new Set([...(tx.tags || []), "transfer", tid]));
            }
            return merged;
          }
          if (tid && (t.tags || []).includes(tid)) {
            const sign = t.amount < 0 ? -1 : 1;
            return {
              ...t,
              amount: sign * Math.abs(tx.amount),
              date: tx.date || t.date,
              desc: tx.desc || t.desc,
            };
          }
          return t;
        });
      });
    } else {
      setTxs(prev => [{
        id: "n" + Date.now(),
        date: tx.date || new Date().toISOString().slice(0, 16),
        ...tx,
      }, ...prev]);
    }
    setShowAdd(false);
    setEditTx(null);
  };

  const handleSaveAcc = (acc) => {
    if (acc.id) {
      setAccs(prev => prev.map(a => a.id === acc.id ? {
        ...a, name: acc.name.trim(), type: acc.type,
        balance: parseFloat(acc.balance) || 0,
        color: acc.color, last4: acc.last4 || null,
      } : a));
      setEditAcc(null);
      return;
    }
    const id = acc.name.toLowerCase().replace(/[^a-z0-9]+/g, "").slice(0, 12) + Date.now().toString(36).slice(-4);
    setAccs(prev => [...prev, {
      id, name: acc.name.trim(), type: acc.type,
      balance: parseFloat(acc.balance) || 0,
      color: acc.color, last4: acc.last4 || null,
    }]);
    setShowAddAcc(false);
  };

  const handleDeleteAcc = (id) => {
    const hasRefs = txs.some(t => t.acc === id);
    const msg = hasRefs
      ? "¿Eliminar cuenta? Las transacciones asociadas se mantendrán pero quedarán huérfanas."
      : "¿Eliminar cuenta?";
    if (!confirm(msg)) return;
    setAccs(prev => prev.filter(a => a.id !== id));
    setEditAcc(null);
  };

  const dropTxAndPair = (id) => {
    setTxs(prev => {
      const target = prev.find(t => t.id === id);
      const tid = target && (target.tags || []).find(x => x.startsWith?.("tr:"));
      return prev.filter(t => {
        if (t.id === id) return false;
        if (tid && (t.tags || []).includes(tid)) return false;
        return true;
      });
    });
  };

  const handleDelete = (tx) => {
    dropTxAndPair(tx.id);
    setEditTx(null);
    setSwipedId(null);
  };

  const handleSwipeDelete = (tx) => {
    const isTransfer = (tx.tags || []).some(x => x.startsWith?.("tr:"));
    // Snapshot pair for undo
    let snapshot = [];
    setTxs(prev => {
      const target = prev.find(t => t.id === tx.id);
      const tid = target && (target.tags || []).find(x => x.startsWith?.("tr:"));
      snapshot = prev.filter(t => t.id === tx.id || (tid && (t.tags || []).includes(tid)));
      return prev.filter(t => !snapshot.some(s => s.id === t.id));
    });
    setSwipedId(null);
    const label = isTransfer ? "Transferencia borrada" : "Movimiento borrado";
    window.toast?.show(label, {
      timeout: 5000,
      action: { label: "Deshacer", onClick: () => setTxs(prev => [...snapshot, ...prev]) },
    });
  };

  const handleSwipeEdit = (tx) => {
    setSwipedId(null);
    setEditTx(tx);
  };

  return (
    <div style={{
      width: fullscreen ? "100vw" : 390,
      height: fullscreen ? "100vh" : 844,
      background: "var(--bx-bg)",
      fontFamily: "-apple-system, BlinkMacSystemFont, 'SF Pro Text', Inter, system-ui, sans-serif",
      color: "var(--bx-ink-1)",
      display: "flex", flexDirection: "column",
      position: "relative",
      overflow: "hidden",
    }}>
      {/* Top bar */}
      <header style={{
        padding: "8px 20px 12px",
        display: "flex", alignItems: "center", justifyContent: "space-between",
        flexShrink: 0,
      }}>
        {(() => {
          const s = window.finanzasStorage.getSession?.();
          const email = s?.user?.email || "";
          const username = email.split("@")[0] || "Usuario";
          const initials = (username || "?").slice(0, 2).toUpperCase();
          const displayName = username.charAt(0).toUpperCase() + username.slice(1);
          return (
        <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
          <div style={{
            width: 36, height: 36, borderRadius: 18,
            background: "linear-gradient(135deg, #1a2a5e, #2f4a92)",
            display: "inline-flex", alignItems: "center", justifyContent: "center",
            color: "#fff", fontSize: 12, fontWeight: 700,
          }}>{initials}</div>
          <div>
            <div style={{ fontSize: 11, color: "var(--bx-ink-3)", letterSpacing: "0.04em" }}>Buenos días</div>
            <div style={{ fontSize: 14, fontWeight: 700, color: "var(--bx-ink-1)" }}>{displayName}</div>
          </div>
        </div>
          );
        })()}
        <div style={{ display: "flex", gap: 8 }}>
          <button onClick={() => setShowSearch(true)} style={iconBtnSm()}>
            <Icon name="search" size={16} stroke={2} />
          </button>
          <button onClick={() => { setShowNotifs(true); markNotifsSeen(); }} style={{ ...iconBtnSm(), position: "relative" }}>
            <Icon name="bell" size={16} stroke={2} />
            {unseenCount > 0 && (
              <span style={{
                position: "absolute", top: 6, right: 6, width: 6, height: 6,
                borderRadius: 3, background: "var(--bx-danger)",
              }} />
            )}
          </button>
        </div>
      </header>

      {/* Scrollable body */}
      <div style={{ flex: 1, overflowY: "auto", paddingBottom: 12 }}>
      {tab === "inicio" && accs.length === 0 && (
        <div style={{ padding: "40px 24px", textAlign: "center" }}>
          <div style={{
            width: 72, height: 72, borderRadius: 36, margin: "0 auto 20px",
            background: "var(--bx-bg-tint)",
            color: "var(--bx-navy-700)",
            display: "inline-flex", alignItems: "center", justifyContent: "center",
          }}>
            <Icon name="wallet" size={32} stroke={1.5} />
          </div>
          <div style={{
            fontFamily: "Manrope, Inter, sans-serif",
            fontSize: 20, fontWeight: 800, color: "var(--bx-ink-1)",
          }}>Bienvenido</div>
          <div style={{ fontSize: 13, color: "var(--bx-ink-3)", marginTop: 8, lineHeight: 1.5 }}>
            Empieza creando tu primera cuenta.<br/>
            Banco, efectivo, tarjeta o inversión.
          </div>
          <button onClick={() => setShowAddAcc(true)} style={{
            marginTop: 24, padding: "14px 24px",
            background: "var(--bx-navy-700)", color: "#fff",
            border: "none", borderRadius: 10, cursor: "pointer",
            fontSize: 14, fontWeight: 600, letterSpacing: "0.04em",
            fontFamily: "Inter, sans-serif",
            display: "inline-flex", alignItems: "center", gap: 8,
          }}>
            <Icon name="plus" size={16} stroke={2.5} />
            Crear primera cuenta
          </button>
        </div>
      )}

      {tab === "inicio" && accs.length > 0 && (<>
        {/* Balance hero card */}
        <div style={{
          margin: "4px 16px 0",
          background: "var(--bx-navy-900)",
          borderRadius: 18,
          padding: "20px 22px 18px",
          color: "#fff",
          position: "relative",
          overflow: "hidden",
          backgroundImage: `radial-gradient(120% 80% at 100% 0%, #1f3a8a 0%, transparent 55%),
            radial-gradient(120% 80% at 0% 100%, #14245a 0%, transparent 55%),
            linear-gradient(135deg, #0a1230 0%, #14245a 50%, #1a2a5e 100%)`,
        }}>
          {/* Decorative ribbon */}
          <svg width="180" height="120" viewBox="0 0 180 120" style={{
            position: "absolute", top: -20, right: -40, opacity: 0.18,
          }}>
            <circle cx="90" cy="60" r="56" stroke="#fff" strokeWidth="1" fill="none" />
            <circle cx="90" cy="60" r="40" stroke="#fff" strokeWidth="1" fill="none" />
            <circle cx="90" cy="60" r="24" stroke="#fff" strokeWidth="1" fill="none" />
          </svg>
          <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
            <div style={{
              fontSize: 10, letterSpacing: "0.18em", textTransform: "uppercase",
              color: "rgba(255,255,255,0.7)", fontWeight: 600,
            }}>Balance total</div>
            <button onClick={() => setHide(h => !h)} style={{
              background: "transparent", border: "none", cursor: "pointer",
              color: "rgba(255,255,255,0.7)", padding: 0,
              display: "inline-flex", alignItems: "center",
            }}><Icon name={hide ? "eyeOff" : "eye"} size={13} stroke={2} /></button>
          </div>
          <div style={{
            fontFamily: "'Bebas Neue', sans-serif",
            fontSize: 52, lineHeight: 1, marginTop: 6, fontWeight: 400,
            letterSpacing: "0.01em",
            whiteSpace: "nowrap",
          }}>{window.fmtMXN(balance, { hide }).replace("$", "$ ")}</div>
          <div style={{ fontSize: 11, color: "rgba(255,255,255,0.6)", marginTop: 4 }}>
            {accs.length} {accs.length === 1 ? "cuenta" : "cuentas"} · MXN
          </div>

          {/* Stats split */}
          <div style={{
            display: "grid", gridTemplateColumns: "1fr 1fr", gap: 0,
            marginTop: 16, paddingTop: 14,
            borderTop: "1px solid rgba(255,255,255,0.12)",
          }}>
            <div>
              <div style={{
                fontSize: 9, letterSpacing: "0.18em", textTransform: "uppercase",
                color: "rgba(255,255,255,0.6)", fontWeight: 600, display: "flex", alignItems: "center", gap: 4,
              }}><Icon name="arrowUp" size={10} stroke={2.5} style={{ color: "#7fd4a7" }} />Ingresos</div>
              <div style={{
                fontFamily: "'JetBrains Mono', monospace", fontSize: 16, fontWeight: 700, marginTop: 4,
                color: "#7fd4a7", fontFeatureSettings: "'tnum'",
              }}>{window.fmtMXN(income)}</div>
            </div>
            <div style={{ borderLeft: "1px solid rgba(255,255,255,0.12)", paddingLeft: 16 }}>
              <div style={{
                fontSize: 9, letterSpacing: "0.18em", textTransform: "uppercase",
                color: "rgba(255,255,255,0.6)", fontWeight: 600, display: "flex", alignItems: "center", gap: 4,
              }}><Icon name="arrowDown" size={10} stroke={2.5} style={{ color: "#e68f86" }} />Gastos</div>
              <div style={{
                fontFamily: "'JetBrains Mono', monospace", fontSize: 16, fontWeight: 700, marginTop: 4,
                color: "#fff", fontFeatureSettings: "'tnum'",
              }}>{window.fmtMXN(spend)}</div>
            </div>
          </div>
        </div>

        {/* Accounts strip */}
        <div style={{ marginTop: 18 }}>
          <div style={{
            padding: "0 20px 8px", display: "flex", justifyContent: "space-between", alignItems: "baseline",
          }}>
            <div style={{
              fontFamily: "Manrope, Inter, sans-serif",
              fontSize: 15, fontWeight: 800, color: "var(--bx-ink-1)",
            }}>Cuentas</div>
            <button onClick={() => setShowAddAcc(true)} style={{
              fontSize: 11, color: "var(--bx-navy-700)", background: "transparent",
              border: "none", cursor: "pointer", fontWeight: 600,
              display: "inline-flex", alignItems: "center", gap: 4,
            }}>
              <Icon name="plus" size={12} stroke={2.5} />
              Nueva cuenta
            </button>
          </div>
          <div style={{
            display: "flex", gap: 10, overflowX: "auto",
            padding: "4px 20px 8px", scrollSnapType: "x mandatory",
            scrollbarWidth: "none",
          }}>
            {accs.map(a => (
              <div key={a.id} style={{ scrollSnapAlign: "start" }}>
                <window.AccountCard acc={a} compact onClick={() => setEditAcc(a)} />
              </div>
            ))}
          </div>
        </div>

        {/* Mini chart card */}
        <div style={{
          margin: "16px 16px 0",
          background: "var(--bx-paper)",
          border: "1px solid var(--bx-line)",
          borderRadius: 14,
          padding: "16px 18px",
        }}>
          <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline" }}>
            <div>
              <div style={{
                fontSize: 10, letterSpacing: "0.18em", textTransform: "uppercase",
                color: "var(--bx-ink-3)", fontWeight: 600,
              }}>Gasto diario · {monthName}</div>
              <div style={{
                fontFamily: "'Bebas Neue', sans-serif", fontSize: 30,
                marginTop: 4, fontWeight: 400, letterSpacing: "0.01em", lineHeight: 1,
                color: "var(--bx-ink-1)",
              }}>{window.fmtMXN(avgDaily).replace("$", "$ ")}</div>
              <div style={{ fontSize: 11, color: "var(--bx-ink-3)", marginTop: 2 }}>
                promedio diario · {dayOfMonth} {dayOfMonth === 1 ? "día" : "días"}
              </div>
            </div>
            {momPct !== null && (
              <div style={{
                padding: "4px 8px",
                background: momPct >= 0 ? "rgba(192,57,43,0.10)" : "rgba(42,138,95,0.10)",
                color: momPct >= 0 ? "var(--bx-danger)" : "var(--bx-success)",
                borderRadius: 6,
                fontSize: 11, fontWeight: 600,
                display: "flex", alignItems: "center", gap: 4,
              }}>
                <Icon name={momPct >= 0 ? "arrowUp" : "arrowDown"} size={10} stroke={2.5} />
                {momPct >= 0 ? "+" : ""}{Math.round(momPct)}%
              </div>
            )}
          </div>
          <div style={{ marginTop: 8 }}>
            <window.Sparkline values={trend} width={326} height={50} />
          </div>
        </div>

        {/* Recent movements */}
        <div style={{ margin: "16px 16px 0" }}>
          <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline", marginBottom: 6 }}>
            <div style={{
              fontFamily: "Manrope, Inter, sans-serif",
              fontSize: 15, fontWeight: 800, color: "var(--bx-ink-1)",
            }}>Últimos movimientos</div>
            <button onClick={() => setTab("transacciones")} style={{
              fontSize: 11, color: "var(--bx-navy-700)", background: "transparent",
              border: "none", cursor: "pointer", fontWeight: 600,
              display: "flex", alignItems: "center", gap: 2,
            }}>Ver todo <Icon name="chevR" size={11} stroke={2.5} /></button>
          </div>
          <div style={{
            background: "var(--bx-paper)",
            border: "1px solid var(--bx-line)",
            borderRadius: 14,
            padding: txs.length === 0 ? "20px 14px" : "4px 14px",
          }}>
            {txs.length === 0 ? (
              <div style={{ textAlign: "center", color: "var(--bx-ink-3)", fontSize: 13 }}>
                Sin movimientos. Pulsa <strong>+</strong> para añadir uno.
              </div>
            ) : txs.slice(0, 6).map((t, i) => (
              <SwipeableTxRow key={t.id} tx={t}
                last={i === Math.min(5, txs.length - 1)}
                swiped={swipedId === t.id}
                onSwipe={() => setSwipedId(s => s === t.id ? null : t.id)}
                onEdit={() => handleSwipeEdit(t)}
                onDelete={() => handleSwipeDelete(t)} />
            ))}
          </div>
        </div>

        {/* Budgets preview */}
        <div style={{ margin: "16px 16px 12px" }}>
          <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline", marginBottom: 6 }}>
            <div style={{
              fontFamily: "Manrope, Inter, sans-serif",
              fontSize: 15, fontWeight: 800, color: "var(--bx-ink-1)",
            }}>Presupuestos</div>
            <div style={{ fontSize: 11, color: "var(--bx-ink-3)" }}>{dayOfMonth} / {daysInMonth} días</div>
          </div>
          <div style={{
            background: "var(--bx-paper)",
            border: "1px solid var(--bx-line)",
            borderRadius: 14,
            padding: "10px 18px",
          }}>
            {budgets.length === 0 ? (
              <div style={{ padding: 16, textAlign: "center", color: "var(--bx-ink-3)", fontSize: 12 }}>
                Sin presupuestos. Configúralos en la pestaña Presupuesto.
              </div>
            ) : budgets.slice(0, 3).map(b => (
              <window.BudgetRow key={b.cat} b={b} />
            ))}
          </div>
        </div>
      </>)}

      {tab === "transacciones" && (
        <div style={{ padding: "12px 16px 24px" }}>
          <div style={{
            fontFamily: "Manrope, Inter, sans-serif", fontSize: 22, fontWeight: 800,
            color: "var(--bx-ink-1)", marginBottom: 12,
          }}>Movimientos</div>
          <window.TransactionsView txs={txs} accs={accs} onEdit={setEditTx} dense />
        </div>
      )}

      {tab === "presupuestos" && (
        <div style={{ padding: "12px 16px 24px", display: "flex", flexDirection: "column", gap: 14 }}>
          <div style={{
            fontFamily: "Manrope, Inter, sans-serif", fontSize: 22, fontWeight: 800,
            color: "var(--bx-ink-1)",
          }}>Presupuestos</div>
          <window.BudgetsView budgets={budgets} setBudgets={setBudgets} txs={txs} />
        </div>
      )}

      {tab === "ajustes" && (
        <div style={{ padding: "12px 16px 24px", display: "flex", flexDirection: "column", gap: 14 }}>
          <div style={{
            fontFamily: "Manrope, Inter, sans-serif", fontSize: 22, fontWeight: 800,
            color: "var(--bx-ink-1)",
          }}>Ajustes</div>
          <SettingsBlock txs={txs} onReset={() => {
            if (!confirm("¿Restablecer todos los datos? Esto borrará tus transacciones, cuentas y presupuestos.")) return;
            window.finanzasStorage.reset();
            location.reload();
          }} />
        </div>
      )}
      </div>

      {/* FAB */}
      {accs.length > 0 && (
        <button onClick={() => setShowAdd(true)} style={{
          position: "absolute",
          bottom: 96, right: 20,
          width: 60, height: 60, borderRadius: 30,
          background: "var(--bx-navy-700)",
          color: "#fff", border: "none", cursor: "pointer",
          display: "inline-flex", alignItems: "center", justifyContent: "center",
          boxShadow: "0 12px 32px -8px rgba(13,20,48,.5)",
          zIndex: 5,
        }}>
          <Icon name="plus" size={24} stroke={2.5} />
        </button>
      )}

      {/* Bottom tab bar */}
      <nav style={{
        flexShrink: 0,
        background: "rgba(255,255,255,0.92)",
        backdropFilter: "blur(20px)",
        borderTop: "1px solid var(--bx-line)",
        padding: "8px 0 28px",
        display: "grid", gridTemplateColumns: "repeat(5, 1fr)",
        alignItems: "center",
      }}>
        {MOB_TABS.map(t => {
          const isFab = t.id === "add";
          const active = tab === t.id;
          if (isFab) {
            return <div key={t.id} />; // FAB sits above
          }
          return (
            <button key={t.id} onClick={() => setTab(t.id)} style={{
              display: "flex", flexDirection: "column", alignItems: "center", gap: 4,
              background: "transparent", border: "none", cursor: "pointer",
              color: active ? "var(--bx-navy-700)" : "var(--bx-ink-3)",
              padding: "6px 0",
            }}>
              <Icon name={t.icon} size={20} stroke={active ? 2.25 : 1.75} />
              <div style={{
                fontSize: 10, letterSpacing: "0.04em",
                fontWeight: active ? 700 : 500,
              }}>{t.label}</div>
            </button>
          );
        })}
      </nav>

      {(showAdd || editTx) && (
        <window.AddExpenseForm variant="sheet"
          initial={editTx}
          accs={accs}
          onClose={() => { setShowAdd(false); setEditTx(null); }}
          onSave={handleSave}
          onDelete={handleDelete} />
      )}

      {(showAddAcc || editAcc) && (
        <AddAccountSheet
          initial={editAcc}
          onClose={() => { setShowAddAcc(false); setEditAcc(null); }}
          onSave={handleSaveAcc}
          onDelete={editAcc ? () => handleDeleteAcc(editAcc.id) : null} />
      )}

      {showNotifs && (
        <NotificationsSheet
          notifs={notifs}
          onClose={() => setShowNotifs(false)}
          onGoBudgets={() => { setShowNotifs(false); setTab("presupuestos"); }} />
      )}

      {showSearch && (
        <SearchOverlay
          txs={txs}
          onClose={() => setShowSearch(false)}
          onPick={(t) => { setShowSearch(false); setEditTx(t); }} />
      )}
    </div>
  );
}

function SearchOverlay({ txs, onClose, onPick }) {
  const Icon = window.FinanzasIcon;
  const [q, setQ] = mbState("");
  const inputRef = React.useRef(null);
  React.useEffect(() => { inputRef.current?.focus(); }, []);
  const results = mbMemo(() => {
    const qq = q.trim().toLowerCase();
    if (!qq) return [];
    return txs.filter(t => {
      const cat = window.catBy(t.cat)?.label.toLowerCase() || "";
      const acc = window.accBy(t.acc)?.name.toLowerCase() || "";
      const amt = Math.abs(t.amount).toString();
      return t.desc.toLowerCase().includes(qq)
        || cat.includes(qq) || acc.includes(qq) || amt.includes(qq)
        || (t.tags || []).some(x => x.toLowerCase().includes(qq));
    }).slice(0, 50);
  }, [q, txs]);
  return (
    <div onClick={onClose} style={{
      position: "absolute", inset: 0, zIndex: 100,
      background: "rgba(13, 20, 48, 0.32)",
      animation: "finanzasFadeIn 220ms cubic-bezier(0.16,1,0.3,1)",
    }}>
      <div onClick={(e) => e.stopPropagation()} style={{
        background: "var(--bx-paper)",
        height: "100%", display: "flex", flexDirection: "column",
      }}>
        <div style={{
          display: "flex", alignItems: "center", gap: 10,
          padding: "14px 16px", borderBottom: "1px solid var(--bx-line)",
        }}>
          <Icon name="search" size={18} stroke={2} style={{ color: "var(--bx-ink-3)" }} />
          <input ref={inputRef} value={q} onChange={(e) => setQ(e.target.value)}
            placeholder="Buscar por descripción, categoría, cuenta, monto, etiqueta…"
            style={{
              flex: 1, border: "none", outline: "none", background: "transparent",
              fontSize: 15, color: "var(--bx-ink-1)", fontFamily: "Inter, sans-serif",
            }} />
          <button onClick={onClose} style={{
            background: "transparent", border: "none", cursor: "pointer",
            color: "var(--bx-ink-2)", fontSize: 13, fontWeight: 600,
            fontFamily: "Inter, sans-serif",
          }}>Cancelar</button>
        </div>
        <div style={{ flex: 1, overflowY: "auto", padding: "4px 16px" }}>
          {!q.trim() ? (
            <div style={{ padding: "40px 16px", textAlign: "center", color: "var(--bx-ink-3)", fontSize: 13 }}>
              Escribe para buscar entre tus movimientos.
            </div>
          ) : results.length === 0 ? (
            <div style={{ padding: "40px 16px", textAlign: "center", color: "var(--bx-ink-3)", fontSize: 13 }}>
              Sin resultados para "{q}".
            </div>
          ) : results.map((t, i) => (
            <div key={t.id} onClick={() => onPick(t)}>
              <window.TxRow tx={t} dense last={i === results.length - 1} />
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

function AddAccountSheet({ onClose, onSave, onDelete = null, initial = null }) {
  const Icon = window.FinanzasIcon;
  const isEdit = !!initial;
  const [name, setName] = mbState(initial?.name || "");
  const [type, setType] = mbState(initial?.type || "Débito");
  const [balance, setBalance] = mbState(initial ? String(initial.balance) : "");
  const [last4, setLast4] = mbState(initial?.last4 || "");
  const [color, setColor] = mbState(initial?.color || "#1a2a5e");
  const TYPES = ["Débito", "Crédito", "Cash", "Inversión"];
  const COLORS = ["#1a2a5e", "#2f4a92", "#c0392b", "#2a8a5f", "#c98415", "#6a8fd6", "#8b5a2b", "#243a78"];
  const canSave = name.trim().length > 0;
  const handleSubmit = () => {
    if (!canSave) return;
    onSave({ ...(initial || {}), name, type, balance, last4, color });
  };
  return (
    <div onClick={onClose} style={{
      position: "absolute", inset: 0, zIndex: 100,
      background: "rgba(13, 20, 48, 0.32)",
      display: "flex", alignItems: "flex-end",
      animation: "finanzasFadeIn 220ms cubic-bezier(0.16,1,0.3,1)",
    }}>
      <div onClick={(e) => e.stopPropagation()} style={{
        background: "var(--bx-paper)",
        borderTopLeftRadius: 20, borderTopRightRadius: 20,
        width: "100%", maxHeight: "92%",
        display: "flex", flexDirection: "column",
        animation: "finanzasSheetUp 320ms cubic-bezier(0.16,1,0.3,1)",
        overflow: "hidden",
      }}>
        <div style={{ padding: "10px 0 0", display: "flex", justifyContent: "center" }}>
          <div style={{ width: 36, height: 4, borderRadius: 2, background: "var(--bx-line-2)" }} />
        </div>
        <div style={{
          padding: "16px 20px 12px",
          display: "flex", alignItems: "center", justifyContent: "space-between",
        }}>
          <div style={{
            fontFamily: "Manrope, Inter, sans-serif",
            fontSize: 17, fontWeight: 800, color: "var(--bx-ink-1)",
          }}>{isEdit ? "Editar cuenta" : "Nueva cuenta"}</div>
          <button onClick={onClose} style={{
            background: "var(--bx-bg)", border: "none", cursor: "pointer",
            width: 32, height: 32, borderRadius: 16,
            display: "inline-flex", alignItems: "center", justifyContent: "center",
            color: "var(--bx-ink-2)",
          }}><Icon name="x" size={16} stroke={2} /></button>
        </div>
        <div style={{ padding: "8px 20px 12px", display: "flex", flexDirection: "column", gap: 14, overflowY: "auto" }}>
          <div>
            <div style={accFieldLabel()}>Nombre</div>
            <input value={name} onChange={(e) => setName(e.target.value)}
              placeholder="ej. Santander, BBVA, Efectivo"
              style={accInputStyle()} />
          </div>
          <div>
            <div style={accFieldLabel()}>Tipo</div>
            <div style={{ display: "grid", gridTemplateColumns: "repeat(4, 1fr)", gap: 8 }}>
              {TYPES.map(t => {
                const sel = t === type;
                return (
                  <button key={t} onClick={() => setType(t)} style={{
                    padding: "10px 4px", fontSize: 12, fontWeight: 600,
                    background: sel ? "var(--bx-navy-700)" : "var(--bx-bg)",
                    color: sel ? "#fff" : "var(--bx-ink-2)",
                    border: `1px solid ${sel ? "var(--bx-navy-700)" : "var(--bx-line)"}`,
                    borderRadius: 8, cursor: "pointer",
                  }}>{t}</button>
                );
              })}
            </div>
          </div>
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10 }}>
            <div>
              <div style={accFieldLabel()}>Saldo inicial</div>
              <input value={balance} type="number" inputMode="decimal"
                onChange={(e) => setBalance(e.target.value)}
                placeholder="0" style={accInputStyle()} />
            </div>
            <div>
              <div style={accFieldLabel()}>Últimos 4</div>
              <input value={last4} onChange={(e) => setLast4(e.target.value.replace(/[^\d]/g, "").slice(0, 4))}
                placeholder="0000" maxLength={4} inputMode="numeric"
                style={accInputStyle()} />
            </div>
          </div>
          <div>
            <div style={accFieldLabel()}>Color</div>
            <div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
              {COLORS.map(c => (
                <button key={c} onClick={() => setColor(c)} style={{
                  width: 32, height: 32, borderRadius: 16,
                  background: c, border: c === color ? "3px solid var(--bx-ink-1)" : "2px solid var(--bx-line)",
                  cursor: "pointer", padding: 0,
                }} />
              ))}
            </div>
          </div>
        </div>
        <div style={{ padding: "12px 20px 24px", borderTop: "1px solid var(--bx-line)", display: "flex", gap: 10 }}>
          {isEdit && onDelete && (
            <button onClick={onDelete} style={{
              padding: "16px 20px",
              fontSize: 14, fontFamily: "Inter, sans-serif",
              fontWeight: 600, letterSpacing: "0.04em", textTransform: "uppercase",
              background: "transparent", color: "var(--bx-danger)",
              border: "1px solid var(--bx-danger)", borderRadius: 10,
              cursor: "pointer",
            }}>Borrar</button>
          )}
          <button onClick={handleSubmit} disabled={!canSave} style={{
            flex: 1, padding: "16px",
            fontSize: 14, fontFamily: "Inter, sans-serif",
            fontWeight: 600, letterSpacing: "0.04em", textTransform: "uppercase",
            background: canSave ? "var(--bx-navy-700)" : "var(--bx-ink-4)",
            color: "#fff", border: "none", borderRadius: 10,
            cursor: canSave ? "pointer" : "not-allowed",
          }}>{isEdit ? "Guardar cambios" : "Guardar cuenta"}</button>
        </div>
      </div>
    </div>
  );
}

function NotificationsSheet({ notifs, onClose, onGoBudgets }) {
  const Icon = window.FinanzasIcon;
  return (
    <div onClick={onClose} style={{
      position: "absolute", inset: 0, zIndex: 100,
      background: "rgba(13, 20, 48, 0.32)",
      display: "flex", alignItems: "flex-end",
      animation: "finanzasFadeIn 220ms cubic-bezier(0.16,1,0.3,1)",
    }}>
      <div onClick={(e) => e.stopPropagation()} style={{
        background: "var(--bx-paper)",
        borderTopLeftRadius: 20, borderTopRightRadius: 20,
        width: "100%", maxHeight: "70%",
        display: "flex", flexDirection: "column",
        animation: "finanzasSheetUp 320ms cubic-bezier(0.16,1,0.3,1)",
        overflow: "hidden",
      }}>
        <div style={{ padding: "10px 0 0", display: "flex", justifyContent: "center" }}>
          <div style={{ width: 36, height: 4, borderRadius: 2, background: "var(--bx-line-2)" }} />
        </div>
        <div style={{
          padding: "16px 20px 12px",
          display: "flex", alignItems: "center", justifyContent: "space-between",
        }}>
          <div style={{
            fontFamily: "Manrope, Inter, sans-serif",
            fontSize: 17, fontWeight: 800, color: "var(--bx-ink-1)",
          }}>Notificaciones</div>
          <button onClick={onClose} style={{
            background: "var(--bx-bg)", border: "none", cursor: "pointer",
            width: 32, height: 32, borderRadius: 16,
            display: "inline-flex", alignItems: "center", justifyContent: "center",
            color: "var(--bx-ink-2)",
          }}><Icon name="x" size={16} stroke={2} /></button>
        </div>
        <div style={{ padding: "4px 20px 24px", overflowY: "auto" }}>
          {notifs.length === 0 ? (
            <div style={{ padding: "32px 16px", textAlign: "center", color: "var(--bx-ink-3)", fontSize: 13 }}>
              Sin notificaciones nuevas.
            </div>
          ) : notifs.map(n => {
            const cat = window.catBy(n.cat);
            const over = n.pct >= 1;
            return (
              <button key={n.cat} onClick={onGoBudgets} style={{
                width: "100%", textAlign: "left",
                display: "flex", alignItems: "center", gap: 12,
                padding: "12px 0", borderBottom: "1px solid var(--bx-line)",
                background: "transparent", border: "none", borderBottomColor: "var(--bx-line)",
                cursor: "pointer",
              }}>
                <window.CatBadge catId={n.cat} size={36} />
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontSize: 13, fontWeight: 600, color: "var(--bx-ink-1)" }}>
                    {over ? `Excediste presupuesto de ${cat.label}` : `Cerca del límite en ${cat.label}`}
                  </div>
                  <div style={{ fontSize: 11, color: "var(--bx-ink-3)", marginTop: 2 }}>
                    {window.fmtMXN(n.spent)} de {window.fmtMXN(n.limit)} · {Math.round(n.pct * 100)}%
                  </div>
                </div>
                <Icon name="chevR" size={14} stroke={2} style={{ color: "var(--bx-ink-3)" }} />
              </button>
            );
          })}
        </div>
      </div>
    </div>
  );
}

function accFieldLabel() {
  return {
    fontSize: 11, letterSpacing: "0.16em", textTransform: "uppercase",
    color: "var(--bx-ink-3)", fontWeight: 600, marginBottom: 8,
    fontFamily: "Inter, sans-serif",
  };
}

function accInputStyle() {
  return {
    width: "100%", padding: "12px 14px",
    background: "var(--bx-bg)",
    border: "1px solid var(--bx-line)",
    borderRadius: 8,
    fontSize: 14, fontFamily: "Inter, sans-serif", color: "var(--bx-ink-1)",
    outline: "none",
  };
}

function SwipeableTxRow({ tx, last, swiped, onSwipe, onEdit, onDelete }) {
  const Icon = window.FinanzasIcon;
  return (
    <div style={{
      position: "relative", overflow: "hidden",
      borderBottom: last ? "none" : "1px solid var(--bx-line)",
    }}>
      {/* Action panel revealed by swipe */}
      <div style={{
        position: "absolute", top: 0, bottom: 0, right: 0,
        display: "flex", alignItems: "stretch",
        opacity: swiped ? 1 : 0, pointerEvents: swiped ? "auto" : "none",
        transition: "opacity 120ms",
      }}>
        <button onClick={onEdit} style={{
          width: 64, background: "var(--bx-bg-tint)", color: "var(--bx-navy-700)",
          border: "none", cursor: "pointer", display: "flex", flexDirection: "column",
          alignItems: "center", justifyContent: "center", gap: 4,
          fontSize: 10, fontWeight: 600, letterSpacing: "0.04em",
        }}>
          <Icon name="tag" size={16} stroke={2} />
          Editar
        </button>
        <button onClick={onDelete} style={{
          width: 64, background: "var(--bx-danger)", color: "#fff",
          border: "none", cursor: "pointer", display: "flex", flexDirection: "column",
          alignItems: "center", justifyContent: "center", gap: 4,
          fontSize: 10, fontWeight: 600, letterSpacing: "0.04em",
        }}>
          <Icon name="x" size={16} stroke={2.5} />
          Borrar
        </button>
      </div>
      {/* Row */}
      <div onClick={onSwipe} style={{
        background: "var(--bx-paper)",
        transform: swiped ? "translateX(-128px)" : "translateX(0)",
        transition: "transform 220ms cubic-bezier(0.2,0.8,0.2,1)",
      }}>
        <window.TxRow tx={tx} dense last />
      </div>
    </div>
  );
}

function SettingsBlock({ onReset, txs }) {
  const prefs = window.finanzasStorage.loadPrefs();
  const [dark, setDark] = mbState(prefs.dark);
  const session = window.finanzasStorage.getSession?.();
  const email = session?.user?.email || "";
  const toggleDark = () => {
    const next = !dark;
    setDark(next);
    window.finanzasStorage.savePrefs({ ...window.finanzasStorage.loadPrefs(), dark: next });
    document.documentElement.classList.toggle("dark", next);
  };
  const signOut = async () => {
    if (!confirm("¿Cerrar sesión?")) return;
    await window.finanzasStorage.signOut();
    location.reload();
  };
  const exportCsv = () => {
    if (!txs || !txs.length) { window.toast?.show("Sin movimientos para exportar"); return; }
    const csv = window.exportTxCsv(txs);
    const stamp = new Date().toISOString().slice(0, 10);
    window.downloadFile(`finanzas-${stamp}.csv`, csv);
    window.toast?.success("CSV descargado");
  };
  return (
    <div style={{
      background: "var(--bx-paper)", border: "1px solid var(--bx-line)",
      borderRadius: 14, overflow: "hidden",
    }}>
      {email && <Setting label="Cuenta" value={email} />}
      <Setting label="Modo oscuro" value={dark ? "Activado" : "Desactivado"} onClick={toggleDark} />
      <Setting label="Exportar movimientos" value="CSV" onClick={exportCsv} />
      <Setting label="Versión" value="1.1.0 · cloud" />
      <Setting label="Cerrar sesión" value="Salir" onClick={signOut} />
      <Setting label="Restablecer datos" value="Borrar todo" danger onClick={onReset} />
    </div>
  );
}

function Setting({ label, value, danger, onClick }) {
  return (
    <button onClick={onClick} disabled={!onClick} style={{
      width: "100%", padding: "14px 16px",
      display: "flex", alignItems: "center", justifyContent: "space-between",
      background: "transparent", border: "none",
      borderBottom: "1px solid var(--bx-line)",
      cursor: onClick ? "pointer" : "default",
      textAlign: "left", fontFamily: "Inter, sans-serif",
    }}>
      <span style={{ fontSize: 14, color: "var(--bx-ink-1)", fontWeight: 500 }}>{label}</span>
      <span style={{
        fontSize: 12, color: danger ? "var(--bx-danger)" : "var(--bx-ink-3)",
        fontWeight: 500,
      }}>{value}</span>
    </button>
  );
}

function iconBtnSm() {
  return {
    width: 36, height: 36, borderRadius: 18,
    background: "var(--bx-paper)", border: "1px solid var(--bx-line)",
    color: "var(--bx-ink-2)", cursor: "pointer",
    display: "inline-flex", alignItems: "center", justifyContent: "center",
  };
}

Object.assign(window, { MobileApp });
