/* O Tal Picolé — App shell with central state & CRUD */

const NAV_ITEMS = [
  { id:"dashboard",          label:"Dashboard",           icon: Icons.Dashboard },
  { id:"fabricacao",         label:"Fabricação",          icon: Icons.Factory },
  { id:"pdv-distribuidores", label:"PDV Distribuidores",  icon: Icons.Store },
  { id:"pdv-picolezeiros",   label:"PDV Picolezeiros",    icon: Icons.Sun },
  { id:"clientes",           label:"Clientes",            icon: Icons.Users },
  { id:"estoque",            label:"Estoque",             icon: Icons.Box },
  { id:"movimentacoes",      label:"Movimentações",       icon: Icons.Wallet },
  { id:"relatorios",         label:"Relatórios",          icon: Icons.Chart },
  { id:"configuracoes",      label:"Configurações",       icon: Icons.Settings },
];

/* Helpers */
const uid = (prefix) => prefix + "-" + Math.random().toString(36).slice(2, 8);

/* Lista de abas para permissões por usuário */
const TAB_PERMS = [
  { id:"dashboard",          label:"Dashboard" },
  { id:"fabricacao",         label:"Fabricação" },
  { id:"pdv-distribuidores", label:"PDV Distribuidores" },
  { id:"pdv-picolezeiros",   label:"PDV Picolezeiros" },
  { id:"clientes",           label:"Clientes" },
  { id:"estoque",            label:"Estoque" },
  { id:"movimentacoes",      label:"Movimentações" },
  { id:"relatorios",         label:"Relatórios" },
  { id:"configuracoes",      label:"Configurações" },
];
const DEFAULT_PERMS_BY_ROLE = {
  "Admin":        ["*"],
  "Gestor":       ["dashboard","fabricacao","pdv-distribuidores","pdv-picolezeiros","clientes","estoque","movimentacoes","relatorios"],
  "Operador":     ["dashboard","pdv-distribuidores","pdv-picolezeiros","estoque","clientes"],
  "Visualizador": ["dashboard","relatorios"],
};
window.TAB_PERMS = TAB_PERMS;
window.DEFAULT_PERMS_BY_ROLE = DEFAULT_PERMS_BY_ROLE;

