// Settings view — configurar empresa, categorias, formas de pagamento, meta

const BUSINESS_ICONS = [
  { key: "briefcase",   label: "Empresa" },
  { key: "building",    label: "Escritório" },
  { key: "factory",     label: "Fábrica" },
  { key: "store",       label: "Loja" },
  { key: "truck",       label: "Transporte" },
  { key: "tools",       label: "Construção" },
  { key: "coffee",      label: "Alimentação" },
  { key: "book",        label: "Educação" },
  { key: "heart",       label: "Saúde" },
  { key: "laptop",      label: "Tecnologia" },
  { key: "users",       label: "Pessoas" },
  { key: "person",      label: "Pessoal" },
  { key: "phone",       label: "Telecom" },
  { key: "camera",      label: "Foto/Mídia" },
  { key: "music",       label: "Entretenimento" },
  { key: "scissors",    label: "Estética" },
  { key: "key",         label: "Imóveis" },
  { key: "box",         label: "Logística" },
  { key: "globe",       label: "Internacional" },
  { key: "shield",      label: "Segurança" },
  { key: "mapPin",      label: "Local" },
  { key: "shoppingBag", label: "Varejo" },
  { key: "leaf",        label: "Agro/Eco" },
  { key: "headphones",  label: "Suporte" },
  { key: "zap",         label: "Energia" },
  { key: "palette",     label: "Design/Arte" },
  { key: "car",         label: "Locação" },
  { key: "scale",       label: "Advocacia" },
  { key: "home",        label: "Casa/Lar" },
  { key: "wallet",      label: "Finanças" },
  { key: "target",      label: "Meta" },
  { key: "chart",       label: "Relatório" },
  { key: "trending",    label: "Crescimento" },
  { key: "dashboard",   label: "Gestão" },
  { key: "spark",       label: "Premium" },
  { key: "dollar",      label: "Dinheiro" },
  { key: "percent",     label: "Tributos" },
  { key: "tag",         label: "Categoria" },
  { key: "calendar",    label: "Agenda" },
];

// Abre seletor de arquivo e retorna base64 via callback
function pickImage(onDone) {
  const inp = document.createElement("input");
  inp.type = "file";
  inp.accept = "image/*";
  inp.onchange = e => {
    const file = e.target.files[0];
    if (!file) return;
    const reader = new FileReader();
    reader.onload = ev => onDone(ev.target.result);
    reader.readAsDataURL(file);
  };
  inp.click();
}

// ---- Exportar CSV ----
function exportCSV(ws) {
  const sep = ";";
  const fmtDate = (d) => {
    if (!d) return "";
    const [y, m, day] = String(d).split("-");
    return `${day}/${m}/${y}`;
  };
  const fmtVal = (v) => (Number(v) || 0).toFixed(2).replace(".", ",");

  const header = ["Tipo", "Data", "Descrição", "Valor (R$)", "Status", "Pagamento", "Categoria", "Subcategoria", "Membro"].join(sep);
  const esc = (s) => `"${String(s || "").replace(/"/g, '""')}"`;

  const rows = [];
  for (const t of (ws.transactions || [])) {
    rows.push(["Despesa", fmtDate(t.date), esc(t.desc), fmtVal(t.value), t.status || "", t.payment || "", t.category || "", t.subcategory || "", t.who || ""].join(sep));
  }
  for (const r of (ws.receitas || [])) {
    rows.push(["Receita", fmtDate(r.date), esc(r.desc), fmtVal(r.value), r.status || "", "", r.category || "", "", r.who || ""].join(sep));
  }

  const csv = "﻿" + [header, ...rows].join("\r\n");
  const blob = new Blob([csv], { type: "text/csv;charset=utf-8" });
  const url = URL.createObjectURL(blob);
  const a = document.createElement("a");
  a.href = url;
  a.download = `${(ws.name || "financeiro").replace(/\s+/g, "_")}_lancamentos.csv`;
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
  URL.revokeObjectURL(url);
}

// ---- Importar CSV ----

// Extrai mês/ano de contexto a partir do nome de uma aba
// Ex.: "Janeiro 2026" → {month:0, year:2026}, "Abr/26" → {month:3, year:2026}, "01-2026" → {month:0, year:2026}
function extractSheetDateContext(sheetName) {
  if (!sheetName) return { month: null, year: null };
  const s = String(sheetName).toLowerCase();
  const monthNames = {
    janeiro:0,jan:0, fevereiro:1,fev:1, "março":2,marco:2,mar:2,
    abril:3,abr:3, maio:4,mai:4, junho:5,jun:5,
    julho:6,jul:6, agosto:7,ago:7, setembro:8,set:8,
    outubro:9,out:9, novembro:10,nov:10, dezembro:11,dez:11,
  };
  let month = null, year = null;
  for (const name of Object.keys(monthNames)) {
    const re = new RegExp(`(^|[^a-zçãéí])${name}([^a-zçãéí]|$)`, "i");
    if (re.test(s)) { month = monthNames[name]; break; }
  }
  // Numérico: MM/YYYY · MM-YYYY · MM.YYYY · MM/YY
  const mmYY = s.match(/(?:^|\D)(0?[1-9]|1[0-2])[\/\-\.](\d{2,4})(?:\D|$)/);
  if (mmYY) {
    if (month === null) month = parseInt(mmYY[1], 10) - 1;
    let y = parseInt(mmYY[2], 10);
    if (y < 100) y += 2000;
    year = y;
  }
  // Ano de 4 dígitos isolado
  if (year === null) {
    const yr = s.match(/\b(19|20)\d{2}\b/);
    if (yr) year = parseInt(yr[0], 10);
  }
  // Ano abreviado de 2 dígitos após / - . ou espaço
  if (year === null) {
    const sh = s.match(/[\/\-\.\s'](\d{2})\b/);
    if (sh) year = 2000 + parseInt(sh[1], 10);
  }
  return { month, year };
}

// Aceita Date, número-serial Excel, dd/mm/yyyy, dd-mm-yyyy, dd.mm.yyyy, dd/mm, "15", yyyy-mm-dd
// `ctx` opcional: { month, year } extraído do nome da aba para completar datas incompletas
function parseCsvDate(d, ctx) {
  const fillFromCtx = () => {
    if (!ctx || ctx.month == null || ctx.year == null) return null;
    return `${ctx.year}-${String(ctx.month + 1).padStart(2, "0")}-01`;
  };
  if (d == null || d === "") return fillFromCtx();

  // Se já veio Date (cellDates do XLSX)
  if (d instanceof Date && !isNaN(d)) {
    return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
  }
  const s = String(d).trim();
  if (!s) return fillFromCtx();

  // ISO yyyy-mm-dd
  if (/^\d{4}-\d{2}-\d{2}$/.test(s)) return s;

  // dd/mm/yyyy · dd-mm-yyyy · dd.mm.yyyy (com 2 ou 4 dígitos no ano)
  const br = s.match(/^(\d{1,2})[\/\-\.](\d{1,2})[\/\-\.](\d{2,4})$/);
  if (br) {
    let y = parseInt(br[3], 10);
    if (y < 100) y += 2000;
    return `${y}-${br[2].padStart(2, "0")}-${br[1].padStart(2, "0")}`;
  }

  // dd/mm · dd-mm · dd.mm (sem ano) — completa com ctx ou ano atual
  const dm = s.match(/^(\d{1,2})[\/\-\.](\d{1,2})$/);
  if (dm) {
    const y = ctx?.year ?? new Date().getFullYear();
    return `${y}-${dm[2].padStart(2, "0")}-${dm[1].padStart(2, "0")}`;
  }

  // Apenas dia (1-31) — usa mês/ano da aba
  if (/^\d{1,2}$/.test(s)) {
    const day = parseInt(s, 10);
    if (day >= 1 && day <= 31 && ctx?.month != null && ctx?.year != null) {
      return `${ctx.year}-${String(ctx.month + 1).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
    }
  }

  // Número-serial do Excel (>1000 evita confundir com dia)
  if (/^\d+(\.\d+)?$/.test(s) && parseFloat(s) > 1000) {
    const n = parseFloat(s);
    const ms = Math.round((n - 25569) * 86400 * 1000);
    const dt = new Date(ms);
    if (!isNaN(dt)) return `${dt.getUTCFullYear()}-${String(dt.getUTCMonth() + 1).padStart(2, "0")}-${String(dt.getUTCDate()).padStart(2, "0")}`;
  }

  return fillFromCtx();
}
function parseCsvValue(v) {
  let s = String(v || "0").replace(/[R$\s]/g, "");
  if (s.includes(",") && s.includes(".")) s = s.replace(/\./g, "").replace(",", ".");
  else if (s.includes(",")) s = s.replace(",", ".");
  return parseFloat(s) || 0;
}
function parseCsvLine(line, sep) {
  const result = []; let curr = "", inQ = false;
  for (let i = 0; i < line.length; i++) {
    const ch = line[i];
    if (ch === '"' && !inQ) inQ = true;
    else if (ch === '"' && inQ) inQ = false;
    else if (ch === sep && !inQ) { result.push(curr); curr = ""; }
    else curr += ch;
  }
  result.push(curr);
  return result;
}

// Converte valor de célula Excel para string legível
function xlCell(val) {
  if (val === null || val === undefined) return "";
  if (val instanceof Date) {
    const y = val.getFullYear();
    const m = String(val.getMonth() + 1).padStart(2, "0");
    const d = String(val.getDate()).padStart(2, "0");
    return `${d}/${m}/${y}`;
  }
  return String(val).trim();
}

// Extrai { headers, rows } de uma aba do workbook SheetJS
// Cada linha recebe __sheet = sheetName para que parseCsvDate possa usar como contexto.
function xlParseSheet(wb, sheetName) {
  const sheet = wb.Sheets[sheetName];
  const mat = window.XLSX.utils.sheet_to_json(sheet, { header: 1, raw: true, defval: "" });
  // Encontra primeira linha não-vazia como cabeçalho
  let hi = 0;
  while (hi < mat.length && !mat[hi].some(c => c !== "" && c !== null && c !== undefined)) hi++;
  if (hi >= mat.length) return { headers: [], rows: [] };
  const rawHdrs = mat[hi];
  const headers = rawHdrs.map(h => xlCell(h)).filter(Boolean);
  const rows = mat.slice(hi + 1)
    .filter(r => r.some(c => c !== "" && c !== null && c !== undefined))
    .map(r => {
      const o = {};
      rawHdrs.forEach((h, i) => { const k = xlCell(h); if (k) o[k] = xlCell(r[i]); });
      o.__sheet = sheetName;
      return o;
    });
  return { headers, rows };
}

function SmartImportModal({ ws, setWs, onClose, setRangeMode, setCustomRange }) {
  // step: "init" | "key" | "upload" | "loading" | "preview" | "done"
  const [step, setStep] = React.useState("init");
  const [apiKey, setApiKey] = React.useState("");
  const [showKey, setShowKey] = React.useState(false);
  const [filename, setFilename] = React.useState("");
  const [preview, setPreview] = React.useState(null);
  const [err, setErr] = React.useState(null);
  const [progress, setProgress] = React.useState("Lendo planilha…");

  React.useEffect(() => {
    const fn = e => { if (e.key === "Escape" && step !== "loading") onClose(); };
    window.addEventListener("keydown", fn);
    return () => window.removeEventListener("keydown", fn);
  }, [onClose, step]);

  const [provider, setProvider] = React.useState("anthropic");

  React.useEffect(() => {
    window.db.getAiConfig().then(({ provider: p, key: k }) => {
      if (p) setProvider(p);
      if (k) { setApiKey(k); setStep("upload"); }
      else setStep("key");
    });
  }, []);

  const saveKeyAndProceed = async () => {
    if (!apiKey.trim()) return;
    await window.db.setAiConfig(provider, apiKey.trim());
    setStep("upload");
  };

  const handleFile = async (file) => {
    if (!file) return;
    setFilename(file.name);
    setErr(null);
    setStep("loading");
    setProgress("Lendo planilha…");
    try {
      const rawData = await aiExtractRaw(file);
      setProgress("Analisando com IA…");
      const result = await aiParseWithClaude(rawData, ws, (msg) => setProgress(msg));
      const expCats = new Set((ws.categories || []).map(c => c.toUpperCase()));
      const incCats = new Set((ws.incomeCategories || []).map(c => c.toUpperCase()));
      const fitCat = (cat, fallback, allowed) => {
        const c = (cat || "").toUpperCase().trim();
        return c && allowed.has(c) ? c : fallback;
      };
      // Despesas: aplica MERCHANT_RULES (Uber/iFood/Netflix...) ANTES de validar contra a lista,
      // pra a IA não conseguir errar em mercados conhecidos. Depois cai pra OUTROS se a categoria
      // resultante não existir nas classificações do usuário.
      const txns = result.transactions.filter(t => t.type === "expense").map(t => {
        const enriched = applyMerchantRules({ ...t, category: (t.category || "").toUpperCase() });
        const finalCat = fitCat(enriched.category, "OUTROS", expCats);
        return toUiTxn({ ...enriched, category: finalCat });
      });
      const recs = result.transactions.filter(t => t.type === "income")
        .map(t => ({ ...t, category: fitCat(t.category, "OUTRAS RECEITAS", incCats) }))
        .map(toUiTxn);
      setPreview({ txns, recs, total: result.transactions.length });
      setStep("preview");
    } catch (ex) {
      console.error(ex);
      setErr(ex.message || String(ex));
      setStep("upload");
    }
  };

  const doImport = () => {
    // Mantém só as classificações existentes — não adiciona novas no import
    setWs(prev => ({
      ...prev,
      transactions: [...(prev.transactions || []), ...preview.txns],
      receitas: [...(prev.receitas || []), ...preview.recs],
    }));
    // Expande o filtro de período pra cobrir as importações sem esconder dados anteriores
    const allDates = [...preview.txns, ...preview.recs].map(t => t.date).filter(Boolean).sort();
    if (allDates.length && setRangeMode && setCustomRange) {
      const newFrom = allDates[0];
      const newTo = allDates[allDates.length - 1];
      setRangeMode("PERSONALIZADO");
      setCustomRange(prev => {
        if (!prev || !prev.from || !prev.to) return { from: newFrom, to: newTo };
        return {
          from: prev.from < newFrom ? prev.from : newFrom,
          to: prev.to > newTo ? prev.to : newTo,
        };
      });
    }
    setStep("done");
  };

  const changeKey = () => { setStep("key"); };

  return (
    <div className="modal-backdrop" onClick={() => step !== "loading" && onClose()}>
      <div className="modal" onClick={e => e.stopPropagation()} style={{ width: 540, maxWidth: "calc(100vw - 24px)", maxHeight: "calc(100vh - 40px)", overflowY: "auto", padding: 24 }}>

        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start", marginBottom: 18 }}>
          <div>
            <div style={{ fontSize: 10.5, fontWeight: 600, letterSpacing: 0.8, textTransform: "uppercase", color: "var(--fg-3)", marginBottom: 3 }}>
              <Icon name="spark" size={11} /> Importar com IA
            </div>
            <h3 style={{ margin: 0, fontSize: 16, fontWeight: 600 }}>
              {step === "key" && "Configurar chave de IA"}
              {step === "upload" && "Selecionar planilha"}
              {step === "loading" && "Processando…"}
              {step === "preview" && "Confira antes de importar"}
              {step === "done" && "Concluído!"}
            </h3>
          </div>
          {step !== "loading" && (
            <button className="icon-btn" onClick={onClose}><Icon name="x" size={16} /></button>
          )}
        </div>

        {step === "key" && (
          <div>
            <p style={{ fontSize: 13, color: "var(--fg-2)", margin: "0 0 16px", lineHeight: 1.5 }}>
              A IA vai ler sua planilha e organizar os lançamentos automaticamente. Escolha um provedor e cole sua chave (fica salva só neste navegador).
            </p>

            <div style={{ marginBottom: 12 }}>
              <div style={{ fontSize: 11.5, color: "var(--fg-3)", textTransform: "uppercase", letterSpacing: 0.5, fontWeight: 500, marginBottom: 6 }}>
                Provedor
              </div>
              <div style={{ display: "flex", gap: 6 }}>
                {Object.entries(AI_PROVIDERS).map(([k, v]) => {
                  const active = provider === k;
                  return (
                    <button key={k} type="button" onClick={() => setProvider(k)} style={{
                      flex: 1, padding: "10px 12px", fontFamily: "inherit", fontSize: 12, fontWeight: active ? 600 : 400,
                      border: active ? "2px solid var(--accent)" : "1px solid var(--border)",
                      background: active ? "color-mix(in oklab, var(--accent) 6%, var(--bg-1))" : "var(--bg-1)",
                      color: active ? "var(--fg-1)" : "var(--fg-2)",
                      borderRadius: 8, cursor: "pointer",
                    }}>
                      {v.label}
                    </button>
                  );
                })}
              </div>
            </div>

            <div style={{ display: "flex", gap: 8, alignItems: "center", marginBottom: 12 }}>
              <input
                type={showKey ? "text" : "password"}
                className="input"
                value={apiKey}
                onChange={e => setApiKey(e.target.value)}
                placeholder={AI_PROVIDERS[provider].placeholder}
                autoFocus
                style={{ flex: 1, fontFamily: "ui-monospace, monospace", fontSize: 12.5 }}
              />
              <button className="btn-ghost" onClick={() => setShowKey(s => !s)}>
                {showKey ? "Ocultar" : "Mostrar"}
              </button>
            </div>
            <div style={{ padding: "10px 12px", background: "var(--surface-2)", borderRadius: 8, fontSize: 11.5, color: "var(--fg-2)", lineHeight: 1.5, marginBottom: 16 }}>
              Não tem chave? Crie em <a href={AI_PROVIDERS[provider].keysUrl} target="_blank" rel="noopener" style={{ color: "var(--fg-1)", textDecoration: "underline" }}>{AI_PROVIDERS[provider].keysLabel}</a>.
              <br />{AI_PROVIDERS[provider].note}
            </div>
            <div style={{ display: "flex", gap: 8, justifyContent: "flex-end" }}>
              <button className="btn-ghost" onClick={onClose}>Cancelar</button>
              <button className="btn-primary" disabled={!apiKey.trim()} onClick={saveKeyAndProceed} style={{ opacity: !apiKey.trim() ? 0.5 : 1 }}>
                Salvar e continuar →
              </button>
            </div>
          </div>
        )}

        {step === "upload" && (
          <div>
            <p style={{ fontSize: 13, color: "var(--fg-2)", margin: "0 0 16px", lineHeight: 1.5 }}>
              Envie qualquer planilha — Excel ou CSV. A IA detecta colunas, datas, abas e tipos automaticamente.
            </p>
            <label style={{ display: "flex", flexDirection: "column", alignItems: "center", gap: 12, padding: "36px 24px", border: "2px dashed var(--border)", borderRadius: 14, cursor: "pointer", background: "var(--surface-2)" }}>
              <div style={{ width: 52, height: 52, borderRadius: 14, background: "color-mix(in oklab, var(--accent) 12%, transparent)", color: "var(--accent)", display: "flex", alignItems: "center", justifyContent: "center" }}>
                <Icon name="spark" size={24} />
              </div>
              <div style={{ textAlign: "center" }}>
                <div style={{ fontSize: 14, fontWeight: 600, color: "var(--fg-1)", marginBottom: 5 }}>Clique para selecionar o arquivo</div>
                <div style={{ fontSize: 12, color: "var(--fg-3)" }}>Excel (.xlsx, .xls, .ods) · CSV (.csv)</div>
              </div>
              <input type="file" accept=".csv,.xlsx,.xls,.ods,.txt" style={{ display: "none" }} onChange={e => { if (e.target.files[0]) handleFile(e.target.files[0]); }} />
            </label>
            {err && (
              <div style={{ padding: "10px 12px", background: "color-mix(in oklab, var(--danger) 8%, transparent)", border: "1px solid color-mix(in oklab, var(--danger) 25%, transparent)", borderRadius: 8, fontSize: 12.5, color: "var(--danger)", marginTop: 14 }}>
                {err}
              </div>
            )}
            <div style={{ display: "flex", justifyContent: "flex-end", marginTop: 14 }}>
              <button className="btn-ghost" onClick={changeKey} style={{ fontSize: 11.5, color: "var(--fg-3)" }}>
                Trocar chave de IA
              </button>
            </div>
          </div>
        )}

        {step === "loading" && (
          <div style={{ padding: "40px 0", textAlign: "center" }}>
            <div style={{ width: 56, height: 56, borderRadius: 16, background: "color-mix(in oklab, var(--accent) 12%, transparent)", color: "var(--accent)", display: "flex", alignItems: "center", justifyContent: "center", margin: "0 auto 18px", animation: "spin 1.4s linear infinite" }}>
              <Icon name="spark" size={26} />
            </div>
            <div style={{ fontSize: 14, fontWeight: 600, color: "var(--fg-1)", marginBottom: 6 }}>{progress}</div>
            <div style={{ fontSize: 12, color: "var(--fg-3)" }}>{filename}</div>
            <div style={{ fontSize: 11.5, color: "var(--fg-3)", marginTop: 18, lineHeight: 1.5 }}>
              Pode levar 5–30 segundos dependendo do tamanho da planilha.
            </div>
            <style>{`@keyframes spin { to { transform: rotate(360deg); } }`}</style>
          </div>
        )}

        {step === "preview" && preview && (() => {
          const dates = [...preview.txns, ...preview.recs].map(t => t.date).filter(Boolean).sort();
          const dateRangeStr = dates.length ? `${formatBR(dates[0])} → ${formatBR(dates[dates.length - 1])}` : "";
          return (
          <div>
            <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12, marginBottom: 14 }}>
              <div style={{ padding: "16px", background: "color-mix(in oklab, var(--danger) 8%, var(--surface-1))", borderRadius: 10, textAlign: "center" }}>
                <div style={{ fontSize: 32, fontWeight: 700, color: "var(--danger)" }}>{preview.txns.length}</div>
                <div style={{ fontSize: 12.5, color: "var(--fg-2)" }}>despesa{preview.txns.length !== 1 ? "s" : ""}</div>
              </div>
              <div style={{ padding: "16px", background: "color-mix(in oklab, var(--success) 8%, var(--surface-1))", borderRadius: 10, textAlign: "center" }}>
                <div style={{ fontSize: 32, fontWeight: 700, color: "var(--success)" }}>{preview.recs.length}</div>
                <div style={{ fontSize: 12.5, color: "var(--fg-2)" }}>receita{preview.recs.length !== 1 ? "s" : ""}</div>
              </div>
            </div>

            {dateRangeStr && (
              <div style={{ padding: "10px 14px", background: "var(--surface-2)", borderRadius: 8, fontSize: 12.5, color: "var(--fg-2)", marginBottom: 14, display: "flex", alignItems: "center", gap: 8 }}>
                <Icon name="calendar" size={13} />
                <span>Período: <strong style={{ color: "var(--fg-1)" }}>{dateRangeStr}</strong></span>
              </div>
            )}

            {preview.total > 0 && (
              <div style={{ marginBottom: 18, border: "1px solid var(--border)", borderRadius: 8, overflow: "hidden" }}>
                <div style={{ padding: "8px 12px", background: "var(--surface-2)", fontSize: 11, fontWeight: 600, color: "var(--fg-3)", textTransform: "uppercase", letterSpacing: 0.5 }}>
                  Amostra (primeiras {Math.min(5, preview.total)} linhas)
                </div>
                <div style={{ maxHeight: 180, overflowY: "auto" }}>
                  {[...preview.txns, ...preview.recs].slice(0, 5).map((t, i) => (
                    <div key={i} style={{ display: "flex", gap: 10, padding: "8px 12px", borderBottom: i < 4 ? "1px solid var(--border)" : "none", fontSize: 12 }}>
                      <span style={{ width: 70, color: "var(--fg-3)", fontVariantNumeric: "tabular-nums" }}>{t.date}</span>
                      <span style={{ flex: 1, color: "var(--fg-1)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{t.desc}</span>
                      <span style={{ color: "var(--fg-3)", fontSize: 10.5 }}>{t.category || "—"}</span>
                      <span style={{ width: 90, textAlign: "right", fontVariantNumeric: "tabular-nums", fontWeight: 500, color: t.value > 0 ? "var(--success)" : "var(--fg-1)" }}>{fmtBRL(t.value)}</span>
                    </div>
                  ))}
                </div>
              </div>
            )}

            <p style={{ fontSize: 12.5, color: "var(--fg-2)", marginBottom: 16 }}>
              Os dados serão <strong>adicionados</strong> sem apagar o que já existe. Você pode editar cada linha depois.
            </p>
            <div style={{ display: "flex", gap: 8, justifyContent: "flex-end" }}>
              <button className="btn-ghost" onClick={() => setStep("upload")}>Voltar</button>
              <button className="btn-primary" onClick={doImport}>
                <Icon name="check" size={13} /> Importar {preview.total} lançamento{preview.total !== 1 ? "s" : ""}
              </button>
            </div>
          </div>
          );
        })()}

        {step === "done" && preview && (
          <div style={{ textAlign: "center", padding: "16px 0" }}>
            <div style={{ width: 60, height: 60, borderRadius: 16, background: "color-mix(in oklab, var(--success) 12%, transparent)", color: "var(--success)", display: "flex", alignItems: "center", justifyContent: "center", margin: "0 auto 16px" }}>
              <Icon name="check" size={30} />
            </div>
            <div style={{ fontSize: 19, fontWeight: 600, color: "var(--fg-1)", marginBottom: 8 }}>Importação concluída!</div>
            <div style={{ fontSize: 13.5, color: "var(--fg-2)", marginBottom: 16 }}>
              {preview.txns.length} despesa{preview.txns.length !== 1 ? "s" : ""} e {preview.recs.length} receita{preview.recs.length !== 1 ? "s" : ""} importada{preview.recs.length !== 1 ? "s" : ""}.
            </div>
            <div style={{ padding: "10px 14px", background: "color-mix(in oklab, var(--accent) 8%, var(--surface-1))", border: "1px solid color-mix(in oklab, var(--accent) 25%, transparent)", borderRadius: 8, fontSize: 12, color: "var(--fg-2)", marginBottom: 20, lineHeight: 1.5 }}>
              O filtro de período foi ajustado pra cobrir todas as datas importadas — você verá os lançamentos imediatamente em Transações.
            </div>
            <button className="btn-primary" onClick={onClose}>Ver lançamentos</button>
          </div>
        )}
      </div>
    </div>
  );
}

// Converte "YYYY-MM-DD" para "DD/MM/YYYY"
function formatBR(d) {
  if (!d) return "";
  const m = String(d).match(/^(\d{4})-(\d{2})-(\d{2})$/);
  return m ? `${m[3]}/${m[2]}/${m[1]}` : String(d);
}

function toUiTxn(t) {
  return {
    id: crypto.randomUUID(),
    date: t.date || new Date().toISOString().slice(0, 10),
    desc: (t.desc || "Importado").trim(),
    value: Number(t.value) || 0,
    status: t.status || "",
    payment: t.payment || "",
    category: (t.category || "").toUpperCase().trim(),
    subcategory: t.subcategory || "",
    who: t.who || "",
  };
}

async function aiExtractRaw(file) {
  if (/\.(xlsx|xls|ods)$/i.test(file.name)) {
    if (!window.XLSX) throw new Error("Biblioteca Excel não carregada — recarregue a página (F5).");
    const buf = await file.arrayBuffer();
    const wb = window.XLSX.read(buf, { cellDates: true, cellNF: true });
    const sheets = {};
    for (const name of wb.SheetNames) {
      const sheet = wb.Sheets[name];
      const mat = window.XLSX.utils.sheet_to_json(sheet, { header: 1, raw: false, defval: "" });
      // Lê todas as linhas (sem cap) — chunking depois garante que tudo cabe na IA
      sheets[name] = mat.filter(r => r.some(c => c !== "" && c != null));
    }
    return { type: "xlsx", filename: file.name, sheets };
  } else {
    const text = await file.text();
    const sep = (text.match(/;/g) || []).length > (text.match(/,/g) || []).length ? ";" : ",";
    const lines = text.split(/\r?\n/).filter(l => l.trim());
    if (!lines.length) throw new Error("Arquivo vazio.");
    const rows = lines.map(l => parseCsvLine(l, sep).map(c => c.replace(/^["']|["']$/g, "").trim()));
    const sheetName = file.name.replace(/\.[^.]+$/, "");
    return { type: "csv", filename: file.name, sheets: { [sheetName]: rows } };
  }
}

// Limpa células de uma linha — remove vazias finais, trunca strings longas
function cleanRow(r) {
  const cleaned = r.map(c => {
    if (c == null) return "";
    const s = String(c).trim();
    return s.length > 200 ? s.slice(0, 200) : s;
  });
  while (cleaned.length && cleaned[cleaned.length - 1] === "") cleaned.pop();
  return cleaned.join(" | ");
}

// Formata header (contexto, marcado [H]) e dados ([L<num>]) separadamente
// pra IA não confundir cabeçalho com transação.
function formatChunkText(headerRows, dataRows, dataStartLine) {
  const hPart = headerRows.map((r, i) => `[H${i + 1}] ${cleanRow(r)}`).join("\n");
  const dPart = dataRows.map((r, i) => `[L${dataStartLine + i}] ${cleanRow(r)}`).join("\n");
  return { hPart, dPart };
}

// Processa cada aba; chunks pequenos pra IA não truncar a saída.
async function aiParseWithClaude(rawData, ws, onProgress) {
  const ROWS_PER_CHUNK = 60;
  const allTransactions = [];

  const sheetNames = Object.keys(rawData.sheets);
  const fileName = rawData.filename || "";

  // Conta total de chunks pra progresso
  let totalChunks = 0;
  for (const name of sheetNames) {
    const rows = rawData.sheets[name] || [];
    if (rows.length === 0) continue;
    // Header = primeiras 3 linhas (NÃO entram em data)
    const dataLen = Math.max(0, rows.length - 3);
    totalChunks += Math.max(1, Math.ceil(dataLen / ROWS_PER_CHUNK));
  }

  let chunkCounter = 0;
  for (const sheetName of sheetNames) {
    const rows = rawData.sheets[sheetName] || [];
    if (rows.length === 0) continue;

    // Header = primeiras 3 linhas (sempre passadas como contexto, NUNCA viram transação)
    // Data = resto da planilha (dividido em chunks)
    const headerRows = rows.slice(0, Math.min(3, rows.length));
    const dataRows = rows.slice(headerRows.length);

    // Se a aba é tão pequena que não tem data além do header, processa o header como dado
    let dataChunks;
    let dataLineOffsets;
    if (dataRows.length === 0) {
      dataChunks = [headerRows];
      dataLineOffsets = [1];
    } else {
      dataChunks = [];
      dataLineOffsets = [];
      for (let i = 0; i < dataRows.length; i += ROWS_PER_CHUNK) {
        dataChunks.push(dataRows.slice(i, i + ROWS_PER_CHUNK));
        dataLineOffsets.push(headerRows.length + i + 1); // 1-indexed line number
      }
    }

    for (let i = 0; i < dataChunks.length; i++) {
      chunkCounter++;
      const label = `Aba "${sheetName}"${dataChunks.length > 1 ? ` (lote ${i + 1}/${dataChunks.length})` : ""} — ${chunkCounter}/${totalChunks}`;
      onProgress?.(`Analisando ${label}…`);
      // Passa header (contexto) + chunk de data (a processar) separadamente
      const headerForChunk = (dataRows.length === 0) ? [] : headerRows;
      const result = await aiProcessChunk(sheetName, headerForChunk, dataChunks[i], dataLineOffsets[i], ws, fileName);
      console.log(`[IA] ${label}: ${dataChunks[i].length} linhas de DADOS enviadas → ${result.transactions.length} transações extraídas`);
      allTransactions.push(...result.transactions);
    }
  }

  console.log(`[IA] TOTAL: ${allTransactions.length} transações extraídas`);
  return { transactions: allTransactions };
}

async function aiProcessChunk(sheetName, headerRows, dataRows, dataStartLine, ws, fileName = "") {
  const { hPart, dPart } = formatChunkText(headerRows, dataRows, dataStartLine);

  const accountTypeLabel = ws.type === "personal" ? "Pessoal (orçamento doméstico)" : "Empresarial (negócio)";

  // Inferência de tipo padrão pelo NOME do arquivo + nome da aba.
  // Se filename contém "vendas", "receitas", "faturamento", "entradas", "recebimentos"
  // → tudo é INCOME por default (a menos que a linha tenha indicador explícito de despesa)
  // Se contém "despesas", "saídas", "pagamentos", "contas a pagar" → tudo é EXPENSE
  const nameContext = `${fileName} ${sheetName}`.toLowerCase();
  const incomePatterns = /\b(vendas?|receitas?|faturamento|entradas?|recebimentos?|recebido|comiss[ãa]o|honor[áa]rios?|aluguel[\s_-]?recebido|cobran[çc]as?|cobrar)\b/i;
  const expensePatterns = /\b(despesas?|sa[íi]das?|pagamentos?|pagar|contas[\s_-]?a[\s_-]?pagar|custos?|gastos?|fornecedores?|compras?|folha)\b/i;
  let defaultTypeHint = "AMBÍGUO (use regras de detecção por descrição)";
  if (incomePatterns.test(nameContext) && !expensePatterns.test(nameContext)) {
    defaultTypeHint = "INCOME — o nome do arquivo/aba indica claramente RECEITAS/VENDAS. Por padrão, TODAS as linhas são income, a não ser que a linha tenha indicador explícito (coluna tipo='Despesa', valor negativo claramente marcado como saída, ou descrição inequívoca de despesa como 'Salário Funcionária').";
  } else if (expensePatterns.test(nameContext) && !incomePatterns.test(nameContext)) {
    defaultTypeHint = "EXPENSE — o nome do arquivo/aba indica claramente DESPESAS/SAÍDAS. Por padrão, TODAS as linhas são expense, a não ser que a linha tenha indicador explícito de receita.";
  }

  const userMsg = `ARQUIVO: "${fileName || "(sem nome)"}"
ABA: "${sheetName}"
TIPO PADRÃO INFERIDO PELO NOME: ${defaultTypeHint}
TIPO DE CONTA: ${accountTypeLabel}
CLASSIFICAÇÕES DE DESPESA DISPONÍVEIS (use APENAS estas; OUTROS se nada encaixar): ${(ws.categories || []).join(", ") || "(nenhuma)"}
CLASSIFICAÇÕES DE RECEITA DISPONÍVEIS (use APENAS estas; OUTRAS RECEITAS se nada encaixar): ${(ws.incomeCategories || []).join(", ") || "(nenhuma)"}
FORMAS DE PAGAMENTO EXISTENTES: ${(ws.payments || []).join(", ") || "(nenhuma)"}

═══ CABEÇALHO (apenas contexto — NÃO emita transação para estas linhas) ═══
${hPart || "(sem cabeçalho)"}

═══ LINHAS DE DADOS (extraia UMA transação para cada linha com data+valor) ═══
${dPart}

INSTRUÇÃO FINAL: Emita transações APENAS para linhas marcadas com [L*]. NUNCA emita transação para linhas marcadas com [H*] — essas são só pra você entender as colunas.`;

  const systemMsg = `Você é um extrator EXAUSTIVO e CONSISTENTE de dados financeiros de planilhas brasileiras.

REGRA #1 — APENAS LINHAS [L*] VIRAM TRANSAÇÃO:
Linhas [H*] são CABEÇALHO (só pra você entender as colunas). NUNCA emita transação pra elas.
Linhas [L*] são DADOS. UMA transação por linha [L*] que tenha data + valor.

REGRA #2 — COMPLETUDE:
Se há 60 linhas [L*] com data+valor, retorne 60 transações. Não pule, não agregue, não resuma.

REGRA #3 — TIPO (expense vs income) — MUITO IMPORTANTE:
Aplique estas regras NESTA ORDEM (a primeira que aplicar manda):

0. ⚠️ NOME DO ARQUIVO/ABA (PRIMEIRA PRIORIDADE — quando definido como INCOME ou EXPENSE no userMsg "TIPO PADRÃO INFERIDO PELO NOME"):
   Se o nome do arquivo OU da aba diz claramente "Vendas", "Receitas", "Faturamento", "Entradas", "Recebimentos" → TODAS as linhas são INCOME por default.
   Se diz "Despesas", "Saídas", "Pagamentos", "Contas a Pagar", "Custos", "Gastos" → TODAS as linhas são EXPENSE por default.
   Esse default SÓ pode ser quebrado por indicador EXPLÍCITO numa linha individual (ex: coluna tipo com valor contrário, ou descrição inequívoca de tipo contrário).
   EXEMPLO: arquivo chamado "Vendas Janeiro 2026.xlsx" → cada linha vira INCOME. Não importa que tenha desc "Salário Funcionária" — se o arquivo é de vendas, é INCOME.

1. Se a planilha tem coluna de tipo explícita ("Despesa"/"Receita", "Saída"/"Entrada", "D"/"C", "Débito"/"Crédito"), USE (sobre o default por nome).

2. Senão (TIPO PADRÃO é AMBÍGUO), INFERIR pela DESCRIÇÃO seguindo a tabela:

DESPESAS (saída de caixa) — SEMPRE expense, mesmo com valor positivo:
- Salário, Pró-labore, Folha, FGTS, INSS, 13º, Férias, Vale-transporte, Vale-refeição
- DARF, DAS, IRPF, IRPJ, ISS, ICMS, PIS, COFINS, "Imposto", "Tributo", "Taxa"
- Aluguel (quando é a empresa quem paga), Condomínio, IPTU, Conta de luz/água/internet/telefone
- Contador, Contabilidade, Honorário (que paga), Advogado (que paga)
- Fornecedor, Compra, Material, Mercadoria
- Cartão de crédito (a fatura), Empréstimo (parcela), Financiamento
- Combustível, Posto, Uber/99 (que paga), Estacionamento, Pedágio
- Restaurante, iFood, Mercado, Padaria
- Netflix, Spotify, Software, Assinatura

RECEITAS (entrada de caixa):
- "Cliente X pagou", "Recebimento de", "Venda", "Faturamento", "Receita"
- Aluguel RECEBIDO (quando a empresa é locadora) — só se houver indicação clara
- "Comissão recebida", "Juros recebidos"
- "Pix recebido de" (não confundir com "Pix" como forma de pagamento)

3. Em caso de empate, default: expense.

EXEMPLOS DA SUA PLANILHA:
- "Salário Funcionária" → SEMPRE expense (a empresa PAGA o salário)
- "FGTS" → expense
- "DARF" → expense
- "Aluguel" (sem outro contexto) → expense (a empresa paga)
- Mas "Aluguel — Cliente Maria" → income (locadora recebendo)

REGRA #4 — DETECTE COLUNAS PELO HEADER:
Use as linhas [H*] pra identificar qual coluna é data, descrição, valor, tipo, status, etc. Aplique consistentemente em todas as [L*].

REGRA #5 — SUBCATEGORIA SEMÂNTICA:
"subcategory" = nome CURTO e NORMALIZADO que agrupa lançamentos similares (pra somar em relatórios).

Exemplos:
- "UBER 15/01" → "Uber"
- "99 POP" → "99"
- "OUTBACK CURITIBA" / "REST. ABC" → "Restaurante"
- "IFOOD" → "iFood"
- "POSTO SHELL" / "POSTO IPIRANGA" → "Combustível"
- "ALUGUEL APTO JANEIRO" → "Aluguel"
- "NETFLIX" → "Netflix"
- "PÃO DE AÇÚCAR" / "CARREFOUR" → "Mercado"
- "SALÁRIO FUNCIONÁRIA" → "Salário Funcionária"
- "FGTS" → "FGTS"

Regras:
- Curto (1-3 palavras), primeira letra maiúscula
- CONSISTENTE: mesmo nome pra lançamentos similares na mesma planilha
- Não copie a desc bruta — normalize

REGRA #6 — CATEGORIA (use APENAS as da lista no userMsg, NUNCA invente):
Escolha a classificação existente que MAIS se aproxima de cada lançamento.
Mapeamento por contexto (use SÓ se a classificação correspondente existir na lista):
- Uber, 99 (corridas/Tecnologia), Cabify, posto, combustível, estacionamento, pedágio, ônibus, metrô → TRANSPORTE
- iFood, Rappi, 99 Food, restaurante, lanche, mercado, supermercado, padaria → ALIMENTAÇÃO
- Netflix, Spotify, Disney+, HBO, Apple Music, YouTube Premium → ASSINATURAS
- Cinema, jogos, viagens, eventos, shows → LAZER
- Drogasil, Pague Menos, Raia, farmácia, hospital, clínica, laboratório, plano de saúde → SAÚDE
- Escola, faculdade, cursos online, livros didáticos → EDUCAÇÃO
- TIM/VIVO/CLARO/OI (telefonia), CEMIG/ENEL/CPFL (energia), SABESP (água), aluguel, condomínio, IPTU → MORADIA
- Google Ads, Meta Ads, anúncios pagos → MARKETING (empresa)
- Adobe, Microsoft 365, AWS, Notion, software, hospedagem → TECNOLOGIA (empresa)
- Salário pago, FGTS, INSS, pró-labore → FOLHA (empresa)
- DARF, ICMS, ISS, IPTU empresarial, taxas, tributos → TRIBUTOS (empresa)
- Contador, contas fixas mensais → CUSTOS FIXOS (empresa)
- Compras, fornecedores, materiais pontuais → CUSTOS VARIÁVEIS (empresa)
- Quando NADA encaixa → "OUTROS" (despesa) ou "OUTRAS RECEITAS" (receita)
A subcategoria é livre — ela diferencia os lançamentos dentro da classificação.

PARSING:
- Data: YYYY-MM-DD. Se só "15" ou "15/06", use nome da aba pra completar.
- Valor: número limpo (1500.00, não "R$ 1.500,00").
- Status:
  * Despesa: PAGO / PENDENTE / AGENDADO
  * Receita: RECEBIDO / A RECEBER
  * Default: data passou → PAGO/RECEBIDO; senão PENDENTE/A RECEBER.

IGNORAR:
- Linhas [L*] totalmente vazias
- Totalizadores ("TOTAL", "SUBTOTAL", "SOMA", "Σ")
- Cabeçalhos repetidos no meio dos dados

OUTPUT obrigatório (JSON estrito, sem markdown):
{
  "transactions": [
    {"type":"expense","date":"YYYY-MM-DD","desc":"...","value":0,"status":"...","category":"...","payment":"","subcategory":"...","who":""}
  ]
}`;

  const text = await window.db.callAi({
    system: systemMsg,
    user: userMsg,
    maxTokens: 8000,
  });

  const fenced = text.match(/```(?:json)?\s*([\s\S]+?)\s*```/);
  const jsonStr = fenced ? fenced[1] : (text.match(/\{[\s\S]+\}/)?.[0] || text);
  let parsed;
  try { parsed = JSON.parse(jsonStr); }
  catch (e) {
    // Se chegou truncado, tenta recuperar o array de transações até onde deu
    const partial = text.match(/"transactions"\s*:\s*\[([\s\S]*)/);
    if (partial) {
      try {
        const tail = partial[1].replace(/,\s*\{[^{}]*$/, "").replace(/,\s*$/, "");
        const recovered = JSON.parse("[" + tail + "]");
        console.warn(`[IA] Recovery: aba "${sheetName}" — parse parcial recuperou ${recovered.length} transações`);
        return { transactions: recovered };
      } catch (e2) { /* falhou recovery */ }
    }
    console.error(`[IA] Aba "${sheetName}" — resposta inválida:`, text.slice(0, 500));
    throw new Error(`IA retornou resposta inválida na aba "${sheetName}". Veja o console.`);
  }
  if (!Array.isArray(parsed.transactions)) throw new Error(`Aba "${sheetName}" não devolveu transações.`);
  return { transactions: parsed.transactions };
}

function IconPicker({ selected, onSelect, onClose, title = "Escolher símbolo" }) {
  React.useEffect(() => {
    const onKey = (e) => { if (e.key === "Escape") onClose(); };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [onClose]);

  return (
    <div className="modal-backdrop" onClick={onClose}>
      <div className="modal" onClick={e => e.stopPropagation()} style={{ width: 460, maxWidth: "calc(100vw - 32px)", padding: 22 }}>
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 16 }}>
          <h3 style={{ margin: 0, fontSize: 15, fontWeight: 600 }}>{title}</h3>
          <button className="icon-btn" onClick={onClose}><Icon name="x" size={16} /></button>
        </div>
        <div style={{ display: "grid", gridTemplateColumns: "repeat(6, 1fr)", gap: 6, maxHeight: 420, overflowY: "auto" }}>
          {BUSINESS_ICONS.map(({ key, label }) => (
            <button
              key={key}
              onClick={() => { onSelect(key); onClose(); }}
              title={label}
              style={{
                display: "flex", flexDirection: "column", alignItems: "center", gap: 5,
                padding: "10px 4px", borderRadius: 10,
                border: selected === key ? "2px solid var(--accent)" : "1px solid var(--border)",
                background: selected === key ? "color-mix(in oklab, var(--accent) 10%, var(--surface-1))" : "var(--surface-1)",
                cursor: "pointer", color: selected === key ? "var(--accent)" : "var(--fg-2)",
                transition: "all .12s", fontFamily: "inherit",
              }}
            >
              <Icon name={key} size={20} />
              <span style={{ fontSize: 9, textAlign: "center", lineHeight: 1.2, color: "var(--fg-3)", overflow: "hidden", whiteSpace: "nowrap", width: "100%", textOverflow: "ellipsis" }}>
                {label}
              </span>
            </button>
          ))}
        </div>
      </div>
    </div>
  );
}

function Settings({ ws, setWs, activeWs, setWorkspaces, accent, setRangeMode, setCustomRange, plan, setPlan, showUpgrade, userId }) {
  const [iconPickerOpen, setIconPickerOpen] = React.useState(false);
  const [catIconTarget, setCatIconTarget] = React.useState(null);
  const [importOpen, setImportOpen] = React.useState(false);

  const updateField = (field, value) => setWs(prev => ({ ...prev, [field]: value }));

  // --- Helpers logo empresa ---
  const uploadLogo    = () => pickImage(img => updateField("logo", img));
  const removeLogo    = () => updateField("logo", null);

  // --- Helpers foto/ícone de categoria ---
  const catIcon       = (c) => (ws.categoryIcons  || {})[c] || "tag";
  const catImg        = (c) => (ws.categoryImages || {})[c] || null;
  const setCatIcon    = (c, icon) => updateField("categoryIcons",  { ...(ws.categoryIcons  || {}), [c]: icon });
  const uploadCatImg  = (c) => pickImage(img => updateField("categoryImages", { ...(ws.categoryImages || {}), [c]: img }));
  const removeCatImg  = (c) => {
    const imgs = { ...(ws.categoryImages || {}) };
    delete imgs[c];
    updateField("categoryImages", imgs);
  };

  const addCategory = () => {
    const name = prompt("Nome da categoria de despesa:");
    if (name) updateField("categories", [...(ws.categories || []), name.toUpperCase()]);
  };
  const removeCategory = (cat) => {
    if (!confirm(`Remover "${cat}"?`)) return;
    setWs(prev => {
      const icons = { ...(prev.categoryIcons  || {}) };
      const imgs  = { ...(prev.categoryImages || {}) };
      delete icons[cat]; delete imgs[cat];
      return { ...prev, categories: prev.categories.filter(c => c !== cat), categoryIcons: icons, categoryImages: imgs };
    });
  };

  const addIncomeCategory = () => {
    const name = prompt("Nome da categoria de receita:");
    if (name) updateField("incomeCategories", [...(ws.incomeCategories || []), name.toUpperCase()]);
  };
  const removeIncomeCategory = (cat) => {
    if (!confirm(`Remover "${cat}"?`)) return;
    updateField("incomeCategories", (ws.incomeCategories || []).filter(c => c !== cat));
  };

  // Reseta classificações para os padrões da conta e reatribui órfãos pra OUTROS / OUTRAS RECEITAS.
  // Limpa lixo deixado por importações antigas (antes do bloqueio de auto-criação).
  const restoreDefaultCategories = (kind) => {
    const isBusiness = ws.type !== "personal";
    const defExp = isBusiness
      ? ["CUSTOS FIXOS", "CUSTOS VARIÁVEIS", "FOLHA", "TRIBUTOS", "TRANSPORTE", "MARKETING", "TECNOLOGIA", "OUTROS"]
      : ["MORADIA", "ALIMENTAÇÃO", "TRANSPORTE", "SAÚDE", "EDUCAÇÃO", "LAZER", "ASSINATURAS", "OUTROS"];
    const defInc = isBusiness
      ? ["VENDAS", "SERVIÇOS", "ALUGUÉIS", "RENDIMENTOS", "OUTROS"]
      : ["SALÁRIO", "FREELANCE", "RENDIMENTOS", "PRESENTE", "OUTROS"];
    const target = kind === "income" ? defInc : defExp;
    const label = kind === "income" ? "receita" : "despesa";
    const fallback = kind === "income" ? "OUTRAS RECEITAS" : "OUTROS";
    if (!confirm(`Restaurar classificações padrão de ${label}?\n\nAtuais serão substituídas por:\n${target.join(", ")}\n\nLançamentos com classificação removida serão marcados como "${fallback}".`)) return;
    setWs(prev => {
      const validSet = new Set(target);
      if (kind === "income") {
        const incFallback = validSet.has("OUTRAS RECEITAS") ? "OUTRAS RECEITAS" : (validSet.has("OUTROS") ? "OUTROS" : target[0]);
        if (!validSet.has(incFallback)) target.push(incFallback);
        return {
          ...prev,
          incomeCategories: target,
          receitas: (prev.receitas || []).map(r => validSet.has((r.category || "").toUpperCase()) ? r : { ...r, category: incFallback }),
        };
      }
      const expFallback = "OUTROS";
      const filteredIcons = Object.fromEntries(Object.entries(prev.categoryIcons || {}).filter(([k]) => validSet.has(k)));
      const filteredImgs  = Object.fromEntries(Object.entries(prev.categoryImages || {}).filter(([k]) => validSet.has(k)));
      return {
        ...prev,
        categories: target,
        categoryIcons: filteredIcons,
        categoryImages: filteredImgs,
        transactions: (prev.transactions || []).map(t => validSet.has((t.category || "").toUpperCase()) ? t : { ...t, category: expFallback }),
      };
    });
  };

  const addPayment = () => {
    const name = prompt("Forma de pagamento:");
    if (name) updateField("payments", [...(ws.payments || []), name]);
  };
  const removePayment = (p) => {
    if (!confirm(`Remover "${p}"?`)) return;
    updateField("payments", ws.payments.filter(x => x !== p));
  };

  const wsIconLabel = BUSINESS_ICONS.find(i => i.key === ws.icon)?.label || ws.icon;
  const accentColor = ws.accent || accent;

  // ===== Sidebar interna de seções =====
  const SECTIONS = [
    // CONTA
    { id: "identificacao", label: "Identificação",          group: "Conta", icon: "person" },
    { id: "meta",          label: "Meta mensal",            group: "Conta", icon: "target" },
    { id: "categorias",    label: "Classificações",         group: "Conta", icon: "tag" },
    { id: "pagamentos",    label: "Formas de pagamento",    group: "Conta", icon: "wallet" },
    { id: "dados",         label: "Importar / Exportar",    group: "Conta", icon: "download" },
    // SEGURANÇA
    { id: "lixeira",       label: "Lixeira",                group: "Segurança", icon: "trash" },
    { id: "backup",        label: "Backup",                 group: "Segurança", icon: "download" },
    // INTEGRAÇÕES
    { id: "ia",            label: "Inteligência Artificial", group: "Integrações", icon: "spark" },
    { id: "mobile",        label: "Chat móvel (QR code)",   group: "Integrações", icon: "mic" },
    // ASSINATURA
    { id: "plano",         label: "Plano e cobrança",       group: "Assinatura", icon: "spark" },
  ];

  // URL hash sync
  const [section, setSection] = React.useState(() => {
    try {
      const hash = (window.location.hash || "").replace("#", "");
      return SECTIONS.find(s => s.id === hash)?.id || "identificacao";
    } catch (e) { return "identificacao"; }
  });
  React.useEffect(() => {
    try { if (window.location.hash !== "#" + section) window.location.hash = section; } catch (e) {}
  }, [section]);

  // Agrupa pra renderizar no sidebar
  const groups = SECTIONS.reduce((acc, s) => {
    if (!acc[s.group]) acc[s.group] = [];
    acc[s.group].push(s);
    return acc;
  }, {});
  const groupOrder = ["Conta", "Segurança", "Integrações", "Assinatura"];

  // Lookup atual pra dropdown mobile
  const currentSection = SECTIONS.find(s => s.id === section);

  return (
    <div className="settings-shell" style={{ display: "grid", gridTemplateColumns: "220px 1fr", gap: 28, maxWidth: 1100, alignItems: "start" }}>
      <style>{`
        /* Mobile: esconde sidebar interna (vira <select> dropdown logo abaixo) */
        @media (max-width: 900px) {
          .settings-shell { grid-template-columns: 1fr !important; gap: 12px !important; }
          .settings-shell aside.settings-side-nav { display: none !important; }
          .settings-mobile-picker { display: block !important; }
        }
      `}</style>

      {/* DROPDOWN MOBILE — substitui sidebar interna */}
      <div className="settings-mobile-picker" style={{ display: "none", marginBottom: 4 }}>
        <label style={{ display: "block", fontSize: 11, color: "var(--fg-3)", textTransform: "uppercase", letterSpacing: 0.5, fontWeight: 500, marginBottom: 6, padding: "0 4px" }}>
          Seção
        </label>
        <select
          value={section}
          onChange={e => setSection(e.target.value)}
          style={{
            width: "100%",
            padding: "12px 14px",
            border: "1px solid var(--border)",
            borderRadius: 12,
            background: "var(--surface-1)",
            color: "var(--fg-1)",
            fontSize: 15,
            fontFamily: "inherit",
            fontWeight: 600,
            cursor: "pointer",
            appearance: "none",
            WebkitAppearance: "none",
            backgroundImage: "url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"%237a9686\" stroke-width=\"2\" stroke-linecap=\"round\"><polyline points=\"6 9 12 15 18 9\"/></svg>')",
            backgroundRepeat: "no-repeat",
            backgroundPosition: "right 14px center",
            paddingRight: 38,
          }}
        >
          {groupOrder.map(g => (
            <optgroup key={g} label={g}>
              {(groups[g] || []).map(s => (
                <option key={s.id} value={s.id}>{s.label}</option>
              ))}
            </optgroup>
          ))}
        </select>
      </div>

      {/* SIDEBAR INTERNA — escondida no mobile */}
      <aside className="settings-side-nav" style={{ position: "sticky", top: 24, display: "flex", flexDirection: "column", gap: 18, padding: "4px 0" }}>
        {groupOrder.map(g => (
          <div key={g}>
            <div className="eyebrow" style={{ padding: "0 12px 8px", fontSize: 10 }}>{g}</div>
            <div style={{ display: "flex", flexDirection: "column", gap: 2 }}>
              {(groups[g] || []).map(s => {
                const active = section === s.id;
                return (
                  <button
                    key={s.id}
                    onClick={() => setSection(s.id)}
                    style={{
                      display: "flex", alignItems: "center", gap: 10,
                      padding: "8px 12px", borderRadius: 10,
                      border: "none", cursor: "pointer", fontFamily: "inherit",
                      fontSize: 13, textAlign: "left",
                      background: active ? "color-mix(in oklab, var(--green) 10%, transparent)" : "transparent",
                      color: active ? "var(--fg-1)" : "var(--fg-2)",
                      fontWeight: active ? 600 : 400,
                      transition: "background .15s ease, color .15s ease",
                    }}
                    onMouseEnter={e => { if (!active) e.currentTarget.style.background = "var(--surface-2)"; }}
                    onMouseLeave={e => { if (!active) e.currentTarget.style.background = "transparent"; }}
                  >
                    <Icon name={s.icon} size={13} />
                    <span>{s.label}</span>
                    {active && <span style={{ marginLeft: "auto", color: "var(--green)" }}>›</span>}
                  </button>
                );
              })}
            </div>
          </div>
        ))}
      </aside>

      {/* CONTEÚDO DA SEÇÃO ATIVA */}
      <div style={{ display: "flex", flexDirection: "column", gap: 14, maxWidth: 800, minWidth: 0 }}>

      {/* === IDENTIFICAÇÃO === */}
      {section === "identificacao" && (
      <div className="card" style={{ padding: 22 }}>
        <h3 style={{ margin: 0, marginBottom: 4, fontSize: 15, fontWeight: 600 }}>Identificação</h3>
        <p style={{ margin: 0, marginBottom: 18, fontSize: 12.5, color: "var(--fg-3)" }}>Como esta conta aparece na barra lateral.</p>

        {/* Logo / Ícone */}
        <div style={{ marginBottom: 20 }}>
          <div style={{ fontSize: 11.5, color: "var(--fg-3)", textTransform: "uppercase", letterSpacing: 0.5, fontWeight: 500, marginBottom: 10 }}>
            Foto ou símbolo da empresa
          </div>
          <div className="logo-picker-wrap" style={{ display: "flex", alignItems: "center", gap: 16, flexWrap: "wrap" }}>
            {/* Preview */}
            <div style={{ width: 64, height: 64, borderRadius: 16, overflow: "hidden", flexShrink: 0, background: `color-mix(in oklab, ${accentColor} 15%, transparent)`, color: accentColor, display: "flex", alignItems: "center", justifyContent: "center", border: `1.5px solid color-mix(in oklab, ${accentColor} 30%, transparent)` }}>
              {ws.logo
                ? <img src={ws.logo} style={{ width: "100%", height: "100%", objectFit: "cover" }} />
                : <Icon name={ws.icon || "briefcase"} size={28} />
              }
            </div>

            {/* Ações */}
            <div className="logo-actions" style={{ flex: 1, minWidth: 0, display: "flex", flexDirection: "column", gap: 8 }}>
              <div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
                <button className="btn-ghost" onClick={uploadLogo} style={{ flex: "1 1 130px", justifyContent: "center" }}>
                  <Icon name="camera" size={13} />
                  {ws.logo ? "Trocar foto" : "Enviar foto"}
                </button>
                <button className="btn-ghost" onClick={() => setIconPickerOpen(true)} style={{ flex: "1 1 130px", justifyContent: "center" }}>
                  <Icon name="spark" size={13} /> Símbolo pronto
                </button>
              </div>
              {ws.logo && (
                <button onClick={removeLogo} style={{ display: "inline-flex", alignItems: "center", justifyContent: "center", gap: 5, fontSize: 12, color: "var(--danger)", background: "color-mix(in oklab, var(--danger) 8%, transparent)", border: "1px solid color-mix(in oklab, var(--danger) 25%, transparent)", borderRadius: 7, padding: "7px 12px", cursor: "pointer", fontFamily: "inherit", minHeight: 36 }}>
                  <Icon name="trash" size={12} /> Remover foto
                </button>
              )}
              {!ws.logo && (
                <div style={{ fontSize: 11.5, color: "var(--fg-3)" }}>Símbolo atual: <strong>{wsIconLabel}</strong></div>
              )}
            </div>
          </div>
        </div>

        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }}>
          <Field label="Apelido curto">
            <input className="input" value={ws.name} onChange={e => updateField("name", e.target.value)} />
          </Field>
          <Field label="Nome completo">
            <input className="input" value={ws.fullName} onChange={e => updateField("fullName", e.target.value)} />
          </Field>
        </div>

        <div style={{ marginTop: 14 }}>
          <Field label="Cor de destaque">
            <div className="color-picker-wrap" style={{ display: "flex", flexDirection: "column", gap: 10 }}>
              {/* Linha 1: picker visual + hex input */}
              <div style={{ display: "flex", gap: 8, alignItems: "center" }}>
                <input type="color" value={ws.accent} onChange={e => updateField("accent", e.target.value)}
                  style={{ width: 44, height: 44, border: "1px solid var(--border)", borderRadius: 10, cursor: "pointer", background: "transparent", flexShrink: 0, padding: 2 }} />
                <input className="input" value={ws.accent} onChange={e => updateField("accent", e.target.value)}
                  style={{ flex: 1, minWidth: 0, fontFamily: "ui-monospace, monospace" }} />
              </div>
              {/* Linha 2: 6 swatches em grid responsivo (wrap natural) */}
              <div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
                {["#16a34a", "#b45309", "#7c3aed", "#0ea5e9", "#dc2626", "#0891b2"].map(c => (
                  <button key={c} onClick={() => updateField("accent", c)} style={{ width: 36, height: 36, borderRadius: 10, background: c, border: ws.accent === c ? "2px solid var(--fg-1)" : "1px solid var(--border)", cursor: "pointer", flexShrink: 0 }} />
                ))}
              </div>
            </div>
          </Field>
        </div>
      </div>
      )}

      {/* === META MENSAL === */}
      {section === "meta" && (
      <div className="card" style={{ padding: 22 }}>
        <h3 style={{ margin: 0, marginBottom: 4, fontSize: 15, fontWeight: 600 }}>Meta de gasto mensal</h3>
        <p style={{ margin: 0, marginBottom: 16, fontSize: 12.5, color: "var(--fg-3)" }}>Limite usado para a barra de progresso no Dashboard.</p>
        <div style={{ display: "flex", gap: 12, alignItems: "center" }}>
          <input type="number" step="100" className="input" value={ws.monthlyGoal}
            onChange={e => updateField("monthlyGoal", parseFloat(e.target.value) || 0)}
            style={{ flex: 1, fontVariantNumeric: "tabular-nums", fontSize: 16, fontWeight: 500 }} />
          <span style={{ color: "var(--fg-3)", fontSize: 13 }}>R$ / mês</span>
        </div>
        <input type="range" min="500" max="50000" step="500" value={ws.monthlyGoal}
          onChange={e => updateField("monthlyGoal", parseFloat(e.target.value))}
          style={{ width: "100%", marginTop: 12, accentColor: accent }} />
      </div>
      )}

      {/* === CLASSIFICAÇÕES (despesas + receitas no mesmo painel) === */}
      {section === "categorias" && (<>
      <div className="card" style={{ padding: 22 }}>
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start", marginBottom: 4 }}>
          <h3 style={{ margin: 0, fontSize: 15, fontWeight: 600 }}>Classificações de despesas</h3>
          <div style={{ display: "flex", gap: 6 }}>
            <button className="btn-ghost" onClick={() => restoreDefaultCategories("expense")} title="Volta para a lista padrão e marca lançamentos órfãos como OUTROS">
              <Icon name="trash" size={13} /> Restaurar padrões
            </button>
            <button className="btn-ghost" onClick={addCategory}><Icon name="plus" size={13} /> Adicionar</button>
          </div>
        </div>
        <p style={{ margin: 0, marginBottom: 16, fontSize: 12.5, color: "var(--fg-3)" }}>
          Aparecem no dropdown ao lançar uma <strong>saída</strong>. Clique no ícone para trocar.
        </p>
        <div style={{ display: "flex", flexWrap: "wrap", gap: 8 }}>
          {(ws.categories || []).map(c => (
            <div key={c} className="cat-chip" style={{ display: "inline-flex", alignItems: "center", gap: 5, padding: "3px 6px 3px 5px" }}>
              {/* Ícone ou foto */}
              {catImg(c)
                ? (
                  <img
                    src={catImg(c)}
                    onClick={() => removeCatImg(c)}
                    title="Clique para remover foto"
                    style={{ width: 16, height: 16, borderRadius: 4, objectFit: "cover", cursor: "pointer", flexShrink: 0 }}
                  />
                ) : (
                  <button
                    onClick={() => setCatIconTarget(c)}
                    title="Mudar símbolo"
                    style={{ display: "flex", alignItems: "center", justifyContent: "center", background: "none", border: "none", cursor: "pointer", color: accentColor, padding: 0, flexShrink: 0 }}
                  >
                    <Icon name={catIcon(c)} size={12} />
                  </button>
                )
              }
              <span style={{ fontSize: 11, fontWeight: 500 }}>{c}</span>
              <button className="icon-btn" style={{ width: 14, height: 14, padding: 0 }} onClick={() => removeCategory(c)}>
                <Icon name="x" size={10} />
              </button>
            </div>
          ))}
        </div>
      </div>

      {/* Classificações de RECEITAS */}
      <div className="card" style={{ padding: 22 }}>
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start", marginBottom: 4 }}>
          <h3 style={{ margin: 0, fontSize: 15, fontWeight: 600 }}>Classificações de receitas</h3>
          <div style={{ display: "flex", gap: 6 }}>
            <button className="btn-ghost" onClick={() => restoreDefaultCategories("income")} title="Volta para a lista padrão e marca lançamentos órfãos como OUTRAS RECEITAS">
              <Icon name="trash" size={13} /> Restaurar padrões
            </button>
            <button className="btn-ghost" onClick={addIncomeCategory}><Icon name="plus" size={13} /> Adicionar</button>
          </div>
        </div>
        <p style={{ margin: 0, marginBottom: 16, fontSize: 12.5, color: "var(--fg-3)" }}>
          Aparecem no dropdown ao lançar uma <strong>entrada</strong>. Lista separada das despesas.
        </p>
        <div style={{ display: "flex", flexWrap: "wrap", gap: 8 }}>
          {(ws.incomeCategories || []).map(c => (
            <div key={c} className="cat-chip" style={{ display: "inline-flex", alignItems: "center", gap: 5, padding: "3px 6px 3px 9px" }}>
              <span style={{ fontSize: 11, fontWeight: 500 }}>{c}</span>
              <button className="icon-btn" style={{ width: 14, height: 14, padding: 0 }} onClick={() => removeIncomeCategory(c)}>
                <Icon name="x" size={10} />
              </button>
            </div>
          ))}
          {(ws.incomeCategories || []).length === 0 && (
            <span style={{ fontSize: 11.5, color: "var(--fg-3)" }}>Nenhuma classificação cadastrada — adicione com o botão acima.</span>
          )}
        </div>
      </div>
      </>)}

      {/* === PAGAMENTOS === */}
      {section === "pagamentos" && (
      <div className="card" style={{ padding: 22 }}>
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start", marginBottom: 4 }}>
          <h3 style={{ margin: 0, fontSize: 15, fontWeight: 600 }}>Formas de pagamento</h3>
          <button className="btn-ghost" onClick={addPayment}><Icon name="plus" size={13} /> Adicionar</button>
        </div>
        <p style={{ margin: 0, marginBottom: 16, fontSize: 12.5, color: "var(--fg-3)" }}>Métodos disponíveis ao registrar uma transação.</p>
        <div style={{ display: "flex", flexWrap: "wrap", gap: 8 }}>
          {(ws.payments || []).map(p => (
            <div key={p} style={{ display: "flex", alignItems: "center", gap: 6, padding: "6px 10px", border: "1px solid var(--border)", borderRadius: 999, background: "var(--surface-1)", fontSize: 12.5 }}>
              {p}
              <button className="icon-btn" style={{ width: 18, height: 18, padding: 0 }} onClick={() => removePayment(p)}>
                <Icon name="x" size={11} />
              </button>
            </div>
          ))}
        </div>
      </div>
      )}

      {/* === DADOS (Importar/Exportar) === */}
      {section === "dados" && (
      <div className="card" style={{ padding: 22 }}>
        <h3 style={{ margin: 0, marginBottom: 4, fontSize: 15, fontWeight: 600 }}>Importar / Exportar</h3>
        <p style={{ margin: 0, marginBottom: 16, fontSize: 12.5, color: "var(--fg-3)" }}>Baixe todos os lançamentos em CSV ou importe dados de outra planilha — a IA detecta tudo automaticamente.</p>
        <div style={{ display: "flex", gap: 8 }}>
          <button className="btn-ghost" onClick={() => exportCSV(ws)}>
            <Icon name="download" size={13} /> Exportar CSV
          </button>
          <button className="btn-ghost" onClick={() => setImportOpen(true)}>
            <Icon name="spark" size={13} /> Importar planilha
          </button>
        </div>
        <p style={{ marginTop: 12, marginBottom: 0, fontSize: 11.5, color: "var(--fg-3)" }}>
          A importação usa IA — configure sua chave em <a href="#ia" onClick={e => { e.preventDefault(); setSection("ia"); }} style={{ color: "var(--green)", fontWeight: 600 }}>Inteligência Artificial</a>.
        </p>
      </div>
      )}

      {/* === LIXEIRA === */}
      {section === "lixeira" && <TrashCard ws={ws} setWs={setWs} activeWs={activeWs} setWorkspaces={setWorkspaces} accent={accent} />}

      {/* === BACKUP === */}
      {section === "backup" && <BackupCard ws={ws} activeWs={activeWs} accent={accent} setWorkspaces={setWorkspaces} />}

      {/* === IA === */}
      {section === "ia" && <AiKeyCard />}

      {/* === MOBILE === */}
      {section === "mobile" && <MobileChatShareCard accent={accent} />}

      {/* === PLANO === */}
      {section === "plano" && <PlanCard plan={plan} setPlan={setPlan} showUpgrade={showUpgrade} accent={accent} />}

      </div>{/* /content area */}

      {/* Modais — globais, não dependem de seção */}
      {iconPickerOpen && (
        <IconPicker
          selected={ws.icon}
          onSelect={icon => updateField("icon", icon)}
          onClose={() => setIconPickerOpen(false)}
          title="Símbolo da empresa"
        />
      )}
      {catIconTarget && (
        <IconPicker
          selected={catIcon(catIconTarget)}
          onSelect={icon => setCatIcon(catIconTarget, icon)}
          onClose={() => setCatIconTarget(null)}
          title={`Símbolo para "${catIconTarget}"`}
        />
      )}
      {importOpen && (
        <SmartImportModal ws={ws} setWs={setWs} onClose={() => setImportOpen(false)}
          setRangeMode={setRangeMode} setCustomRange={setCustomRange} />
      )}
    </div>
  );
}

// ========== LIXEIRA ==========
// Lista itens com deleted_at != NULL nos últimos 90 dias da workspace ativa.
// Permite restaurar (= deleted_at = NULL) ou apagar definitivamente (1 por vez).
function TrashCard({ ws, setWs, activeWs, setWorkspaces, accent }) {
  const [trash, setTrash] = React.useState({ transactions: [], incomes: [] });
  const [loading, setLoading] = React.useState(true);
  const [tab, setTab] = React.useState("transactions");

  const reload = React.useCallback(async () => {
    if (!activeWs) { setLoading(false); return; }
    setLoading(true);
    try {
      const t = await window.db.fetchTrash(activeWs);
      setTrash(t);
    } catch (e) { console.error(e); }
    setLoading(false);
  }, [activeWs]);

  React.useEffect(() => { reload(); }, [reload]);

  const refreshLocalState = async () => {
    // Refetch tudo após restore/purge pra sincronizar local com banco
    const data = await window.db.fetchAll();
    setWorkspaces(data || {});
  };

  const restore = async (kind, id) => {
    const fn = kind === "transactions" ? window.db.restoreTransaction : window.db.restoreIncome;
    const ok = await fn(id);
    if (!ok) { alert("Erro ao restaurar"); return; }
    await reload();
    await refreshLocalState();
    if (window.toast) window.toast({ message: "✓ Lançamento restaurado", duration: 3000 });
  };

  const hardDelete = async (kind, id, desc) => {
    if (!confirm(`Apagar DEFINITIVAMENTE "${desc}"?\n\nNão tem como recuperar depois.`)) return;
    const fn = kind === "transactions" ? window.db.hardDeleteTransaction : window.db.hardDeleteIncome;
    const ok = await fn(id);
    if (!ok) { alert("Erro ao apagar"); return; }
    await reload();
  };

  const restoreAll = async () => {
    const list = trash[tab] || [];
    if (!list.length) return;
    if (!confirm(`Restaurar ${list.length} lançamentos?`)) return;
    const fn = tab === "transactions" ? window.db.restoreTransaction : window.db.restoreIncome;
    for (const item of list) await fn(item.id);
    await reload();
    await refreshLocalState();
    if (window.toast) window.toast({ message: `✓ ${list.length} lançamentos restaurados`, duration: 4000 });
  };

  const list = trash[tab] || [];
  const total = trash.transactions.length + trash.incomes.length;

  return (
    <div className="card" style={{ padding: 22 }}>
      <h3 style={{ margin: 0, marginBottom: 4, fontSize: 15, fontWeight: 600, display: "flex", alignItems: "center", gap: 8 }}>
        <Icon name="trash" size={16} /> Lixeira
      </h3>
      <p style={{ margin: 0, marginBottom: 18, fontSize: 12.5, color: "var(--fg-3)" }}>
        Lançamentos excluídos ficam aqui por 90 dias. Pode restaurar ou apagar definitivamente.
        {total > 0 && <> <strong style={{ color: "var(--fg-1)" }}>{total} item{total === 1 ? "" : "s"}</strong> na lixeira.</>}
      </p>

      <div style={{ display: "flex", gap: 4, marginBottom: 16, borderBottom: "1px solid var(--border)" }}>
        {["transactions", "incomes"].map(k => (
          <button key={k} onClick={() => setTab(k)} style={{
            padding: "8px 14px", border: "none", background: "transparent", cursor: "pointer", fontFamily: "inherit",
            fontSize: 13, fontWeight: tab === k ? 600 : 500,
            color: tab === k ? "var(--fg-1)" : "var(--fg-3)",
            borderBottom: tab === k ? `2px solid ${accent}` : "2px solid transparent",
            marginBottom: -1,
          }}>
            {k === "transactions" ? "Saídas" : "Entradas"} ({trash[k].length})
          </button>
        ))}
      </div>

      {loading ? (
        <div style={{ padding: 24, textAlign: "center", color: "var(--fg-3)", fontSize: 13 }}>Carregando…</div>
      ) : list.length === 0 ? (
        <div style={{ padding: 32, textAlign: "center", color: "var(--fg-3)" }}>
          <Icon name="check" size={28} />
          <div style={{ marginTop: 10, fontSize: 13 }}>Lixeira vazia.</div>
        </div>
      ) : (
        <>
          <div style={{ display: "flex", justifyContent: "flex-end", marginBottom: 10 }}>
            <button className="btn-ghost" onClick={restoreAll} style={{ fontSize: 12 }}>
              <Icon name="check" size={12} /> Restaurar todos ({list.length})
            </button>
          </div>
          <div style={{ display: "flex", flexDirection: "column", gap: 6, maxHeight: 480, overflowY: "auto" }}>
            {list.map(item => {
              const value = Math.abs(Number(item.value) || 0);
              const valueStr = value.toLocaleString("pt-BR", { style: "currency", currency: "BRL" });
              const deletedAt = new Date(item.deletedAt);
              const ageDays = Math.floor((Date.now() - deletedAt.getTime()) / 86400000);
              return (
                <div key={item.id} style={{
                  display: "grid", gridTemplateColumns: "1fr auto auto", gap: 12, alignItems: "center",
                  padding: "10px 12px", borderRadius: 8, background: "var(--surface-2)", border: "1px solid var(--border)",
                }}>
                  <div style={{ minWidth: 0 }}>
                    <div style={{ fontSize: 13, fontWeight: 500, color: "var(--fg-1)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
                      {item.desc || "Sem descrição"}
                    </div>
                    <div style={{ fontSize: 11, color: "var(--fg-3)", marginTop: 2 }}>
                      {item.date} · {valueStr} · Excluído há {ageDays === 0 ? "menos de 1 dia" : `${ageDays} dia${ageDays === 1 ? "" : "s"}`}
                    </div>
                  </div>
                  <button onClick={() => restore(tab, item.id)} className="btn-ghost" style={{ fontSize: 12, padding: "6px 10px" }} title="Restaurar">
                    <Icon name="check" size={12} /> Restaurar
                  </button>
                  <button onClick={() => hardDelete(tab, item.id, item.desc)} className="icon-btn" style={{ color: "var(--danger)" }} title="Apagar definitivamente">
                    <Icon name="trash" size={13} />
                  </button>
                </div>
              );
            })}
          </div>
        </>
      )}

      <div style={{ marginTop: 16, padding: 12, borderRadius: 8, background: "color-mix(in oklab, var(--green) 6%, transparent)", border: "1px dashed color-mix(in oklab, var(--green) 30%, transparent)", fontSize: 12, color: "var(--fg-2)" }}>
        <strong style={{ color: "var(--green)" }}>Como funciona:</strong> quando você apaga um lançamento, ele vai pra cá em vez de sumir. Ficam 90 dias disponíveis pra restaurar. Depois disso são apagados de verdade automaticamente.
      </div>
    </div>
  );
}

// ========== BACKUP ==========
// Mostra status do backup automático (GitHub Action) e permite download manual em JSON.
// Restore aceita arquivo JSON e faz upsert no banco (idempotente).
function BackupCard({ ws, activeWs, accent, setWorkspaces }) {
  const [importing, setImporting] = React.useState(false);
  const fileRef = React.useRef(null);

  const downloadBackup = async () => {
    try {
      const data = await window.db.fetchAll();
      const blob = new Blob([JSON.stringify({ version: 1, exportedAt: new Date().toISOString(), workspaces: data }, null, 2)], { type: "application/json" });
      const url = URL.createObjectURL(blob);
      const a = document.createElement("a");
      a.href = url;
      a.download = `planilha-ia-backup-${new Date().toISOString().slice(0, 10)}.json`;
      a.click();
      URL.revokeObjectURL(url);
    } catch (e) {
      alert("Erro ao baixar backup: " + (e?.message || e));
    }
  };

  const handleRestoreFile = async (e) => {
    const file = e.target.files?.[0];
    if (!file) return;
    if (!confirm(
      `Restaurar do arquivo "${file.name}"?\n\n` +
      `Como funciona: tudo do backup é mesclado com seus dados atuais. ` +
      `Itens com mesmo ID são atualizados; itens novos do backup voltam pro banco. ` +
      `NADA é apagado dos seus dados atuais.\n\n` +
      `Continuar?`
    )) { e.target.value = ""; return; }

    setImporting(true);
    try {
      const text = await file.text();
      const json = JSON.parse(text);
      const wsData = json.workspaces || json;
      let totalTx = 0, totalIn = 0;
      for (const [wsKey, w] of Object.entries(wsData)) {
        if (w.transactions?.length) {
          const res = await window.db.insertTransactions(wsKey, w.transactions);
          totalTx += res.saved;
        }
        if (w.receitas?.length) {
          const res = await window.db.insertIncomes(wsKey, w.receitas);
          totalIn += res.saved;
        }
      }
      const fresh = await window.db.fetchAll();
      setWorkspaces(fresh || {});
      alert(`✓ Restore completo!\n\n${totalTx} saídas e ${totalIn} entradas processadas.`);
    } catch (e) {
      console.error(e);
      alert("Erro ao restaurar: " + (e?.message || e));
    } finally {
      setImporting(false);
      e.target.value = "";
    }
  };

  return (
    <div className="card" style={{ padding: 22 }}>
      <h3 style={{ margin: 0, marginBottom: 4, fontSize: 15, fontWeight: 600, display: "flex", alignItems: "center", gap: 8 }}>
        <Icon name="download" size={16} /> Backup
      </h3>
      <p style={{ margin: 0, marginBottom: 18, fontSize: 12.5, color: "var(--fg-3)" }}>
        Seus dados são protegidos por backup automático diário + você pode baixar manualmente quando quiser.
      </p>

      <div style={{ display: "grid", gap: 12 }}>

        <div style={{ padding: 14, borderRadius: 10, border: "1px solid var(--border)", background: "var(--bg-1)" }}>
          <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 6 }}>
            <span style={{ width: 8, height: 8, borderRadius: 4, background: "var(--green)" }} />
            <strong style={{ fontSize: 13, color: "var(--fg-1)" }}>Backup automático diário</strong>
          </div>
          <div style={{ fontSize: 12, color: "var(--fg-3)", lineHeight: 1.5 }}>
            Roda todo dia às 3h da manhã via GitHub Actions e salva os dados em repositório privado seguro. Histórico de 1 ano disponível pra restaurar.
          </div>
        </div>

        <div style={{ padding: 14, borderRadius: 10, border: "1px solid var(--border)", background: "var(--bg-1)" }}>
          <div style={{ fontSize: 13, color: "var(--fg-1)", fontWeight: 600, marginBottom: 6 }}>Baixar backup agora</div>
          <div style={{ fontSize: 12, color: "var(--fg-3)", lineHeight: 1.5, marginBottom: 10 }}>
            Exporta TODAS as contas, lançamentos e configurações num arquivo JSON. Guarde no Google Drive ou imprima — é seu seguro adicional.
          </div>
          <button className="btn-ghost" onClick={downloadBackup}>
            <Icon name="download" size={13} /> Baixar backup completo (.json)
          </button>
        </div>

        <div style={{ padding: 14, borderRadius: 10, border: "1px solid var(--border)", background: "var(--bg-1)" }}>
          <div style={{ fontSize: 13, color: "var(--fg-1)", fontWeight: 600, marginBottom: 6 }}>Restaurar de backup</div>
          <div style={{ fontSize: 12, color: "var(--fg-3)", lineHeight: 1.5, marginBottom: 10 }}>
            Tem um arquivo JSON de backup (do GitHub ou que você baixou)? Selecione abaixo. Faz merge: nada que você já tem é apagado, só os do backup voltam.
          </div>
          <input ref={fileRef} type="file" accept="application/json,.json" onChange={handleRestoreFile} style={{ display: "none" }} />
          <button className="btn-ghost" onClick={() => fileRef.current?.click()} disabled={importing}>
            <Icon name="spark" size={13} /> {importing ? "Restaurando…" : "Escolher arquivo de backup"}
          </button>
        </div>

        <div style={{ padding: 12, borderRadius: 8, background: "color-mix(in oklab, var(--green) 6%, transparent)", border: "1px dashed color-mix(in oklab, var(--green) 30%, transparent)", fontSize: 12, color: "var(--fg-2)" }}>
          <strong style={{ color: "var(--green)" }}>Camadas de proteção ativas:</strong>
          <ul style={{ margin: "6px 0 0", paddingLeft: 20, lineHeight: 1.6 }}>
            <li>Soft delete (lixeira de 90 dias)</li>
            <li>Bloqueio de exclusões em massa (>3/ciclo, >20/min)</li>
            <li>Trigger no banco (>50 deletes = bloqueado)</li>
            <li>Snapshot local automático no navegador</li>
            <li>Backup diário em repositório privado</li>
          </ul>
        </div>

      </div>
    </div>
  );
}

function Field({ label, children }) {
  return (
    <label style={{ display: "flex", flexDirection: "column", gap: 6 }}>
      <span style={{ fontSize: 11.5, color: "var(--fg-3)", textTransform: "uppercase", letterSpacing: 0.5, fontWeight: 500 }}>{label}</span>
      {children}
    </label>
  );
}

// Configurações de IA: provider (Anthropic/OpenAI/Gemini) + chave salva no navegador
const AI_PROVIDERS = {
  anthropic: {
    label: "Anthropic (Claude)",
    placeholder: "sk-ant-api03-...",
    keysUrl: "https://console.anthropic.com/settings/keys",
    keysLabel: "console.anthropic.com → API Keys",
    note: "Usa o modelo Claude Sonnet 4.6 — alta precisão pra extrair faturas.",
  },
  openai: {
    label: "OpenAI (GPT)",
    placeholder: "sk-proj-...",
    keysUrl: "https://platform.openai.com/api-keys",
    keysLabel: "platform.openai.com → API Keys",
    note: "Usa o modelo GPT-4o — alta precisão pra extrair faturas.",
  },
  gemini: {
    label: "Google (Gemini)",
    placeholder: "AIza...",
    keysUrl: "https://aistudio.google.com/apikey",
    keysLabel: "aistudio.google.com → Get API Key",
    note: "Usa o modelo Gemini 1.5 Pro — alta precisão pra extrair faturas.",
  },
};

function AiKeyCard() {
  const [provider, setProvider] = React.useState("anthropic");
  const [key, setKey] = React.useState("");
  const [saved, setSaved] = React.useState(null);
  const [showKey, setShowKey] = React.useState(false);
  const [savedFlash, setSavedFlash] = React.useState(false);

  React.useEffect(() => {
    window.db.getAiConfig().then(({ provider: p, key: k }) => {
      if (p) setProvider(p);
      if (k) { setKey(k); setSaved(true); }
      else setSaved(false);
    });
  }, []);

  const save = async () => {
    await window.db.setAiConfig(provider, key.trim());
    setSaved(!!key.trim());
    setSavedFlash(true);
    setTimeout(() => setSavedFlash(false), 2000);
  };

  const remove = async () => {
    if (!confirm("Remover a chave salva? Você não vai conseguir importar planilhas com IA até salvar de novo.")) return;
    await window.db.clearAiConfig();
    setKey("");
    setSaved(false);
  };

  const cfg = AI_PROVIDERS[provider];

  return (
    <div className="card" style={{ padding: 22 }}>
      <h3 style={{ margin: 0, marginBottom: 4, fontSize: 15, fontWeight: 600, display: "inline-flex", alignItems: "center", gap: 6 }}>
        <Icon name="spark" size={14} /> Inteligência Artificial
      </h3>
      <p style={{ margin: 0, marginBottom: 16, fontSize: 12.5, color: "var(--fg-3)" }}>
        Escolha um provedor de IA e cole sua chave para usar na importação de planilhas. A chave fica salva só neste navegador.
      </p>

      <div style={{ marginBottom: 12 }}>
        <div style={{ fontSize: 11.5, color: "var(--fg-3)", textTransform: "uppercase", letterSpacing: 0.5, fontWeight: 500, marginBottom: 6 }}>
          Provedor
        </div>
        <div style={{ display: "flex", gap: 6 }}>
          {Object.entries(AI_PROVIDERS).map(([k, v]) => {
            const active = provider === k;
            return (
              <button key={k} type="button" onClick={() => setProvider(k)} style={{
                flex: 1, padding: "10px 12px", fontFamily: "inherit", fontSize: 12.5, fontWeight: active ? 600 : 400,
                border: active ? "2px solid var(--accent)" : "1px solid var(--border)",
                background: active ? "color-mix(in oklab, var(--accent) 6%, var(--bg-1))" : "var(--bg-1)",
                color: active ? "var(--fg-1)" : "var(--fg-2)",
                borderRadius: 8, cursor: "pointer",
              }}>
                {v.label}
              </button>
            );
          })}
        </div>
      </div>

      <div style={{ display: "flex", gap: 8, alignItems: "center" }}>
        <input
          type={showKey ? "text" : "password"}
          className="input"
          value={key}
          onChange={e => setKey(e.target.value)}
          placeholder={cfg.placeholder}
          style={{ flex: 1, fontFamily: "ui-monospace, monospace", fontSize: 12.5 }}
        />
        <button className="btn-ghost" onClick={() => setShowKey(s => !s)}>
          {showKey ? "Ocultar" : "Mostrar"}
        </button>
      </div>

      <div style={{ display: "flex", gap: 8, marginTop: 10, alignItems: "center", flexWrap: "wrap" }}>
        <button className="btn-primary" onClick={save} disabled={!key.trim()} style={{ opacity: !key.trim() ? 0.5 : 1 }}>
          <Icon name="check" size={12} /> Salvar chave
        </button>
        {saved && (
          <button className="btn-ghost" onClick={remove} style={{ color: "var(--danger)" }}>
            Remover
          </button>
        )}
        {savedFlash && (
          <span style={{ fontSize: 12, color: "var(--success)" }}>✓ Chave salva neste navegador</span>
        )}
        {saved === true && !savedFlash && (
          <span style={{ fontSize: 11.5, color: "var(--fg-3)", marginLeft: "auto" }}>
            Chave configurada
          </span>
        )}
      </div>

      <div style={{ marginTop: 14, padding: "10px 12px", background: "var(--surface-2)", borderRadius: 8, fontSize: 11.5, color: "var(--fg-2)", lineHeight: 1.5 }}>
        Não tem chave? Crie em <a href={cfg.keysUrl} target="_blank" rel="noopener" style={{ color: "var(--fg-1)", textDecoration: "underline" }}>{cfg.keysLabel}</a>.
        <br />{cfg.note}
      </div>
    </div>
  );
}

// Card pra compartilhar link de lançamento rápido (modo mobile)
// URL FIXA de produção: o QR/link sempre aponta pra cá, independente de
// onde o usuário está navegando (localhost, preview, etc). Garante que
// quem escanear o QR no celular caia direto no app público.
const MOBILE_CHAT_URL = "https://planilha-ia-kappa.vercel.app/Financeiro?chat";

function MobileChatShareCard({ accent }) {
  const shareUrl = MOBILE_CHAT_URL;
  const isLocal = false;
  const [copied, setCopied] = React.useState(false);

  const copy = async () => {
    try {
      await navigator.clipboard.writeText(shareUrl);
      setCopied(true);
      setTimeout(() => setCopied(false), 2000);
    } catch (e) {
      // fallback: select + execCommand
      const ta = document.createElement("textarea");
      ta.value = shareUrl; document.body.appendChild(ta);
      ta.select(); document.execCommand("copy"); document.body.removeChild(ta);
      setCopied(true);
      setTimeout(() => setCopied(false), 2000);
    }
  };

  // QR code via API pública (Google Charts está deprecado; uso quickchart.io que é estável)
  const qrSrc = `https://api.qrserver.com/v1/create-qr-code/?size=180x180&data=${encodeURIComponent(shareUrl)}&margin=8`;

  return (
    <div className="card" style={{ padding: 22 }}>
      <h3 style={{ margin: 0, marginBottom: 4, fontSize: 15, fontWeight: 600 }}>
        <Icon name="spark" size={13} /> Link de chat móvel
      </h3>
      <p style={{ margin: 0, marginBottom: 16, fontSize: 12.5, color: "var(--fg-3)", lineHeight: 1.5 }}>
        Compartilhe este link com você mesmo (no celular). Ele abre <strong>só o chat de lançamentos rápidos</strong> em tela cheia — perfeito pra registrar gastos no dia a dia falando ou digitando, sem ter que navegar pelo app inteiro.
      </p>

      <div style={{ display: "flex", gap: 14, alignItems: "flex-start", flexWrap: "wrap" }}>
        {/* QR code */}
        <div style={{ width: 140, padding: 8, background: "white", border: "1px solid var(--border)", borderRadius: 10, flexShrink: 0 }}>
          <img src={qrSrc} alt="QR code" style={{ width: "100%", height: "auto", display: "block" }} />
          <div style={{ fontSize: 10, textAlign: "center", color: "var(--fg-3)", marginTop: 4 }}>Escaneie no celular</div>
        </div>

        {/* URL + ações */}
        <div style={{ flex: 1, minWidth: 240, display: "flex", flexDirection: "column", gap: 8 }}>
          <div style={{ fontSize: 11, color: "var(--fg-3)", textTransform: "uppercase", letterSpacing: 0.5, fontWeight: 500 }}>URL para abrir só o chat</div>
          <div style={{ display: "flex", gap: 6 }}>
            <input
              readOnly
              value={shareUrl}
              className="input"
              onFocus={e => e.target.select()}
              style={{ flex: 1, fontFamily: "ui-monospace, monospace", fontSize: 12, color: "var(--fg-1)" }}
            />
            <button
              className="btn-primary"
              onClick={copy}
              style={{ background: copied ? "var(--success)" : accent, fontSize: 12.5, whiteSpace: "nowrap" }}
            >
              <Icon name={copied ? "check" : "spark"} size={12} /> {copied ? "Copiado" : "Copiar"}
            </button>
          </div>

          {isLocal && (
            <div style={{ padding: "10px 12px", background: "color-mix(in oklab, var(--warn) 8%, transparent)", border: "1px solid color-mix(in oklab, var(--warn) 25%, transparent)", borderRadius: 8, fontSize: 11.5, color: "var(--fg-2)", lineHeight: 1.5 }}>
              <strong style={{ color: "var(--warn)" }}>⚠ Endereço local detectado.</strong> Este link só funciona neste computador.
              Quando publicar o app na Vercel, este link vai usar automaticamente o domínio público — não precisa mexer em nada aqui, é só voltar e copiar de novo.
            </div>
          )}

          <div style={{ fontSize: 11.5, color: "var(--fg-3)", lineHeight: 1.5 }}>
            <strong>Como usar:</strong> escaneia o QR ou cola o link no navegador do celular. Vai pedir login uma vez (mesmas credenciais), depois abre direto no chat. Tudo que você lançar pelo celular sincroniza com o app.
          </div>
        </div>
      </div>
    </div>
  );
}

// Card de Plano — mostra plano atual + toggle dev pra ativar Pro localmente
function PlanCard({ plan, setPlan, showUpgrade, accent }) {
  const isPro = plan === "pro";
  return (
    <div className="card" style={{
      padding: 24, position: "relative", overflow: "hidden",
      background: isPro
        ? `linear-gradient(165deg, color-mix(in oklab, var(--green) 8%, transparent), transparent), var(--surface-1)`
        : `linear-gradient(165deg, color-mix(in oklab, var(--green) 6%, transparent), transparent), var(--surface-1)`,
      borderColor: isPro ? "color-mix(in oklab, var(--green) 35%, var(--border))" : "var(--border)",
      boxShadow: isPro ? "0 0 24px color-mix(in oklab, var(--green) 12%, transparent), var(--shadow-sm)" : "var(--shadow-sm)",
    }}>
      <div style={{ display: "flex", alignItems: "flex-start", justifyContent: "space-between", gap: 16, flexWrap: "wrap" }}>
        <div style={{ flex: 1, minWidth: 240 }}>
          <div className="eyebrow" style={{ fontSize: 10.5, color: "var(--green)", marginBottom: 8 }}>// seu plano atual</div>
          <div style={{ display: "flex", alignItems: "baseline", gap: 12, flexWrap: "wrap" }}>
            <h3 style={{
              margin: 0, fontFamily: "'Fraunces', Georgia, serif", fontSize: 28, fontWeight: 400,
              letterSpacing: "-0.02em", lineHeight: 1, color: "var(--fg-1)",
            }}>
              {isPro ? <>Plano <em style={{ fontStyle: "italic", fontWeight: 300, color: "var(--green)" }}>Pro</em></> : <>Plano <em style={{ fontStyle: "italic", fontWeight: 300, color: "var(--fg-2)" }}>Free</em></>}
            </h3>
            <span style={{
              padding: "4px 12px", borderRadius: 99, fontSize: 10, fontWeight: 700, letterSpacing: "0.1em",
              fontFamily: "'JetBrains Mono', monospace",
              background: isPro ? "var(--green)" : "var(--surface-2)",
              color: isPro ? "#06120c" : "var(--fg-2)",
              border: isPro ? "none" : "1px solid var(--border)",
            }}>
              {isPro ? "ATIVO" : "GRATUITO"}
            </span>
          </div>
          <p style={{ margin: "10px 0 0", fontSize: 13, color: "var(--fg-2)", lineHeight: 1.55, maxWidth: 540 }}>
            {isPro
              ? "Você tem todas as funcionalidades desbloqueadas, sem limites. Workspaces ilimitados, lançamentos ilimitados, IA sem cap, histórico completo."
              : "Você está usando a versão gratuita. Tem limites de uso pra que você teste o produto antes de pagar."}
          </p>
        </div>
        {!isPro && (
          <button onClick={() => showUpgrade && showUpgrade()} className="btn-primary" style={{ flexShrink: 0, fontSize: 13.5, padding: "12px 22px" }}>
            <Icon name="spark" size={14} /> Fazer upgrade pro Pro
          </button>
        )}
      </div>

      {/* Detalhes dos limites quando Free */}
      {!isPro && (
        <div style={{ marginTop: 20, paddingTop: 18, borderTop: "1px solid var(--border)" }}>
          <div className="eyebrow" style={{ fontSize: 10, marginBottom: 10 }}>Seus limites no Free</div>
          <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(180px, 1fr))", gap: 10 }}>
            {[
              { label: "Contas", value: "1", limit: "Ilimitado no Pro" },
              { label: "Lançamentos manuais", value: "50/mês", limit: "Ilimitado no Pro" },
              { label: "Importação de fatura", value: "1/mês", limit: "Ilimitado no Pro" },
              { label: "Mensagens chat IA", value: "10/dia", limit: "Ilimitado no Pro" },
              { label: "Histórico", value: "3 meses", limit: "12+ meses no Pro" },
              { label: "Modo mobile + planilha + CSV", value: "—", limit: "Tudo no Pro" },
            ].map((item, i) => (
              <div key={i} style={{ padding: "10px 12px", background: "var(--surface-2)", borderRadius: 10, border: "1px solid var(--border)" }}>
                <div className="eyebrow" style={{ fontSize: 9.5, marginBottom: 4 }}>{item.label}</div>
                <div style={{ fontSize: 14, fontWeight: 600, color: "var(--fg-1)", marginBottom: 2 }}>{item.value}</div>
                <div style={{ fontSize: 10.5, color: "var(--green)", fontFamily: "'JetBrains Mono', monospace", letterSpacing: "0.04em" }}>↑ {item.limit}</div>
              </div>
            ))}
          </div>
        </div>
      )}

    </div>
  );
}

window.Settings = Settings;