function App() {
  const [tab, setTab] = useState("dashboard");
  const [toast, setToast] = useState(null);
  const [confirm, setConfirm] = useState(null); // { title, message, onConfirm }
  const [mobileNav, setMobileNav] = useState(false); // drawer aberto no celular
  const [loading, setLoading] = useState(!!window.supa); // só "carregando" se Supabase estiver configurado
  const [loadError, setLoadError] = useState(null);
  const [session, setSession] = useState(undefined); // undefined = ainda checando; null = sem sessão; obj = logado
  const [authReady, setAuthReady] = useState(!window.supa); // se Supabase off, ignora auth

  // ====== Central state ======
  const [state, setState] = useState(() => {
    /* Migra a_receber existentes do FINANCE mock para sales (pendentes).
       A nova regra: finance só armazena valores efetivamente pagos. */
    const pendingFromFinance = FINANCE.filter(f => f.status === "a_receber" && f.cat === "Venda Distribuidor");
    const confirmedFinance = FINANCE.filter(f => !(f.status === "a_receber" && f.cat === "Venda Distribuidor"));
    const seedPendingSales = pendingFromFinance.map(f => {
      const client = DISTRIBUTORS.find(d => d.name === f.ref);
      return {
        id: "v-seed-" + f.id,
        clientId: client?.id || "d1",
        date: f.date,
        time: "—",
        items: [], // detalhes não disponíveis no mock — exibido como resumo
        payment: client?.payment || "Boleto 14d",
        subtotal: f.value, discount: 0, total: f.value, perBox: 60,
        paid: 0,
        status: "a_receber",
        payments: [],
        seedNote: "Pendência histórica importada do mock",
      };
    });
    return ({
    products:     PRODUCTS.map(p => ({ ...p })),
    warehouses:   WAREHOUSES.map(w => ({ ...w })),
    stock:        STOCK.map(s => ({ ...s })),
    stockMovements: STOCK_MOVEMENTS.map(m => ({ ...m })),
    beaches:      BEACHES.map(b => ({ ...b })),
    picolezeiros: PICOLEZEIROS.map(p => ({ ...p, beaches: [...p.beaches] })),
    distributors: DISTRIBUTORS.map(d => ({ ...d, totals: { ...d.totals } })),
    inputs:       INPUTS.map(i => ({ ...i })),
    recipes:      RECIPES.map(r => ({ ...r, items: r.items.map(i => ({ ...i })) })),
    finance:      confirmedFinance.map(f => ({ ...f })),
    trips:        PIC_TRIPS.map(t => ({
      ...t,
      items: t.items.map(i => ({ ...i })),
      beachesPlanned: [...t.beachesPlanned],
      beachesActual:  t.beachesActual ? [...t.beachesActual] : null,
      sold:    t.sold     ? t.sold.map(i => ({...i})) : null,
      returned:t.returned ? t.returned.map(i => ({...i})) : null,
    })),
    /* Vendas detalhadas (distribuidores) — com itens, hora, pagamento, etc. */
    sales: seedPendingSales,
    /* Compras de insumos (com forma de pagamento e status de quitação) */
    inputPurchases: [],
    /* Gastos extras da fábrica (embalagens, limpeza, manutenção, etc.) */
    expenses: [],
    /* Lotes pré-montados para PDV distribuidores */
    salePresets: [
      {
        id: "preset-mix-verao",
        name: "Mix Verão",
        description: "5 picolés mais vendidos · 5 cx cada",
        items: [
          { productId: "p03", boxes: 5 }, // Morango
          { productId: "p07", boxes: 5 }, // Chocolate
          { productId: "p01", boxes: 5 }, // Limão
          { productId: "p04", boxes: 5 }, // Manga
          { productId: "p09", boxes: 5 }, // Coco
        ],
      },
      {
        id: "preset-linha-silver",
        name: "Linha Silver completa",
        description: "7 sabores Silver · 2 cx cada",
        items: [
          { productId: "s01", boxes: 2 },
          { productId: "s02", boxes: 2 },
          { productId: "s03", boxes: 2 },
          { productId: "s04", boxes: 2 },
          { productId: "s05", boxes: 2 },
          { productId: "s06", boxes: 2 },
          { productId: "s07", boxes: 2 },
        ],
      },
      {
        id: "preset-combo-quiosque",
        name: "Combo Quiosque",
        description: "Picolés + Silver · pedido médio",
        items: [
          { productId: "p03", boxes: 4 },
          { productId: "p07", boxes: 4 },
          { productId: "p01", boxes: 3 },
          { productId: "s05", boxes: 2 },
          { productId: "s02", boxes: 2 },
        ],
      },
      {
        id: "preset-gold-degust",
        name: "Gold Degustação",
        description: "Linha Gold premium · 1 cx cada",
        items: [
          { productId: "g01", boxes: 1 },
          { productId: "g02", boxes: 1 },
          { productId: "g03", boxes: 1 },
          { productId: "g04", boxes: 1 },
          { productId: "g05", boxes: 1 },
        ],
      },
    ],
    staff:        STAFF.map(s => ({
      ...s,
      password: s.password || "123456",
      permissions: s.permissions || (s.role === "Admin Super" ? ["*"] : DEFAULT_PERMS_BY_ROLE[s.role] || ["dashboard"])
    })),
    company: {
      name: BRAND.name, cnpj: BRAND.cnpj, address: BRAND.address, phone: BRAND.phone,
      email: "contato@otalpicole.com.br", ie: "",
      logo: (typeof localStorage !== "undefined" && localStorage.getItem("otp.logo")) || "",
    },
    settings: {
      commission: 18, // %
      methods: ["PIX","Dinheiro","Cartão","Boleto 14d","Prazo 30d"],
      divergencePolicy: "warn",
      confirmDelete: true,
      alertLowStock: true,
      alertExpiring: true,
      auditLog: true,
    },
  });
  });

  /* ============ AUTH: checa sessão + escuta mudanças ============ */
  useEffect(() => {
    if (!window.supa) return;
    window.supa.auth.getSession().then(({ data }) => {
      setSession(data.session || null);
      setAuthReady(true);
    });
    const { data: sub } = window.supa.auth.onAuthStateChange((_event, sess) => {
      setSession(sess || null);
    });
    return () => sub?.subscription?.unsubscribe?.();
  }, []);

  /* ============ SUPABASE: load on mount + realtime ============ */
  useEffect(() => {
    if (!window.supa || !window.supaLoadAll) return;
    let cancelled = false;
    window.supaLoadAll()
      .then(data => {
        if (cancelled) return;
        setState(prev => ({
          ...prev,
          ...data,
          // o que vem do banco é fonte da verdade; mantemos só o que NÃO está no banco ainda
        }));
        setLoading(false);
        console.info("[supabase] state carregado do banco");
      })
      .catch(err => {
        console.error("[supabase] falha carregando — usando mocks:", err);
        setLoadError(err.message);
        setLoading(false);
      });
    return () => { cancelled = true; };
  }, []);

  // Realtime: aplica mudanças vindas de outras sessões
  useEffect(() => {
    if (!window.supa || !window.supaSubscribeAll) return;
    const unsub = window.supaSubscribeAll(({ table, stateKey, eventType, new: newRow, old: oldRow }) => {
      setState(prev => {
        // tabelas single-row (company, settings)
        if (table === "company" || table === "settings") {
          return { ...prev, [stateKey]: { ...prev[stateKey], ...newRow } };
        }
        // tabelas com PK composta (stock) → match por warehouseId+productId
        if (stateKey === "stock") {
          const list = prev[stateKey] || [];
          const matches = (x) => x.warehouseId === (newRow?.warehouseId || oldRow?.warehouseId)
                              && x.productId   === (newRow?.productId   || oldRow?.productId);
          if (eventType === "DELETE") return { ...prev, [stateKey]: list.filter(x => !matches(x)) };
          const i = list.findIndex(matches);
          if (i < 0) return { ...prev, [stateKey]: [newRow, ...list] };
          return { ...prev, [stateKey]: list.map((x, idx) => idx === i ? newRow : x) };
        }
        // demais: PK simples por id
        const list = prev[stateKey] || [];
        const id = newRow?.id || oldRow?.id;
        if (!id) return prev;
        if (eventType === "DELETE") return { ...prev, [stateKey]: list.filter(x => x.id !== id) };
        const i = list.findIndex(x => x.id === id);
        if (i < 0) return { ...prev, [stateKey]: [newRow, ...list] };
        return { ...prev, [stateKey]: list.map(x => x.id === id ? newRow : x) };
      });
    });
    return unsub;
  }, []);

  /* ============ ACTIONS ============ */
  const pushToast = (msg) => setToast(msg);

  const askConfirm = (opts) => setConfirm(opts);

  /* Helper: stateKey (camel) → nome da tabela (snake) no Supabase. Inverso de SUPA_TABLE_MAP. */
  const stateKeyToTable = (key) => {
    if (!window.SUPA_TABLE_MAP) return null;
    return Object.entries(window.SUPA_TABLE_MAP).find(([_t, k]) => k === key)?.[0] || null;
  };

  /* Helper de persistência write-through. Não bloqueia a UI — registra erro
     no console se algo falhar. Cada action chama `persist(...)` depois do setState. */
  const persist = {
    upsert: (stateKey, row) => {
      const table = stateKeyToTable(stateKey);
      if (table && window.supaWrite) window.supaWrite.upsert(table, row);
    },
    upsertMany: (stateKey, rows) => {
      const table = stateKeyToTable(stateKey);
      if (!table || !window.supaWrite) return;
      rows.forEach(r => window.supaWrite.upsert(table, r));
    },
    insert: (stateKey, row) => {
      const table = stateKeyToTable(stateKey);
      if (table && window.supaWrite) window.supaWrite.insert(table, row);
    },
    update: (stateKey, match, patch) => {
      const table = stateKeyToTable(stateKey);
      if (table && window.supaWrite) window.supaWrite.update(table, match, patch);
    },
    remove: (stateKey, match) => {
      const table = stateKeyToTable(stateKey);
      if (table && window.supaWrite) window.supaWrite.remove(table, match);
    },
    updateSingleRow: (singleRowTable, patch) => {
      if (window.supaWrite) window.supaWrite.update(singleRowTable, { id: 1 }, patch);
    },
  };

  // Generic CRUD helpers (agora com write-through)
  const addTo = (key, item, prefix) => {
    const withId = { ...item, id: item.id || uid(prefix) };
    setState(s => ({ ...s, [key]: [withId, ...s[key]] }));
    persist.insert(key, withId);
  };
  const updateIn = (key, id, patch) => {
    setState(s => ({ ...s, [key]: s[key].map(x => x.id === id ? { ...x, ...patch } : x) }));
    persist.update(key, { id }, patch);
  };
  const removeFrom = (key, id) => {
    setState(s => ({ ...s, [key]: s[key].filter(x => x.id !== id) }));
    persist.remove(key, { id });
  };

  // ===== Stock helpers (multi-armazém) =====
  // Aplica delta de unidades em (warehouseId, productId), criando linha se necessário,
  // e devolve { stock, stockMovements } atualizados com o movimento registrado.
  const applyStockDelta = (s, warehouseId, productId, deltaUnits, mv) => {
    let stock = s.stock.slice();
    const idx = stock.findIndex(x => x.warehouseId === warehouseId && x.productId === productId);
    if (idx < 0) {
      stock = [...stock, {
        warehouseId, productId,
        units: Math.max(0, deltaUnits),
        perBox: 60, minUnits: 50,
        producedOn: new Date(), expiresOn: new Date(Date.now() + 180*86400000),
      }];
    } else {
      stock = stock.map((x, i) => i === idx
        ? { ...x, units: Math.max(0, x.units + deltaUnits), ...(deltaUnits > 0 ? { producedOn: new Date() } : {}) }
        : x);
    }
    const movement = {
      id: uid("mv"),
      ts: new Date().toISOString(),
      warehouseId, productId,
      units: deltaUnits,
      kind: mv?.kind || "adjust",
      refType: mv?.refType, refId: mv?.refId,
      counterpartWarehouseId: mv?.counterpartWarehouseId,
      note: mv?.note || "",
    };
    return { stock, stockMovements: [movement, ...s.stockMovements] };
  };

  const adjustStock = (productId, deltaUnits, warehouseId, opts) => setState(s => {
    const whId = warehouseId || WH_DISTRIB_ID;
    const next = applyStockDelta(s, whId, productId, deltaUnits, {
      kind: "adjust",
      note: opts?.reason || "",
    });
    // persist: stock row + nova movimentação
    const updatedRow = next.stock.find(x => x.warehouseId === whId && x.productId === productId);
    if (updatedRow) persist.upsert("stock", updatedRow);
    const newMv = next.stockMovements[0];
    if (newMv) persist.insert("stockMovements", newMv);
    return { ...s, ...next };
  });

  const transferStock = ({ fromWh, toWh, productId, units, note }) => {
    if (!fromWh || !toWh || fromWh === toWh || !productId || !units || units <= 0) return;
    const refId = uid("tr");
    setState(s => {
      const fromRow = s.stock.find(x => x.warehouseId === fromWh && x.productId === productId);
      const available = fromRow?.units || 0;
      const qty = Math.min(units, available);
      if (qty <= 0) return s;
      let next = applyStockDelta(s, fromWh, productId, -qty, {
        kind: "transfer-out", refType: "transfer", refId, counterpartWarehouseId: toWh, note: note || "",
      });
      next = applyStockDelta({ ...s, ...next }, toWh, productId, qty, {
        kind: "transfer-in", refType: "transfer", refId, counterpartWarehouseId: fromWh, note: note || "",
      });
      // persist: ambos stock rows + 2 movimentações novas
      const outRow = next.stock.find(x => x.warehouseId === fromWh && x.productId === productId);
      const inRow  = next.stock.find(x => x.warehouseId === toWh   && x.productId === productId);
      if (outRow) persist.upsert("stock", outRow);
      if (inRow)  persist.upsert("stock", inRow);
      persist.insert("stockMovements", next.stockMovements[0]);
      persist.insert("stockMovements", next.stockMovements[1]);
      return { ...s, ...next };
    });
    pushToast("Transferência registrada");
  };

  const createWarehouse = ({ name, note }) => {
    if (!name || !name.trim()) return;
    const w = { id: uid("wh"), name: name.trim(), kind: "custom", system: false, note: note || "" };
    setState(s => ({ ...s, warehouses: [...s.warehouses, w] }));
    persist.insert("warehouses", w);
    pushToast("Armazém criado");
    return w.id;
  };
  const renameWarehouse = (id, patch) => {
    setState(s => ({ ...s, warehouses: s.warehouses.map(w => w.id === id ? { ...w, ...patch } : w) }));
    persist.update("warehouses", { id }, patch);
    pushToast("Armazém atualizado");
  };
  const deleteWarehouse = (id) => {
    setState(s => {
      const w = s.warehouses.find(x => x.id === id);
      if (!w || w.system) return s;
      const hasStock = s.stock.some(x => x.warehouseId === id && x.units > 0);
      if (hasStock) { pushToast("Esvazie o armazém antes de excluí-lo"); return s; }
      persist.remove("warehouses", { id });
      return {
        ...s,
        warehouses: s.warehouses.filter(x => x.id !== id),
        stock: s.stock.filter(x => x.warehouseId !== id),
      };
    });
  };

  // Input consumption
  const consumeInputs = (recipeId, batches) => setState(s => {
    const r = s.recipes.find(x => x.id === recipeId);
    if (!r) return s;
    const inputs = s.inputs.map(inp => {
      const used = r.items.find(it => it.inputId === inp.id);
      if (!used) return inp;
      return { ...inp, stock: Math.max(0, inp.stock - used.qty * batches) };
    });
    return { ...s, inputs };
  });

  // High-level business actions
  const finalizeDistribSale = ({ clientId, items, payment, total, subtotal, discount, perBox }) => {
    /* items: [{ productId, boxes, loose? }] — loose = unidades avulsas (além das caixas) */
    const now = new Date();
    const saleId = uid("v");
    const isPrazo = (payment||"").startsWith("Prazo") || (payment||"").startsWith("Boleto");
    setState(s => {
      const getBox = (productId) => {
        const p = s.products.find(x => x.id === productId);
        return p?.boxSize || perBox || 60;
      };
      // 1. decrement stock do armazém de Distribuidores (caixas × boxSize + avulsos)
      let working = { ...s };
      items.forEach(({ productId, boxes, loose }) => {
        const totalUnits = (boxes || 0) * getBox(productId) + (loose || 0);
        if (totalUnits === 0) return;
        const next = applyStockDelta(working, WH_DISTRIB_ID, productId, -totalUnits, {
          kind: "sale", refType: "sale", refId: saleId,
        });
        working = { ...working, ...next };
      });
      let stock = working.stock;
      let stockMovements = working.stockMovements;
      // 2. add finance entry SOMENTE se à vista (pendentes só geram movimentação após o pagamento)
      const client = s.distributors.find(d => d.id === clientId);
      let finance = s.finance;
      const initialPayments = [];
      if (!isPrazo) {
        const finId = uid("f");
        finance = [{
          id: finId,
          date: now.toISOString().slice(0,10),
          kind: "in",
          cat: "Venda Distribuidor",
          ref: client?.name || "—",
          value: total,
          status: "confirmado",
          saleId,
          method: payment,
        }, ...s.finance];
        initialPayments.push({ date: now.toISOString().slice(0,10), amount: total, method: payment, financeId: finId });
      }
      // 3. bump client totals
      const distributors = s.distributors.map(d => d.id === clientId
        ? { ...d, totals: { compras: d.totals.compras + 1, valor: d.totals.valor + total } }
        : d
      );
      // 4. registra venda detalhada
      const saleRecord = {
        id: saleId,
        clientId,
        date: now.toISOString().slice(0,10),
        time: now.toTimeString().slice(0,5),
        items: items.map(({productId, boxes, loose}) => {
          const p = s.products.find(x => x.id === productId);
          const unitPrice = (p?.price || 0) * 0.55;
          const productBoxSize = p?.boxSize || 60;
          const b = boxes || 0;
          const lo = loose || 0;
          const units = b * productBoxSize + lo;
          return {
            productId, boxes: b, loose: lo, boxSize: productBoxSize,
            units, unitPrice,
            subtotal: units * unitPrice,
          };
        }),
        payment,
        subtotal: subtotal != null ? subtotal : total,
        discount: discount != null ? discount : 0,
        total,
        paid: isPrazo ? 0 : total,
        status: isPrazo ? "a_receber" : "confirmado",
        payments: initialPayments,
      };
      // persist: stock rows + movimentações + finance + distributor + sale
      working.stock.forEach(row => {
        if (items.some(it => it.productId === row.productId) && row.warehouseId === WH_DISTRIB_ID) {
          persist.upsert("stock", row);
        }
      });
      working.stockMovements.slice(0, items.length).forEach(mv => persist.insert("stockMovements", mv));
      if (finance[0] && finance[0].id !== s.finance[0]?.id) persist.insert("finance", finance[0]);
      const distRow = distributors.find(d => d.id === clientId);
      if (distRow) persist.update("distributors", { id: clientId }, { totals: distRow.totals });
      persist.insert("sales", saleRecord);
      return { ...s, stock, stockMovements, finance, distributors, sales: [saleRecord, ...s.sales] };
    });
    pushToast(isPrazo ? "Venda registrada como pendente (a receber)" : "Venda finalizada — estoque e financeiro atualizados");
    return saleId;
  };

  /* Recebimento de pagamento (parcial ou total) de uma venda pendente */
  const receiveSalePayment = ({ saleId, amount, method, date }) => {
    setState(s => {
      const sale = s.sales.find(x => x.id === saleId);
      if (!sale) return s;
      const client = s.distributors.find(d => d.id === sale.clientId);
      const dt = date || new Date().toISOString().slice(0,10);
      const useAmount = Math.min(amount, sale.total - sale.paid);
      if (useAmount <= 0) return s;
      const newPaid = sale.paid + useAmount;
      const newStatus = newPaid >= sale.total - 0.001 ? "confirmado" : "a_receber";
      const finId = uid("f");
      const financeEntry = {
        id: finId,
        date: dt,
        kind: "in",
        cat: "Venda Distribuidor",
        ref: client?.name || "—",
        value: useAmount,
        status: "confirmado",
        saleId,
        method: method || sale.payment,
        partial: newStatus !== "confirmado",
      };
      const sales = s.sales.map(x => x.id === saleId ? {
        ...x,
        paid: newPaid,
        status: newStatus,
        payments: [ ...(x.payments||[]), { date: dt, amount: useAmount, method: method || sale.payment, financeId: finId } ],
      } : x);
      persist.update("sales", { id: saleId }, sales.find(x => x.id === saleId));
      persist.insert("finance", financeEntry);
      return { ...s, sales, finance: [financeEntry, ...s.finance] };
    });
    pushToast("Pagamento registrado em movimentações");
  };

  /* Compra de insumos — com forma de pagamento e fiado */
  const recordInputPurchase = ({ supplier, items, payment, paid, date, note }) => {
    const purchaseId = uid("ip");
    const now = new Date();
    const dt = date || now.toISOString().slice(0,10);
    const total = items.reduce((s, it) => s + (it.qty * it.unitCost), 0);
    const initialPaid = (payment === "Fiado") ? (paid != null ? paid : 0) : (paid != null ? paid : total);
    setState(s => {
      // 1. incrementa estoque (e atualiza custo unit. com média ponderada simples)
      let inputs = s.inputs.slice();
      items.forEach(it => {
        inputs = inputs.map(inp => {
          if (inp.id !== it.inputId) return inp;
          const newStock = inp.stock + it.qty;
          // média ponderada do custo
          const blendedCost = newStock > 0 ? ((inp.stock * inp.unitCost) + (it.qty * it.unitCost)) / newStock : it.unitCost;
          return { ...inp, stock: newStock, unitCost: Math.round(blendedCost * 100) / 100 };
        });
      });
      // 2. finance entry apenas para a parte paga
      let finance = s.finance;
      const initialPayments = [];
      if (initialPaid > 0) {
        const finId = uid("f");
        finance = [{
          id: finId, date: dt, kind:"out", cat:"Compra de Insumo",
          ref: supplier || "Compra de insumos",
          value: initialPaid, status:"confirmado", purchaseId, method: payment,
          partial: initialPaid < total,
        }, ...s.finance];
        initialPayments.push({ date: dt, amount: initialPaid, method: payment || "—", financeId: finId });
      }
      // 3. cria registro da compra
      const purchase = {
        id: purchaseId, supplier: supplier || "—", date: dt,
        time: now.toTimeString().slice(0,5),
        items: items.map(it => ({
          inputId: it.inputId, qty: it.qty, unitCost: it.unitCost,
          subtotal: it.qty * it.unitCost,
        })),
        payment: payment || "PIX",
        total,
        paid: initialPaid,
        status: initialPaid >= total - 0.001 ? "confirmado" : (initialPaid > 0 ? "parcial" : "a_pagar"),
        payments: initialPayments,
        note: note || "",
      };
      // persist: insumos atualizados + finance + purchase
      items.forEach(it => {
        const inp = inputs.find(x => x.id === it.inputId);
        if (inp) persist.update("inputs", { id: inp.id }, { stock: inp.stock, unitCost: inp.unitCost });
      });
      if (initialPaid > 0 && finance[0] && finance[0].id !== s.finance[0]?.id) persist.insert("finance", finance[0]);
      persist.insert("inputPurchases", purchase);
      return { ...s, inputs, finance, inputPurchases: [purchase, ...s.inputPurchases] };
    });
    pushToast(initialPaid >= total - 0.001
      ? "Compra registrada — insumos no estoque e pagamento lançado"
      : initialPaid > 0
        ? "Compra registrada parcialmente paga — saldo em pendências"
        : "Compra fiada registrada — saldo em pendências"
    );
    return purchaseId;
  };

  /* Pagamento (parcial ou total) de uma compra fiada/parcial */
  const payInputPurchase = ({ purchaseId, amount, method, date }) => {
    setState(s => {
      const p = s.inputPurchases.find(x => x.id === purchaseId);
      if (!p) return s;
      const dt = date || new Date().toISOString().slice(0,10);
      const useAmount = Math.min(amount, p.total - p.paid);
      if (useAmount <= 0) return s;
      const newPaid = p.paid + useAmount;
      const newStatus = newPaid >= p.total - 0.001 ? "confirmado" : "parcial";
      const finId = uid("f");
      const financeEntry = {
        id: finId, date: dt, kind:"out", cat:"Compra de Insumo",
        ref: p.supplier || "Compra de insumos",
        value: useAmount, status:"confirmado", purchaseId,
        method: method || p.payment,
        partial: newStatus !== "confirmado",
      };
      const inputPurchases = s.inputPurchases.map(x => x.id === purchaseId ? {
        ...x, paid: newPaid, status: newStatus,
        payments: [ ...(x.payments||[]), { date: dt, amount: useAmount, method: method || p.payment, financeId: finId } ],
      } : x);
      persist.update("inputPurchases", { id: purchaseId }, inputPurchases.find(x => x.id === purchaseId));
      persist.insert("finance", financeEntry);
      return { ...s, inputPurchases, finance: [financeEntry, ...s.finance] };
    });
    pushToast("Pagamento de insumo registrado em movimentações");
  };

  /* Gastos extras da fábrica (embalagens, limpeza, etc.) */
  const recordExpense = ({ name, category, qty, unit, total, payment, paid, date, note }) => {
    const expenseId = uid("ex");
    const now = new Date();
    const dt = date || now.toISOString().slice(0,10);
    const totalNum = parseFloat(total) || 0;
    const initialPaid = payment === "Fiado" ? (paid != null ? paid : 0) : (paid != null ? paid : totalNum);
    setState(s => {
      let finance = s.finance;
      const payments = [];
      if (initialPaid > 0) {
        const finId = uid("f");
        finance = [{
          id: finId, date: dt, kind:"out", cat:"Despesa Operacional",
          ref: name + (qty ? ` (${qty} ${unit||"un"})` : ""),
          value: initialPaid, status:"confirmado",
          expenseId, method: payment, partial: initialPaid < totalNum,
        }, ...s.finance];
        payments.push({ date: dt, amount: initialPaid, method: payment || "—", financeId: finId });
      }
      const expense = {
        id: expenseId, name, category: category || "Outros",
        qty: parseFloat(qty) || 0, unit: unit || "un",
        total: totalNum, paid: initialPaid,
        payment: payment || "PIX",
        date: dt, time: now.toTimeString().slice(0,5),
        status: initialPaid >= totalNum - 0.001 ? "confirmado" : (initialPaid > 0 ? "parcial" : "a_pagar"),
        payments,
        note: note || "",
      };
      if (initialPaid > 0 && finance[0] && finance[0].id !== s.finance[0]?.id) persist.insert("finance", finance[0]);
      persist.insert("expenses", expense);
      return { ...s, finance, expenses: [expense, ...s.expenses] };
    });
    pushToast(initialPaid >= totalNum - 0.001
      ? "Gasto registrado em movimentações"
      : initialPaid > 0
        ? "Gasto parcialmente pago — saldo em pendências"
        : "Gasto fiado — saldo em pendências"
    );
    return expenseId;
  };

  const payExpense = ({ expenseId, amount, method, date }) => {
    setState(s => {
      const ex = s.expenses.find(x => x.id === expenseId);
      if (!ex) return s;
      const dt = date || new Date().toISOString().slice(0,10);
      const useAmount = Math.min(amount, ex.total - ex.paid);
      if (useAmount <= 0) return s;
      const newPaid = ex.paid + useAmount;
      const newStatus = newPaid >= ex.total - 0.001 ? "confirmado" : "parcial";
      const finId = uid("f");
      const financeEntry = {
        id: finId, date: dt, kind:"out", cat:"Despesa Operacional",
        ref: ex.name + (ex.qty ? ` (${ex.qty} ${ex.unit||"un"})` : ""),
        value: useAmount, status:"confirmado",
        expenseId, method: method || ex.payment,
        partial: newStatus !== "confirmado",
      };
      const expenses = s.expenses.map(x => x.id === expenseId ? {
        ...x, paid: newPaid, status: newStatus,
        payments: [ ...(x.payments||[]), { date: dt, amount: useAmount, method: method || ex.payment, financeId: finId } ],
      } : x);
      persist.update("expenses", { id: expenseId }, expenses.find(x => x.id === expenseId));
      persist.insert("finance", financeEntry);
      return { ...s, expenses, finance: [financeEntry, ...s.finance] };
    });
    pushToast("Pagamento de gasto registrado em movimentações");
  };

  const recordProduction = ({ recipeId, batches, warehouseId }) => {
    const prodId = uid("prod");
    setState(s => {
      const r = s.recipes.find(x => x.id === recipeId);
      if (!r) return s;
      const whId = warehouseId || s.warehouses[0]?.id || WH_DISTRIB_ID;
      // consume inputs
      const inputs = s.inputs.map(inp => {
        const used = r.items.find(it => it.inputId === inp.id);
        if (!used) return inp;
        return { ...inp, stock: Math.max(0, inp.stock - used.qty * batches) };
      });
      // add stock no armazém escolhido
      const units = r.yield * batches;
      const next = applyStockDelta({ ...s, inputs }, whId, r.productId, units, {
        kind: "produce", refType: "production", refId: prodId, note: `${r.name} (${batches}x)`,
      });
      // finance cost
      const cost = r.items.reduce((sm, it) => {
        const inp = s.inputs.find(x => x.id === it.inputId);
        return sm + (inp?.unitCost || 0) * it.qty;
      }, 0) * batches;
      const finance = [{
        id: uid("f"), date: new Date().toISOString().slice(0,10), kind:"out",
        cat:"Produção", ref:`${r.name} (${batches}x)`, value: cost, status:"confirmado"
      }, ...s.finance];
      // persist
      inputs.forEach(inp => {
        const before = s.inputs.find(x => x.id === inp.id);
        if (before && before.stock !== inp.stock) persist.update("inputs", { id: inp.id }, { stock: inp.stock });
      });
      const updatedStock = next.stock.find(x => x.warehouseId === whId && x.productId === r.productId);
      if (updatedStock) persist.upsert("stock", updatedStock);
      persist.insert("stockMovements", next.stockMovements[0]);
      persist.insert("finance", finance[0]);
      return { ...s, inputs, stock: next.stock, stockMovements: next.stockMovements, finance };
    });
    pushToast("Produção registrada — estoque e insumos atualizados");
  };

  const recordSaida = ({ picolezeiroId, beaches, items }) => {
    const tripId = uid("t");
    setState(s => {
      // reserve stock (decrement) do armazém de Picolezeiros
      let working = { ...s };
      Object.entries(items).forEach(([pid, qty]) => {
        if (!qty) return;
        const next = applyStockDelta(working, WH_PICOLE_ID, pid, -qty, {
          kind: "saida", refType: "trip", refId: tripId,
        });
        working = { ...working, ...next };
      });
      const trip = {
        id: tripId,
        picolezeiroId, date: new Date().toISOString().slice(0,10),
        status: "em_rota",
        beachesPlanned: beaches, beachesActual: null,
        items: Object.entries(items).map(([productId, qty]) => ({ productId, qty })),
        sold: null, returned: null, faturado: null, comissao: null, recebido: null,
      };
      // persist
      Object.keys(items).forEach(pid => {
        const row = working.stock.find(x => x.warehouseId === WH_PICOLE_ID && x.productId === pid);
        if (row) persist.upsert("stock", row);
      });
      working.stockMovements.slice(0, Object.keys(items).length).forEach(mv => persist.insert("stockMovements", mv));
      persist.insert("trips", trip);
      return { ...s, stock: working.stock, stockMovements: working.stockMovements, trips: [trip, ...s.trips] };
    });
    pushToast("Saída registrada — unidades reservadas no estoque");
  };

  const closeRetorno = ({ tripId, sold, beachesActual, received }) => {
    setState(s => {
      const trip = s.trips.find(t => t.id === tripId);
      if (!trip) return s;
      const pz = s.picolezeiroos || s.picolezeiros;
      const pic = (pz || s.picolezeiros).find(p => p.id === trip.picolezeiroId);

      // Return leftovers ao armazém de Picolezeiros
      let working = { ...s };
      const returned = trip.items.map(it => {
        const soldQ = sold[it.productId] || 0;
        const rest = it.qty - soldQ;
        if (rest > 0) {
          const next = applyStockDelta(working, WH_PICOLE_ID, it.productId, rest, {
            kind: "retorno", refType: "trip", refId: tripId,
          });
          working = { ...working, ...next };
        }
        return { productId: it.productId, qty: rest };
      });
      let stock = working.stock;
      let stockMovements = working.stockMovements;

      // Calculate faturado, comissao
      const soldArr = Object.entries(sold).map(([productId, qty]) => ({ productId, qty }));
      const faturado = soldArr.reduce((sm, it) => {
        const p = s.products.find(x => x.id === it.productId);
        return sm + it.qty * (p?.price || 0);
      }, 0);
      const comissao = faturado * (pic.commission / 100);

      const trips = s.trips.map(t => t.id === tripId ? {
        ...t,
        status: "fechado",
        sold: soldArr, returned, beachesActual,
        faturado, comissao, recebido: received,
      } : t);

      // Two finance entries: in (faturado), out (comissao)
      const today = new Date().toISOString().slice(0,10);
      const finance = [
        { id: uid("f"), date: today, kind:"in",  cat:"Retorno Picolezeiro", ref: pic.name, value: faturado, status:"confirmado" },
        { id: uid("f"), date: today, kind:"out", cat:"Comissão Picolezeiro", ref: pic.name, value: comissao, status:"confirmado" },
        ...s.finance,
      ];

      // Bump picolezeiro totals
      const picolezeiros = s.picolezeiros.map(p => p.id === pic.id ? {
        ...p, totals: {
          saidas:   p.totals.saidas + 1,
          vendido:  p.totals.vendido + faturado,
          comissao: p.totals.comissao + comissao,
        }
      } : p);

      // persist
      working.stock.forEach(row => {
        if (row.warehouseId === WH_PICOLE_ID && trip.items.some(it => it.productId === row.productId)) {
          persist.upsert("stock", row);
        }
      });
      working.stockMovements.slice(0, returned.filter(r => r.qty > 0).length).forEach(mv => persist.insert("stockMovements", mv));
      persist.update("trips", { id: tripId }, trips.find(t => t.id === tripId));
      persist.insert("finance", finance[0]);
      persist.insert("finance", finance[1]);
      const pcRow = picolezeiros.find(p => p.id === pic.id);
      if (pcRow) persist.update("picolezeiros", { id: pic.id }, { totals: pcRow.totals });
      return { ...s, stock, stockMovements, trips, finance, picolezeiros };
    });
    pushToast("Retorno fechado · sobras devolvidas · 2 lançamentos gerados");
  };

  const actions = {
    // generic
    addBeach:        (item) => { addTo("beaches", item, "b"); pushToast("Praia cadastrada"); },
    updateBeach:     (id, patch) => { updateIn("beaches", id, patch); pushToast("Praia atualizada"); },
    deleteBeach:     (id) => { removeFrom("beaches", id); pushToast("Praia excluída"); },

    addPicolezeiro:    (item) => { addTo("picolezeiros", { ...item, totals:{saidas:0,vendido:0,comissao:0} }, "pz"); pushToast("Picolezeiro cadastrado"); },
    updatePicolezeiro: (id, patch) => { updateIn("picolezeiros", id, patch); pushToast("Picolezeiro atualizado"); },
    deletePicolezeiro: (id) => { removeFrom("picolezeiros", id); pushToast("Picolezeiro excluído"); },

    addDistributor:    (item) => { addTo("distributors", { ...item, totals:{compras:0,valor:0} }, "d"); pushToast("Distribuidor cadastrado"); },
    updateDistributor: (id, patch) => { updateIn("distributors", id, patch); pushToast("Distribuidor atualizado"); },
    deleteDistributor: (id) => { removeFrom("distributors", id); pushToast("Distribuidor excluído"); },

    addInput:    (item) => { addTo("inputs", item, "i"); pushToast("Insumo cadastrado"); },
    updateInput: (id, patch) => { updateIn("inputs", id, patch); pushToast("Insumo atualizado"); },
    deleteInput: (id) => { removeFrom("inputs", id); pushToast("Insumo excluído"); },

    addRecipe:    (item) => { addTo("recipes", item, "r"); pushToast("Receita criada"); },
    updateRecipe: (id, patch) => { updateIn("recipes", id, patch); pushToast("Receita atualizada"); },
    deleteRecipe: (id) => { removeFrom("recipes", id); pushToast("Receita excluída"); },

    addProduct:    (item) => { addTo("products", item, "p"); pushToast("Sabor cadastrado"); },
    updateProduct: (id, patch) => { updateIn("products", id, patch); pushToast("Sabor atualizado"); },
    deleteProduct: (id) => { removeFrom("products", id); pushToast("Sabor excluído"); },

    addStaff:    (item) => { addTo("staff", item, "u"); pushToast("Usuário criado"); },
    updateStaff: (id, patch) => { updateIn("staff", id, patch); pushToast("Usuário atualizado"); },
    deleteStaff: (id) => { removeFrom("staff", id); pushToast("Usuário removido"); },

    addFinance:    (item) => { addTo("finance", item, "f"); pushToast("Lançamento criado"); },
    updateFinance: (id, patch) => { updateIn("finance", id, patch); pushToast("Lançamento atualizado"); },
    deleteFinance: (id) => { removeFrom("finance", id); pushToast("Lançamento excluído"); },

    addSalePreset:    (item) => { addTo("salePresets", item, "preset"); pushToast("Lote pré-montado salvo"); },
    deleteSalePreset: (id) => { removeFrom("salePresets", id); pushToast("Lote removido"); },

    adjustStock,
    setStockMin: (productId, min, warehouseId) => setState(s => {
      const stock = s.stock.map(x =>
        (x.productId === productId && (!warehouseId || x.warehouseId === warehouseId))
          ? { ...x, minUnits: min } : x);
      // persist
      stock.forEach((row, i) => {
        if (row.minUnits !== s.stock[i].minUnits) persist.upsert("stock", row);
      });
      return { ...s, stock };
    }),

    cancelTrip: (tripId) => {
      setState(s => {
        const trip = s.trips.find(t => t.id === tripId);
        if (!trip) return s;
        // Return reserved units ao armazém de Picolezeiros
        let working = { ...s };
        trip.items.forEach(it => {
          const next = applyStockDelta(working, WH_PICOLE_ID, it.productId, it.qty, {
            kind: "cancel-saida", refType: "trip", refId: tripId,
          });
          working = { ...working, ...next };
        });
        // persist
        working.stock.forEach(row => {
          if (row.warehouseId === WH_PICOLE_ID && trip.items.some(it => it.productId === row.productId)) {
            persist.upsert("stock", row);
          }
        });
        working.stockMovements.slice(0, trip.items.length).forEach(mv => persist.insert("stockMovements", mv));
        persist.remove("trips", { id: tripId });
        return { ...s, stock: working.stock, stockMovements: working.stockMovements, trips: s.trips.filter(t => t.id !== tripId) };
      });
      pushToast("Saída cancelada · estoque restaurado");
    },

    createWarehouse, renameWarehouse, deleteWarehouse, transferStock,

    updateCompany: (patch) => {
      setState(s => ({ ...s, company: { ...s.company, ...patch } }));
      if (Object.prototype.hasOwnProperty.call(patch, "logo")) {
        try { if (patch.logo) localStorage.setItem("otp.logo", patch.logo); else localStorage.removeItem("otp.logo"); } catch(e){}
      }
      persist.updateSingleRow("company", patch);
      pushToast("Dados da empresa salvos");
    },
    updateSettings: (patch) => {
      setState(s => ({ ...s, settings: { ...s.settings, ...patch } }));
      persist.updateSingleRow("settings", patch);
      pushToast("Parâmetros salvos");
    },

    // business
    finalizeDistribSale,
    receiveSalePayment,
    recordInputPurchase,
    payInputPurchase,
    recordExpense,
    payExpense,
    recordProduction,
    recordSaida,
    closeRetorno,
  };

  const ctx = { state, actions, pushToast, askConfirm, goTo: setTab };

  const current = NAV_ITEMS.find(n => n.id === tab);
  const emRotaCount = state.trips.filter(t => t.status === "em_rota").length;
  const navBadges = { "pdv-picolezeiros": emRotaCount };

  if (!authReady) {
    return (
      <div style={{ display:"flex", alignItems:"center", justifyContent:"center", height:"100vh", background:"var(--canvas, #FBFAF9)" }}>
        <Logo size={48} />
      </div>
    );
  }

  // Se Supabase está ativo e não há sessão → tela de login
  if (window.supa && session === null) {
    return <LoginScreen onLoggedIn={() => { /* setSession() acontece via onAuthStateChange */ }} />;
  }

  if (loading) {
    return (
      <div style={{ display:"flex", alignItems:"center", justifyContent:"center", height:"100vh", background:"var(--canvas, #FBFAF9)" }}>
        <div style={{ textAlign:"center" }}>
          <Logo size={64} />
          <div style={{ marginTop: 18, fontFamily:"var(--font-display)", fontSize:18, color:"var(--ink-700)" }}>
            Carregando dados do banco…
          </div>
          <div className="subtle" style={{ marginTop: 6, fontSize: 12 }}>
            Conectando ao Supabase
          </div>
        </div>
      </div>
    );
  }

  /* Identificar staff logado pelo email da sessão */
  const currentStaff = session ? state.staff.find(s => s.email === session.user?.email) : null;
  const currentLabel = currentStaff?.name || session?.user?.email || "Renata Cardoso";
  const currentRole  = currentStaff?.role || (session ? "Usuário" : "Admin Super");
  const currentAvatar = currentStaff?.photo
    || (currentStaff?.name ? currentStaff.name.split(/\s+/).slice(0,2).map(w => w[0]).join("").toUpperCase()
        : (session?.user?.email ? session.user.email[0].toUpperCase() : "RC"));

  const handleLogout = async () => {
    if (!window.supa) return;
    await window.supa.auth.signOut();
  };

  const goTab = (id) => { setTab(id); setMobileNav(false); };

  return (
    <div className="app" data-screen-label={"App / " + (current?.label || "")}>
      <div className={"sidebar-overlay" + (mobileNav ? " is-open" : "")} onClick={() => setMobileNav(false)} />
      <aside className={"sidebar" + (mobileNav ? " is-open" : "")}>
        <div className="sidebar__brand">
          <Logo size={32} />
          <div className="wm">
            <strong>{state.company.name}</strong>
            <small>Gestão</small>
          </div>
        </div>

        <div className="nav__label">Operação</div>
        {NAV_ITEMS.slice(0, 6).map(it => {
          const Ic = it.icon;
          const badge = navBadges[it.id];
          return (
            <button key={it.id} className={"nav__item" + (tab === it.id ? " is-active" : "")} onClick={() => goTab(it.id)}>
              <Ic />
              {it.label}
              {badge ? <span className="badge">{badge}</span> : null}
            </button>
          );
        })}

        <div className="nav__label">Análise</div>
        {NAV_ITEMS.slice(6, 8).map(it => {
          const Ic = it.icon;
          return (
            <button key={it.id} className={"nav__item" + (tab === it.id ? " is-active" : "")} onClick={() => goTab(it.id)}>
              <Ic />
              {it.label}
            </button>
          );
        })}

        <div className="nav__label">Sistema</div>
        {NAV_ITEMS.slice(8).map(it => {
          const Ic = it.icon;
          return (
            <button key={it.id} className={"nav__item" + (tab === it.id ? " is-active" : "")} onClick={() => goTab(it.id)}>
              <Ic />
              {it.label}
            </button>
          );
        })}

        <div className="sidebar__foot">
          <div className="avatar">{currentAvatar}</div>
          <div className="user-meta">
            <strong>{currentLabel}</strong>
            <small>{currentRole}</small>
          </div>
          <button className="icon-btn" style={{ marginLeft:"auto" }} title={session ? "Sair" : "Logout indisponível (sem auth)"} onClick={handleLogout} disabled={!session}>
            <svg width="16" height="16" viewBox="0 0 20 20" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"><path d="M12 4h3a1 1 0 011 1v10a1 1 0 01-1 1h-3"/><path d="M9 14l4-4-4-4M3 10h10"/></svg>
          </button>
        </div>
      </aside>

      <main className="main">
        <div className="topbar">
          <button className="menu-btn" title="Menu" onClick={() => setMobileNav(v => !v)}>
            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><path d="M3 6h18M3 12h18M3 18h18"/></svg>
          </button>
          <div className="crumbs">
            <span>{state.company.name}</span>
            <span>/</span>
            <strong>{current?.label}</strong>
          </div>
          <div className="topbar__search">
            <Icons.Search size={15} />
            <input placeholder="Buscar produto, cliente, picolezeiro…" />
            <span className="kbd">⌘K</span>
          </div>
          <div className="topbar__actions">
            <button className="icon-btn" title="Notificações"><Icons.Bell /><span className="dot" /></button>
            <button className="icon-btn" title="Calendário"><Icons.Calendar /></button>
            <div style={{ width: 1, height: 22, background: "var(--line)" }} />
          </div>
        </div>

        <div className="content">
          {tab === "dashboard" && <Dashboard ctx={ctx} />}
          {tab === "fabricacao" && <Fabricacao ctx={ctx} />}
          {tab === "pdv-distribuidores" && <PdvDistribuidores ctx={ctx} />}
          {tab === "pdv-picolezeiros" && <PdvPicolezeiros ctx={ctx} />}
          {tab === "clientes" && <Clientes ctx={ctx} />}
          {tab === "estoque" && <Estoque ctx={ctx} />}
          {tab === "movimentacoes" && <Movimentacoes ctx={ctx} />}
          {tab === "relatorios" && <Relatorios ctx={ctx} />}
          {tab === "configuracoes" && <Configuracoes ctx={ctx} />}
        </div>
      </main>

      <Toast message={toast} onDone={() => setToast(null)} />

      {confirm && (
        <Modal title={confirm.title || "Confirmar"} onClose={() => setConfirm(null)}
          footer={
            <>
              <Button variant="ghost" onClick={() => setConfirm(null)}>Cancelar</Button>
              <Button variant="danger" icon={<Icons.Trash size={14}/>} onClick={() => { confirm.onConfirm(); setConfirm(null); }}>
                {confirm.danger ? "Excluir" : "Confirmar"}
              </Button>
            </>
          }
        >
          <div style={{ display:"flex", gap:14, alignItems:"flex-start" }}>
            <div style={{ width:40, height:40, borderRadius:10, background:"var(--danger-bg)", color:"var(--danger)", display:"flex", alignItems:"center", justifyContent:"center", flexShrink:0 }}>
              <Icons.AlertTriangle />
            </div>
            <div>
              <p style={{ margin:"0 0 4px", fontSize:13.5, color:"var(--ink-900)" }}>{confirm.message}</p>
              {confirm.detail && <p className="muted" style={{ margin:0, fontSize:12.5 }}>{confirm.detail}</p>}
            </div>
          </div>
        </Modal>
      )}
    </div>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<App />);
