/* global React, LogoMark, Icon, Factory */
const LEDGER_STORAGE_KEY = "break_accounting_entries_v1";
const PRODUCT_STORAGE_KEY = "break_products_v1";
const ACC_FACTORY_STORAGE_KEY = "break_factory_v2";
const ACC_FACTORY_META_KEY = "break_factory_meta_v2";

const LEDGER_TYPES = [
  { id: "venta", label: "Venta", sign: 1, tone: "in" },
  { id: "produccion", label: "Produccion", sign: 0, tone: "stock" },
  { id: "consumo", label: "Consumo", sign: 0, tone: "stock-out" },
  { id: "inversion", label: "Inversion", sign: 1, tone: "capital" },
  { id: "insumos", label: "Insumos", sign: -1, tone: "out" },
  { id: "arqueo", label: "Arqueo", sign: 0, tone: "audit" },
  { id: "gasto", label: "Gasto", sign: -1, tone: "out" },
  { id: "retiro", label: "Retiro", sign: -1, tone: "withdraw" },
];

const STOCK_TYPES = new Set(["produccion", "consumo"]);
const STOCK_TARGETS = [
  { id: "none", label: "Sin ajuste de stock" },
  { id: "productos", label: "Productos" },
  { id: "insumos", label: "Insumos" },
];
const STOCK_EFFECTS = [
  { id: "in", label: "Sobra / entra", sign: 1 },
  { id: "out", label: "Falta / sale", sign: -1 },
];
const CASH_DIRECTIONS = [
  { id: "none", label: "Sin ajuste de caja", sign: 0 },
  { id: "in", label: "Sobra caja", sign: 1 },
  { id: "out", label: "Falta caja", sign: -1 },
];

const LEDGER_CATEGORIES = {
  venta: ["Sandwiches", "Bebidas", "Combos", "Delivery"],
  produccion: ["Produccion diaria", "Reposicion", "Prueba", "Ajuste stock"],
  consumo: ["Uso interno", "Prueba", "Merma", "Ajuste stock"],
  inversion: ["Aporte inicial", "Equipos", "Capital de trabajo", "Mejora produccion"],
  insumos: ["Compra insumos", "Pan", "Proteina", "Verduras", "Bebidas", "Envases"],
  arqueo: ["Ajuste caja", "Ajuste productos", "Ajuste insumos", "Merma", "Diferencia inventario"],
  gasto: ["Transporte", "Gas", "Permisos", "Reparacion", "Limpieza", "Marketing"],
  retiro: ["Retiro socio", "Pago equipo", "Adelanto"],
};

const LEDGER_METHODS = ["Pix", "Efectivo", "Tarjeta", "Transferencia", "Ifood", "Mixto", "Otro"];
const DEFAULT_PRODUCTS = [
  { id: "focaccia", name: "Focaccia", unit: "un" },
  { id: "baguette", name: "Baguette", unit: "un" },
  { id: "ciabatta", name: "Ciabatta", unit: "un" },
  { id: "sandwich-de-miga", name: "Sandwich de miga", unit: "un" },
];
const ACC_FALLBACK_INSUMOS = [
  { id: "harina", name: "Harina 000", unit: "kg", start: 30, pack: 25, max: 75 },
  { id: "agua", name: "Agua filtrada", unit: "L", start: 40, pack: 20, max: 80 },
  { id: "levadura", name: "Levadura fresca", unit: "g", start: 300, pack: 500, max: 1000 },
  { id: "sal", name: "Sal marina", unit: "g", start: 800, pack: 1000, max: 3000 },
  { id: "aceite", name: "Aceite de oliva", unit: "ml", start: 1500, pack: 1000, max: 5000 },
  { id: "romero", name: "Romero fresco", unit: "g", start: 120, pack: 100, max: 400 },
  { id: "cebolla", name: "Cebolla", unit: "kg", start: 5, pack: 5, max: 15 },
  { id: "tomate-cherry", name: "Tomate cherry", unit: "kg", start: 3, pack: 3, max: 10 },
  { id: "fiambre", name: "Fiambre", unit: "kg", start: 5, pack: 5, max: 15 },
  { id: "envases", name: "Envases", unit: "un", start: 100, pack: 100, max: 400 },
];

const BRL = new Intl.NumberFormat("pt-BR", { style: "currency", currency: "BRL" });
const USD = new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" });
const NUM2 = new Intl.NumberFormat("es-AR", { maximumFractionDigits: 2, minimumFractionDigits: 0 });

function round2(value) {
  const number = Number(value);
  if (!Number.isFinite(number)) return 0;
  return Math.round(number * 100) / 100;
}

function fmtNum(value) {
  return NUM2.format(round2(value));
}

function fmtPct(value) {
  return `${fmtNum(value)}%`;
}

const METHOD_TONE = {
  Pix: "pix",
  Efectivo: "cash",
  Tarjeta: "card",
  Transferencia: "wire",
  Ifood: "ifood",
  Mixto: "mix",
  Otro: "neutral",
};

function methodTone(method) {
  return METHOD_TONE[method] || "neutral";
}

function todayISO() {
  const now = new Date();
  const local = new Date(now.getTime() - now.getTimezoneOffset() * 60000);
  return local.toISOString().slice(0, 10);
}

function monthLabel(value) {
  if (value === "all") return "Todo";
  const [year, month] = value.split("-").map(Number);
  return new Date(year, month - 1, 1).toLocaleDateString("es-AR", { month: "short", year: "numeric" });
}

function dateLabel(value) {
  if (!value) return "";
  return new Date(`${value}T12:00:00`).toLocaleDateString("es-AR", { day: "2-digit", month: "short" });
}

function ledgerType(id) {
  return LEDGER_TYPES.find((type) => type.id === id) || LEDGER_TYPES[0];
}

function productIdFromName(name) {
  let s = String(name || "")
    .normalize("NFD")
    .replace(/[\u0300-\u036f]/g, "")
    .toLowerCase();
    
  if (s.includes("miga")) s = "sandwich de miga";
  if (s.includes("ciaba") || s.includes("chia") || s.includes("chaba")) s = "ciabatta";
  if (s.includes("bagu") || s.includes("vagu")) s = "baguette";
  if (s.includes("foca") || s.includes("focca")) s = "focaccia";
  
  return s.replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
}

function insumoIdFromName(name) {
  return String(name || "")
    .normalize("NFD")
    .replace(/[\u0300-\u036f]/g, "")
    .toLowerCase()
    .replace(/[^a-z0-9]+/g, "-")
    .replace(/^-|-$/g, "");
}

function productNameFromId(id, products) {
  return products.find((product) => product.id === id)?.name || "";
}

function normalizeProduct(product) {
  const name = String(product.name || "").trim();
  const id = product.id || productIdFromName(name);
  return { id, name: name || id, unit: product.unit || "un" };
}

function parseMoney(value) {
  if (typeof value === "number") return Number.isFinite(value) ? value : 0;
  const text = String(value || "").trim();
  if (!text) return 0;
  const normalized = text.includes(",") ? text.replace(/\./g, "").replace(",", ".") : text;
  const number = Number.parseFloat(normalized);
  return Number.isFinite(number) ? number : 0;
}

function asArray(value) {
  return Array.isArray(value) ? value : [];
}

function normalizePayment(payment, totalAmount = 0) {
  const safePayment = payment || {};
  const percent = Math.max(0, parseMoney(safePayment.percent));
  const explicitAmount = parseMoney(safePayment.amount);
  return {
    method: LEDGER_METHODS.includes(safePayment.method) ? safePayment.method : "Otro",
    percent,
    amount: explicitAmount > 0 ? explicitAmount : (percent > 0 && totalAmount > 0 ? totalAmount * percent / 100 : 0),
  };
}

function normalizePriceLine(line, totalQty = 0) {
  const safeLine = line || {};
  const percent = Math.max(0, parseMoney(safeLine.percent));
  const qty = parseMoney(safeLine.qty) || (percent > 0 && totalQty > 0 ? totalQty * percent / 100 : 0);
  const unitPrice = parseMoney(safeLine.unitPrice);
  const amount = parseMoney(safeLine.amount) || (qty > 0 && unitPrice > 0 ? qty * unitPrice : 0);
  return {
    product: String(safeLine.product || "").trim(),
    qty,
    percent,
    unitPrice,
    amount,
  };
}

function normalizeInsumoLine(line, totalQty = 0) {
  const safeLine = line || {};
  const percent = Math.max(0, parseMoney(safeLine.percent));
  const qty = parseMoney(safeLine.qty) || (percent > 0 && totalQty > 0 ? totalQty * percent / 100 : 0);
  const insumoName = String(safeLine.insumoName || safeLine.name || safeLine.product || "").trim();
  return {
    insumoId: String(safeLine.insumoId || insumoIdFromName(insumoName)).trim(),
    insumoName,
    qty,
    percent,
    unit: String(safeLine.unit || "").trim(),
    amount: parseMoney(safeLine.amount),
  };
}

function cashDirectionSign(direction) {
  return CASH_DIRECTIONS.find((item) => item.id === direction)?.sign || 0;
}

function stockEffectSign(effect) {
  return STOCK_EFFECTS.find((item) => item.id === effect)?.sign || -1;
}

function isValidStockTarget(value) {
  return STOCK_TARGETS.some((target) => target.id === value);
}

function loadFactoryInsumosFallback() {
  try {
    const savedMeta = localStorage.getItem(ACC_FACTORY_META_KEY);
    const meta = savedMeta ? JSON.parse(savedMeta) : { insumos: ACC_FALLBACK_INSUMOS };
    const insumos = Array.isArray(meta.insumos) && meta.insumos.length ? meta.insumos : ACC_FALLBACK_INSUMOS;
    const savedState = localStorage.getItem(ACC_FACTORY_STORAGE_KEY);
    const state = savedState ? JSON.parse(savedState) : {};
    const savedStock = state.insumos || {};
    return insumos.map((def) => ({
      ...def,
      stock: typeof savedStock[def.id] === "number" ? savedStock[def.id] : (Number(def.start) || 0),
    }));
  } catch {
    return ACC_FALLBACK_INSUMOS.map((def) => ({ ...def, stock: Number(def.start) || 0 }));
  }
}

function loadFactoryInsumosForAccounting() {
  if (window.BreakFactoryApi && typeof window.BreakFactoryApi.insumoSnapshot === "function") {
    return window.BreakFactoryApi.insumoSnapshot();
  }
  return loadFactoryInsumosFallback();
}

function applyFactoryInsumoDeltasForAccounting(deltas) {
  const cleanDeltas = Array.isArray(deltas)
    ? deltas
        .map((delta) => ({ id: String(delta.id || "").trim(), qty: Number(delta.qty) || 0 }))
        .filter((delta) => delta.id && delta.qty !== 0)
    : [];
  if (!cleanDeltas.length) return loadFactoryInsumosForAccounting();
  if (window.BreakFactoryApi && typeof window.BreakFactoryApi.applyInsumoDeltas === "function") {
    return window.BreakFactoryApi.applyInsumoDeltas(cleanDeltas);
  }

  const snapshot = loadFactoryInsumosFallback();
  const byId = Object.fromEntries(snapshot.map((insumo) => [insumo.id, Number(insumo.stock) || 0]));
  cleanDeltas.forEach((delta) => {
    byId[delta.id] = Math.max(0, (byId[delta.id] || 0) + delta.qty);
  });
  try {
    const savedState = localStorage.getItem(ACC_FACTORY_STORAGE_KEY);
    const state = savedState ? JSON.parse(savedState) : {};
    localStorage.setItem(ACC_FACTORY_STORAGE_KEY, JSON.stringify({ ...state, insumos: byId }));
  } catch {}
  return loadFactoryInsumosFallback();
}

function describePayment(payment) {
  const safePayment = normalizePayment(payment);
  const parts = [safePayment.method];
  if (safePayment.amount > 0) parts.push(BRL.format(round2(safePayment.amount)));
  else if (safePayment.percent > 0) parts.push(fmtPct(safePayment.percent));
  return parts.join(" ");
}

function describePayments(payments, fallbackMethod) {
  const paymentList = asArray(payments);
  if (!paymentList.length) return fallbackMethod || "";
  if (paymentList.length === 1) return describePayment(paymentList[0]);
  return paymentList.map(describePayment).join(" · ");
}

function describePriceLine(line) {
  const parts = [];
  if (line.qty) parts.push(`${fmtNum(line.qty)}×`);
  if (line.product) parts.push(line.product);
  if (line.unitPrice) parts.push(`@ ${BRL.format(round2(line.unitPrice))}`);
  else if (line.amount) parts.push(BRL.format(round2(line.amount)));
  return parts.join(" ");
}

function describePriceLines(priceLines) {
  const lines = asArray(priceLines);
  if (!lines.length) return "";
  return lines.map(describePriceLine).join(" · ");
}

function describeInsumoLine(line, insumos = []) {
  const safeLine = normalizeInsumoLine(line);
  const found = insumos.find((insumo) => insumo.id === safeLine.insumoId);
  const name = found?.name || safeLine.insumoName || safeLine.insumoId;
  const unit = safeLine.unit || found?.unit || "";
  const parts = [];
  if (safeLine.qty) parts.push(`${fmtNum(safeLine.qty)} ${unit}`.trim());
  if (name) parts.push(name);
  return parts.join(" ");
}

function describeInsumoLines(insumoLines, insumos = []) {
  const lines = asArray(insumoLines);
  if (!lines.length) return "";
  return lines.map((line) => describeInsumoLine(line, insumos)).join(" · ");
}

function cashSignForEntry(entry) {
  if (entry.type === "arqueo") return cashDirectionSign(entry.cashDirection);
  return ledgerType(entry.type).sign;
}

function signedAmount(entry) {
  return cashSignForEntry(entry) * Number(entry.amount || 0);
}

function makeLedgerId() {
  return `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 8)}`;
}

function defaultLedgerForm(type = "venta") {
  return {
    date: todayISO(),
    type,
    category: LEDGER_CATEGORIES[type][0],
    description: "",
    product: "",
    qty: "",
    unitPrice: "",
    amount: "",
    usd: "",
    fx: "",
    method: "Pix",
    payments: [],
    priceLines: [],
    insumoLines: [],
    stockTarget: type === "insumos" ? "insumos" : "productos",
    stockEffect: type === "produccion" || type === "insumos" ? "in" : "out",
    cashDirection: "none",
    responsible: "Equipo Break",
    party: "",
    receipt: false,
  };
}

function seedLedgerEntries() {
  return [{
    id: "seed-investment-alan",
    date: todayISO(),
    type: "inversion",
    category: "Aporte inicial",
    description: "Aporte Alan",
    product: "",
    qty: 1,
    unitPrice: "",
    amount: 26500,
    usd: 5000,
    fx: 5.30,
    method: "Transferencia",
    responsible: "Alan",
    party: "",
    receipt: true,
    stockTarget: "none",
    stockEffect: "in",
    cashDirection: "none",
    createdAt: new Date().toISOString(),
  }];
}

function normalizeEntry(entry) {
  const safeEntry = entry || {};
  const typeId = LEDGER_CATEGORIES[safeEntry.type] ? safeEntry.type : "venta";
  const qty = parseMoney(safeEntry.qty);
  const amount = parseMoney(safeEntry.amount);
  const stockTarget = isValidStockTarget(safeEntry.stockTarget)
    ? safeEntry.stockTarget
    : (typeId === "insumos" ? "insumos" : (typeId === "arqueo" ? "productos" : "productos"));
  const stockEffect = STOCK_EFFECTS.some((effect) => effect.id === safeEntry.stockEffect)
    ? safeEntry.stockEffect
    : (typeId === "produccion" || typeId === "insumos" ? "in" : "out");
  const cashDirection = CASH_DIRECTIONS.some((direction) => direction.id === safeEntry.cashDirection)
    ? safeEntry.cashDirection
    : "none";
  return {
    id: safeEntry.id || makeLedgerId(),
    date: safeEntry.date || todayISO(),
    type: typeId,
    category: safeEntry.category || LEDGER_CATEGORIES[typeId][0],
    description: safeEntry.description || "",
    product: safeEntry.product || "",
    qty,
    unitPrice: parseMoney(safeEntry.unitPrice),
    amount,
    usd: parseMoney(safeEntry.usd),
    fx: parseMoney(safeEntry.fx),
    method: safeEntry.method || "Pix",
    payments: asArray(safeEntry.payments).map((payment) => normalizePayment(payment, amount)).filter((payment) => payment.method || payment.amount > 0 || payment.percent > 0),
    priceLines: asArray(safeEntry.priceLines).map((line) => normalizePriceLine(line, qty)).filter((line) => line.product || line.qty > 0 || line.amount > 0),
    insumoLines: asArray(safeEntry.insumoLines).map((line) => normalizeInsumoLine(line, qty)).filter((line) => line.insumoId || line.insumoName || line.qty > 0),
    stockTarget,
    stockEffect,
    cashDirection,
    responsible: safeEntry.responsible || "",
    party: safeEntry.party || "",
    receipt: Boolean(safeEntry.receipt),
    createdAt: safeEntry.createdAt || new Date().toISOString(),
  };
}

function loadLedgerEntries() {
  try {
    const saved = localStorage.getItem(LEDGER_STORAGE_KEY);
    if (!saved) return seedLedgerEntries();
    const parsed = JSON.parse(saved);
    return Array.isArray(parsed)
      ? parsed.map(normalizeEntry).filter(entryHasContent)
      : seedLedgerEntries();
  } catch {
    return seedLedgerEntries();
  }
}

function loadProducts() {
  try {
    const saved = localStorage.getItem(PRODUCT_STORAGE_KEY);
    if (!saved) return DEFAULT_PRODUCTS;
    const parsed = JSON.parse(saved);
    const merged = [...DEFAULT_PRODUCTS, ...(Array.isArray(parsed) ? parsed : [])].map(normalizeProduct);
    return [...new Map(merged.filter((product) => product.id).map((product) => [product.id, product])).values()];
  } catch {
    return DEFAULT_PRODUCTS;
  }
}

function resolveFormAmount(form) {
  const amount = parseMoney(form.amount);
  if (amount > 0) return amount;
  const lineAmount = Array.isArray(form.priceLines) ? form.priceLines.reduce((sum, line) => sum + parseMoney(line.amount), 0) : 0;
  if (lineAmount > 0) return lineAmount;
  const qty = parseMoney(form.qty);
  const unit = parseMoney(form.unitPrice);
  if (qty > 0 && unit > 0) return qty * unit;
  const usd = parseMoney(form.usd);
  const fx = parseMoney(form.fx);
  if (usd > 0 && fx > 0) return usd * fx;
  return 0;
}

function computeLedgerMetrics(entries) {
  return entries.reduce((acc, entry) => {
    const amount = Number(entry.amount || 0);
    const sign = cashSignForEntry(entry);
    const signedAmt = sign * amount;

    if (entry.type === "venta") acc.sales += amount;
    if (entry.type === "insumos" || entry.type === "gasto") acc.expenses += amount;
    if (entry.type === "inversion") acc.investment += amount;
    if (entry.type === "retiro") acc.withdrawals += amount;
    acc.cash += signedAmt;
    acc.result = acc.sales - acc.expenses;

    if (sign !== 0) {
      if (entry.payments && entry.payments.length > 0) {
        entry.payments.forEach(p => {
          if (p.method && p.method !== "Otro" && p.method !== "Mixto") {
            acc.methods[p.method] = (acc.methods[p.method] || 0) + (sign * Number(p.amount || 0));
          }
        });
      } else if (entry.method && entry.method !== "Otro" && entry.method !== "Mixto") {
        acc.methods[entry.method] = (acc.methods[entry.method] || 0) + signedAmt;
      }
    }

    return acc;
  }, { cash: 0, sales: 0, expenses: 0, investment: 0, withdrawals: 0, result: 0, methods: {} });
}

function productStockSignForEntry(entry) {
  if (entry.type === "produccion") return 1;
  if (entry.type === "venta" || entry.type === "consumo") return -1;
  if (entry.type === "arqueo" && entry.stockTarget === "productos") return stockEffectSign(entry.stockEffect);
  return 0;
}

function stockMovementsForEntry(entry) {
  const sign = productStockSignForEntry(entry);
  if (!sign) return [];
  const priceLines = asArray(entry.priceLines);
  const lines = priceLines.length ? priceLines : [{ product: entry.product, qty: entry.qty }];
  return lines
    .map((line) => ({
      productId: productIdFromName(line.product || entry.product),
      product: line.product || entry.product,
      qty: sign * parseMoney(line.qty),
      entry,
    }))
    .filter((movement) => movement.productId && movement.qty !== 0);
}

function insumoMovementsForEntry(entry) {
  let sign = 0;
  if (entry.type === "insumos") sign = 1;
  if (entry.type === "arqueo" && entry.stockTarget === "insumos") sign = stockEffectSign(entry.stockEffect);
  if (!sign) return [];
  return asArray(entry.insumoLines)
    .map((line) => ({
      insumoId: line.insumoId || insumoIdFromName(line.insumoName),
      insumoName: line.insumoName,
      qty: sign * parseMoney(line.qty),
      entry,
    }))
    .filter((movement) => movement.insumoId && movement.qty !== 0);
}

function factoryDeltasForEntry(entry, multiplier = 1) {
  return insumoMovementsForEntry(entry).map((movement) => ({
    id: movement.insumoId,
    qty: movement.qty * multiplier,
  }));
}

function entryHasContent(entry) {
  return Number(entry.amount || 0) > 0
    || stockMovementsForEntry(entry).length > 0
    || insumoMovementsForEntry(entry).length > 0;
}

function computeStock(entries, products) {
  const stock = Object.fromEntries(products.map((product) => [product.id, 0]));
  for (const entry of entries) {
    for (const movement of stockMovementsForEntry(entry)) {
      stock[movement.productId] = (stock[movement.productId] || 0) + movement.qty;
    }
  }
  return stock;
}

function productStockDetail(entry) {
  if (!stockMovementsForEntry(entry).length) return "";
  return entry.priceLines && entry.priceLines.length > 0
    ? describePriceLines(entry.priceLines)
    : describePriceLine({ product: entry.product, qty: entry.qty });
}

function totalProductMovementQty(entry) {
  return stockMovementsForEntry(entry).reduce((sum, movement) => sum + Math.abs(movement.qty), 0);
}

function entryDetailText(entry, insumos = []) {
  const parts = [];
  const cashSign = cashSignForEntry(entry);
  if (cashSign !== 0 && Number(entry.amount || 0) > 0) {
    parts.push(describePayments(entry.payments, entry.method));
  }
  const productText = productStockDetail(entry);
  if (productText) parts.push(productText);
  const insumoText = describeInsumoLines(entry.insumoLines, insumos);
  if (insumoText) parts.push(insumoText);
  return parts.filter(Boolean).join(" · ") || entry.method || "";
}

function entryValueText(entry, insumos = []) {
  const cashSign = cashSignForEntry(entry);
  if (cashSign !== 0 && Number(entry.amount || 0) > 0) {
    return `${cashSign > 0 ? "+" : "-"}${BRL.format(round2(entry.amount))}`;
  }
  const productQty = totalProductMovementQty(entry);
  if (productQty > 0) return `${fmtNum(productQty)} uds`;
  return describeInsumoLines(entry.insumoLines, insumos) || "Ajuste";
}

function entryTone(entry) {
  const cashSign = cashSignForEntry(entry);
  if (cashSign > 0) return "in";
  if (cashSign < 0) return "out";
  const productQty = stockMovementsForEntry(entry).reduce((sum, movement) => sum + movement.qty, 0);
  if (productQty > 0) return "in";
  if (productQty < 0) return "out";
  const insumoQty = insumoMovementsForEntry(entry).reduce((sum, movement) => sum + movement.qty, 0);
  if (insumoQty > 0) return "in";
  if (insumoQty < 0) return "out";
  return "stock";
}

function csvCell(value) {
  const text = value === undefined || value === null ? "" : String(value);
  return /[",\n]/.test(text) ? `"${text.replace(/"/g, '""')}"` : text;
}

function exportLedgerCsv(entries, balanceMap) {
  const sorted = [...entries].sort((a, b) => `${a.date}${a.createdAt}`.localeCompare(`${b.date}${b.createdAt}`));
  const header = [
    "fecha", "tipo_movimiento", "categoria", "descripcion", "producto", "cantidad",
    "precio_unitario_brl", "entrada_brl", "salida_brl", "metodo_pago", "responsable",
    "proveedor_o_cliente", "comprobante", "usd", "tipo_cambio_brl_usd", "pagos_detalle",
    "precios_detalle", "insumos_detalle", "stock_objetivo", "efecto_stock", "direccion_caja",
    "saldo_caja_brl",
  ];
  const rows = sorted.map((entry) => {
    const type = ledgerType(entry.type);
    const cashSign = cashSignForEntry(entry);
    return [
      entry.date,
      type.label,
      entry.category,
      entry.description,
      entry.product,
      entry.qty ? round2(entry.qty) : "",
      entry.unitPrice ? round2(entry.unitPrice) : "",
      cashSign > 0 ? round2(entry.amount) : "",
      cashSign < 0 ? round2(entry.amount) : "",
      entry.method,
      entry.responsible,
      entry.party,
      entry.receipt ? "Si" : "No",
      entry.usd ? round2(entry.usd) : "",
      entry.fx ? round2(entry.fx) : "",
      describePayments(entry.payments, entry.method),
      describePriceLines(entry.priceLines),
      describeInsumoLines(entry.insumoLines, []),
      entry.stockTarget || "",
      entry.stockEffect || "",
      entry.cashDirection || "",
      balanceMap[entry.id] !== undefined ? round2(balanceMap[entry.id]) : "",
    ];
  });
  const csv = [header, ...rows].map((row) => row.map(csvCell).join(",")).join("\n");
  const blob = new Blob([csv], { type: "text/csv;charset=utf-8" });
  const url = URL.createObjectURL(blob);
  const link = document.createElement("a");
  link.href = url;
  link.download = `break-contabilidad-${todayISO()}.csv`;
  link.click();
  URL.revokeObjectURL(url);
}

function fileToDataUrl(file) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => resolve(reader.result);
    reader.onerror = () => reject(new Error("No se pudo leer el archivo."));
    reader.readAsDataURL(file);
  });
}

async function ledgerRequest(path, options = {}) {
  const response = await fetch(path, {
    ...options,
    headers: {
      "Content-Type": "application/json",
      ...(options.headers || {}),
    },
  });
  const result = await response.json().catch(() => ({}));
  if (!response.ok) throw new Error(result.error || "No se pudo sincronizar la contabilidad.");
  return result;
}

async function saveLedgerEntryToServer(entry, editingId) {
  if (editingId) {
    return ledgerRequest(`/api/ledger/entries/${encodeURIComponent(editingId)}`, {
      method: "PUT",
      body: JSON.stringify({ entry }),
    });
  }
  return ledgerRequest("/api/ledger/entries", {
    method: "POST",
    body: JSON.stringify({ entry }),
  });
}

function Accounting() {
  const [entries, setEntries] = React.useState(loadLedgerEntries);
  const [products, setProducts] = React.useState(loadProducts);
  const [factoryInsumos, setFactoryInsumos] = React.useState(loadFactoryInsumosForAccounting);
  const [newProductName, setNewProductName] = React.useState("");
  const [form, setForm] = React.useState(() => defaultLedgerForm());
  const [editingId, setEditingId] = React.useState(null);
  const [error, setError] = React.useState("");
  const [saving, setSaving] = React.useState(false);
  const [syncMessage, setSyncMessage] = React.useState("");
  const [month, setMonth] = React.useState(todayISO().slice(0, 7));
  const [typeFilter, setTypeFilter] = React.useState("all");
  const [query, setQuery] = React.useState("");

  // AI states
  const [aiText, setAiText] = React.useState("");
  const [aiLoading, setAiLoading] = React.useState(false);
  const [aiMessage, setAiMessage] = React.useState("");
  const [aiContext, setAiContext] = React.useState("");
  const [aiQuestions, setAiQuestions] = React.useState([]);
  const [aiFollowup, setAiFollowup] = React.useState("");
  const [receiptImage, setReceiptImage] = React.useState(null);
  const [recording, setRecording] = React.useState(false);
  const [recordingPending, setRecordingPending] = React.useState(false);
  const mediaRecorderRef = React.useRef(null);
  const audioChunksRef = React.useRef([]);
  const audioStreamRef = React.useRef(null);
  const receiptInputRef = React.useRef(null);

  // New view state
  const [view, setView] = React.useState("dashboard"); // "dashboard" | "new" | "factory"

  React.useEffect(() => {
    try { localStorage.setItem(LEDGER_STORAGE_KEY, JSON.stringify(entries)); } catch {}
  }, [entries]);

  React.useEffect(() => {
    try { localStorage.setItem(PRODUCT_STORAGE_KEY, JSON.stringify(products)); } catch {}
  }, [products]);

  React.useEffect(() => {
    let cancelled = false;
    const loadServerLedger = async () => {
      try {
        setSyncMessage("Sincronizando caja...");
        const result = await ledgerRequest("/api/ledger");
        let serverEntries = Array.isArray(result.entries) ? result.entries.map(normalizeEntry).filter(entryHasContent) : [];
        const localEntries = loadLedgerEntries();
        const serverCustom = serverEntries.filter((entry) => entry.id !== "seed-investment-alan");
        const localCustom = localEntries.filter((entry) => entry.id !== "seed-investment-alan");
        if (localCustom.length > 0 && serverCustom.length === 0) {
          const imported = await ledgerRequest("/api/ledger/import", {
            method: "POST",
            body: JSON.stringify({ entries: localEntries }),
          });
          serverEntries = Array.isArray(imported.entries) ? imported.entries.map(normalizeEntry).filter(entryHasContent) : serverEntries;
        }
        if (!cancelled) {
          setEntries(serverEntries);
          setSyncMessage("");
        }
      } catch (err) {
        if (!cancelled) setSyncMessage("Sin conexión con la base del servidor. No guardes movimientos hasta reconectar.");
      }
    };
    loadServerLedger();
    return () => { cancelled = true; };
  }, []);

  React.useEffect(() => {
    setFactoryInsumos(loadFactoryInsumosForAccounting());
  }, [view]);

  React.useEffect(() => () => {
    const recorder = mediaRecorderRef.current;
    if (recorder) {
      recorder.onstop = null;
      recorder.ondataavailable = null;
      recorder.onerror = null;
      if (recorder.state === "recording") recorder.stop();
    }
    if (audioStreamRef.current) {
      audioStreamRef.current.getTracks().forEach((track) => track.stop());
    }
  }, []);

  const months = React.useMemo(() => {
    const unique = [...new Set(entries.map((entry) => entry.date.slice(0, 7)))].sort().reverse();
    return unique.length ? unique : [todayISO().slice(0, 7)];
  }, [entries]);

  React.useEffect(() => {
    if (month !== "all" && !months.includes(month)) setMonth(months[0] || "all");
  }, [month, months]);

  const balanceMap = React.useMemo(() => {
    let balance = 0;
    return [...entries]
      .sort((a, b) => `${a.date}${a.createdAt}`.localeCompare(`${b.date}${b.createdAt}`))
      .reduce((map, entry) => {
        balance += signedAmount(entry);
        map[entry.id] = balance;
        return map;
      }, {});
  }, [entries]);

  const periodEntries = React.useMemo(
    () => entries.filter((entry) => month === "all" || entry.date.startsWith(month)),
    [entries, month]
  );

  const visibleEntries = React.useMemo(() => {
    const needle = query.trim().toLowerCase();
    return periodEntries
      .filter((entry) => typeFilter === "all" || entry.type === typeFilter)
      .filter((entry) => {
        if (!needle) return true;
        return [entry.category, entry.description, entry.product, describeInsumoLines(entry.insumoLines, factoryInsumos), entry.method, entry.responsible, entry.party]
          .join(" ")
          .toLowerCase()
          .includes(needle);
      })
      .sort((a, b) => `${b.date}${b.createdAt}`.localeCompare(`${a.date}${a.createdAt}`));
  }, [periodEntries, query, typeFilter, factoryInsumos]);

  const allMetrics = React.useMemo(() => computeLedgerMetrics(entries), [entries]);
  const periodMetrics = React.useMemo(() => computeLedgerMetrics(periodEntries), [periodEntries]);
  const stock = React.useMemo(() => computeStock(entries, products), [entries, products]);
  const previewAmount = resolveFormAmount(form);
  const previewQty = form.priceLines && form.priceLines.length > 0 
    ? form.priceLines.reduce((sum, line) => sum + parseMoney(line.qty), 0)
    : parseMoney(form.qty);
  const activeCategories = LEDGER_CATEGORIES[form.type] || [];
  const productNames = products.map((product) => product.name);
  const insumoOptions = factoryInsumos.map((insumo) => ({
    ...insumo,
    label: `${insumo.name} (${insumo.unit})`,
  }));
  const hasProductStock = form.type === "venta" || STOCK_TYPES.has(form.type) || (form.type === "arqueo" && form.stockTarget === "productos");
  const hasInsumoStock = form.type === "insumos" || (form.type === "arqueo" && form.stockTarget === "insumos");
  const showPayments = !STOCK_TYPES.has(form.type) && !(form.type === "arqueo" && form.cashDirection === "none");

  const update = (field, value) => setForm((current) => ({ ...current, [field]: value }));
  const ensureProduct = (name) => {
    const clean = String(name || "").trim();
    const id = productIdFromName(clean);
    if (!id) return "";
    const existing = products.find((product) => product.id === id);
    if (existing) return existing.name;
    return ""; // No crear productos nuevos al vuelo
  };
  const ensureInsumo = (value) => {
    const clean = String(value || "").trim();
    const id = insumoIdFromName(clean);
    const direct = factoryInsumos.find((insumo) => insumo.id === clean || insumo.id === id);
    if (direct) return direct;
    return factoryInsumos.find((insumo) => insumoIdFromName(insumo.name) === id) || null;
  };
  const findProductForStock = (value) => {
    const clean = String(value || "").trim();
    const id = productIdFromName(clean);
    return products.find((product) => product.id === clean || product.id === id || product.name === clean) || null;
  };

  const addProduct = () => {
    const name = newProductName.trim();
    const id = productIdFromName(name);
    if (!id) return;
    setProducts((current) => current.some((product) => product.id === id) ? current : [...current, normalizeProduct({ id, name })]);
    setNewProductName("");
  };

  const setType = (type) => setForm((current) => ({
    ...current,
    type,
    category: type === "arqueo" ? "Ajuste productos" : LEDGER_CATEGORIES[type][0],
    method: type === "inversion" ? "Transferencia" : (STOCK_TYPES.has(type) ? "Otro" : (type === "arqueo" ? "Efectivo" : current.method)),
    usd: type === "inversion" ? current.usd : "",
    fx: type === "inversion" ? current.fx : "",
    stockTarget: type === "insumos" ? "insumos" : (type === "arqueo" ? "productos" : "productos"),
    stockEffect: type === "produccion" || type === "insumos" ? "in" : "out",
    cashDirection: type === "arqueo" ? "none" : "none",
    priceLines: type === "insumos" ? [] : current.priceLines,
    insumoLines: type === "insumos" || type === "arqueo" ? current.insumoLines : [],
  }));

  const addPriceLine = () => {
    setForm((current) => {
      const lines = Array.isArray(current.priceLines) ? [...current.priceLines] : [];
      const mainProduct = (current.product || "").trim();
      const mainQty = parseMoney(current.qty);
      const mainUnit = parseMoney(current.unitPrice);
      const mainAmount = parseMoney(current.amount);
      const hasMain = mainProduct || mainQty > 0 || mainUnit > 0 || mainAmount > 0;
      if (lines.length === 0 && hasMain) {
        lines.push({
          product: mainProduct,
          qty: mainQty || "",
          percent: 0,
          unitPrice: mainUnit || "",
          amount: mainAmount || "",
        });
      }
      lines.push({ product: "", qty: "", percent: 0, unitPrice: "", amount: "" });
      return hasMain && lines.length > 1
        ? { ...current, product: "", qty: "", unitPrice: "", amount: "", priceLines: lines }
        : { ...current, priceLines: lines };
    });
  };

  const updatePriceLine = (index, field, value) => {
    setForm((current) => {
      const lines = Array.isArray(current.priceLines) ? [...current.priceLines] : [];
      if (!lines[index]) return current;
      lines[index] = { ...lines[index], [field]: value };
      return { ...current, priceLines: lines };
    });
  };

  const removePriceLine = (index) => {
    setForm((current) => {
      const lines = (current.priceLines || []).filter((_, i) => i !== index);
      return { ...current, priceLines: lines };
    });
  };

  const addInsumoLine = () => {
    setForm((current) => {
      const lines = Array.isArray(current.insumoLines) ? [...current.insumoLines] : [];
      const first = insumoOptions[0];
      lines.push({ insumoId: first?.id || "", insumoName: first?.name || "", qty: "", unit: first?.unit || "", amount: "" });
      return { ...current, insumoLines: lines };
    });
  };

  const updateInsumoLine = (index, field, value) => {
    setForm((current) => {
      const lines = Array.isArray(current.insumoLines) ? [...current.insumoLines] : [];
      if (!lines[index]) return current;
      const nextLine = { ...lines[index], [field]: value };
      if (field === "insumoId") {
        const found = factoryInsumos.find((insumo) => insumo.id === value);
        nextLine.insumoName = found?.name || "";
        nextLine.unit = found?.unit || "";
      }
      lines[index] = nextLine;
      return { ...current, insumoLines: lines };
    });
  };

  const removeInsumoLine = (index) => {
    setForm((current) => {
      const lines = (current.insumoLines || []).filter((_, i) => i !== index);
      return { ...current, insumoLines: lines };
    });
  };

  const addPayment = () => {
    setForm((current) => {
      const payments = Array.isArray(current.payments) ? [...current.payments] : [];
      payments.push({ method: "Pix", amount: "" });
      return { ...current, payments };
    });
  };

  const updatePayment = (index, field, value) => {
    setForm((current) => {
      const payments = Array.isArray(current.payments) ? [...current.payments] : [];
      if (!payments[index]) return current;
      payments[index] = { ...payments[index], [field]: value };
      return { ...current, payments };
    });
  };

  const removePayment = (index) => {
    setForm((current) => {
      const payments = (current.payments || []).filter((_, i) => i !== index);
      return { ...current, payments };
    });
  };

  const resetForm = () => {
    setEditingId(null);
    setError("");
    setForm(defaultLedgerForm(form.type));
    setAiText("");
    setAiMessage("");
    setAiQuestions([]);
    setAiFollowup("");
    setReceiptImage(null);
  };

  const resolveCountAdjustments = (deltas, makeLine) => {
    const adjustments = deltas
      .map((item) => ({
        ...item,
        delta: round2((Number(item.counted) || 0) - (Number(item.current) || 0)),
      }))
      .filter((item) => Math.abs(item.delta) > 0.0001);
    if (!adjustments.length) {
      return { effect: "out", lines: [], message: "Ese conteo ya coincide con el stock actual; no hay ajuste para guardar." };
    }
    const hasIn = adjustments.some((item) => item.delta > 0);
    const hasOut = adjustments.some((item) => item.delta < 0);
    if (hasIn && hasOut) {
      return {
        effect: "out",
        lines: [],
        message: "Ese conteo mezcla faltantes y sobrantes. Guardalo en dos arqueos separados: uno de faltantes y otro de sobrantes.",
      };
    }
    const effect = hasIn ? "in" : "out";
    return {
      effect,
      lines: adjustments.map((item) => makeLine(item, Math.abs(item.delta))),
      message: "",
    };
  };

  const expandCountedStock = (classification, rawProductLines, rawInsumoLines) => {
    if (classification.type !== "arqueo" || classification.stockMode !== "count") return null;
    const stockTarget = isValidStockTarget(classification.stockTarget) ? classification.stockTarget : "productos";
    const stockScope = ["all", "multiple", "single"].includes(classification.stockScope) ? classification.stockScope : "single";
    const countedQty = Math.max(0, parseMoney(classification.countedQty));

    if (stockTarget === "insumos") {
      const source = stockScope === "all"
        ? factoryInsumos.map((insumo) => ({ insumo, counted: countedQty }))
        : rawInsumoLines
            .map((line) => {
              const match = ensureInsumo(line.insumoId || line.insumoName || line.name || line.product);
              return match ? { insumo: match, counted: parseMoney(line.qty) } : null;
            })
            .filter(Boolean);
      const result = resolveCountAdjustments(
        source.map(({ insumo, counted }) => ({
          id: insumo.id,
          name: insumo.name,
          unit: insumo.unit,
          current: Number(insumo.stock || 0),
          counted,
        })),
        (item, qty) => ({ insumoId: item.id, insumoName: item.name, qty, percent: 0, unit: item.unit, amount: 0 })
      );
      return { stockTarget, stockEffect: result.effect, insumoLines: result.lines, priceLines: [], product: "", qty: "", message: result.message };
    }

    if (stockTarget === "productos") {
      const source = stockScope === "all"
        ? products.map((product) => ({ product, counted: countedQty }))
        : (rawProductLines.length ? rawProductLines : [{ product: classification.product, qty: classification.qty }])
            .map((line) => {
              const match = findProductForStock(line.product || classification.product);
              return match ? { product: match, counted: parseMoney(line.qty) } : null;
            })
            .filter(Boolean);
      const result = resolveCountAdjustments(
        source.map(({ product, counted }) => ({
          id: product.id,
          name: product.name,
          unit: product.unit,
          current: Number(stock[product.id] || 0),
          counted,
        })),
        (item, qty) => ({ product: item.name, qty, percent: 0, unitPrice: 0, amount: 0 })
      );
      return { stockTarget, stockEffect: result.effect, priceLines: result.lines, insumoLines: [], product: "", qty: "", message: result.message };
    }

    return null;
  };

  const applyClassification = (classification) => {
    const qty = parseMoney(classification.qty);
    const amount = parseMoney(classification.amount);
    const rawLines = Array.isArray(classification.priceLines) ? classification.priceLines : [];
    const priceLines = rawLines
      .map((line) => {
        const lineProduct = ensureProduct(line.product);
        return normalizePriceLine({ ...line, product: lineProduct || line.product || "" }, qty);
      })
      .filter((line) => (line.product || "").trim() || line.qty > 0 || line.amount > 0);
    const mainProduct = priceLines.length > 1 ? "" : ensureProduct(classification.product);
    const payments = Array.isArray(classification.payments)
      ? classification.payments.map((payment) => normalizePayment(payment, amount)).filter((payment) => payment.method || payment.percent > 0 || payment.amount > 0)
      : [];
    const rawInsumoLines = Array.isArray(classification.insumoLines) ? classification.insumoLines : [];
    const insumoLines = rawInsumoLines
      .map((line) => {
        const match = ensureInsumo(line.insumoId || line.insumoName || line.name || line.product);
        return normalizeInsumoLine({
          ...line,
          insumoId: match?.id || line.insumoId || "",
          insumoName: match?.name || line.insumoName || line.name || line.product || "",
          unit: match?.unit || line.unit || "",
        }, qty);
      })
      .filter((line) => line.insumoId || line.insumoName || line.qty > 0);
    const isStock = STOCK_TYPES.has(classification.type);
    const isArqueo = classification.type === "arqueo";
    const stockTarget = isValidStockTarget(classification.stockTarget)
      ? classification.stockTarget
      : (classification.type === "insumos" ? "insumos" : (isArqueo && insumoLines.length ? "insumos" : "productos"));
    const cashDirection = CASH_DIRECTIONS.some((direction) => direction.id === classification.cashDirection)
      ? classification.cashDirection
      : "none";
    const stockEffect = STOCK_EFFECTS.some((effect) => effect.id === classification.stockEffect)
      ? classification.stockEffect
      : (classification.type === "produccion" || classification.type === "insumos" ? "in" : "out");
    const countedStock = expandCountedStock(classification, rawLines, rawInsumoLines);
    const finalPriceLines = countedStock ? countedStock.priceLines : priceLines;
    const finalInsumoLines = countedStock ? countedStock.insumoLines : insumoLines;
    const finalStockTarget = countedStock ? countedStock.stockTarget : stockTarget;
    const finalStockEffect = countedStock ? countedStock.stockEffect : stockEffect;
    setForm((current) => ({
      ...current,
      date: classification.date || current.date,
      type: classification.type || current.type,
      category: classification.category || current.category,
      description: classification.description || current.description,
      product: countedStock ? countedStock.product : (priceLines.length > 1 ? "" : (mainProduct || classification.product || "")),
      qty: countedStock ? countedStock.qty : (priceLines.length > 1 ? "" : (qty || "")),
      unitPrice: countedStock || priceLines.length > 1 ? "" : (classification.unitPrice || ""),
      amount: isStock || countedStock ? "" : (priceLines.length > 1 ? "" : (amount || "")),
      usd: classification.type === "inversion" ? classification.usd || "" : "",
      fx: classification.type === "inversion" ? classification.fx || "" : "",
      method: isStock || (isArqueo && cashDirection === "none") ? "Otro" : (classification.method || current.method),
      payments: isStock || (isArqueo && cashDirection === "none") ? [] : payments,
      priceLines: finalPriceLines,
      insumoLines: finalInsumoLines,
      stockTarget: finalStockTarget,
      stockEffect: finalStockEffect,
      cashDirection,
      responsible: classification.responsible || current.responsible,
      party: classification.party || "",
      receipt: Boolean(classification.receipt),
    }));
    setEditingId(null);
    setError("");
    return countedStock?.message || "";
  };

  const handleAiResult = (result, contextText, completeMessage) => {
    const stockMessage = applyClassification(result.classification);
    setAiContext(contextText);
    setAiQuestions(result.readyToSave ? [] : result.questions || []);
    setAiFollowup("");
    if (result.readyToSave) {
      const confidence = Math.round((result.classification.confidence || 0) * 100);
      setAiMessage(stockMessage || completeMessage || `Listo ${confidence ? `(${confidence}%)` : ""}. Revisalo y guardalo.`);
      return;
    }
    setAiMessage(stockMessage || "Tengo una parte, pero falta responder esto.");
  };

  const requestClassification = async (text) => {
    const response = await fetch("/api/classify-movement", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        text,
        currentDate: form.date || todayISO(),
        products: products.map((product) => ({ id: product.id, name: product.name, unit: product.unit, stock: stock[product.id] || 0 })),
        insumos: factoryInsumos.map((insumo) => ({ id: insumo.id, name: insumo.name, unit: insumo.unit, stock: Number(insumo.stock || 0) })),
      }),
    });
    const result = await response.json();
    if (!response.ok) throw new Error(result.error || "No se pudo clasificar.");
    return result;
  };

  const classifyMovement = async () => {
    const text = aiText.trim();
    if (!text) {
      setAiMessage("Escribi el movimiento.");
      return;
    }
    setAiLoading(true);
    setAiMessage("");
    try {
      const result = await requestClassification(text);
      handleAiResult(result, text);
    } catch (err) {
      setAiMessage(err.message || "No se pudo clasificar.");
    } finally {
      setAiLoading(false);
    }
  };

  const answerFollowup = async () => {
    const answer = aiFollowup.trim();
    if (!answer) {
      setAiMessage("Respondeme lo que falta.");
      return;
    }
    const contextText = `${aiContext || aiText.trim()}\nDatos agregados: ${answer}`;
    setAiLoading(true);
    setAiMessage("");
    try {
      const result = await requestClassification(contextText);
      handleAiResult(result, contextText);
    } catch (err) {
      setAiMessage(err.message || "No pude usar esa respuesta.");
    } finally {
      setAiLoading(false);
    }
  };

  const audioRecordingMimeType = () => {
    if (typeof MediaRecorder === "undefined" || typeof MediaRecorder.isTypeSupported !== "function") return "";
    return ["audio/webm;codecs=opus", "audio/webm", "audio/mp4", "audio/ogg;codecs=opus"].find((type) => MediaRecorder.isTypeSupported(type)) || "";
  };

  const stopAudioStream = () => {
    if (audioStreamRef.current) {
      audioStreamRef.current.getTracks().forEach((track) => track.stop());
      audioStreamRef.current = null;
    }
  };

  const transcribeAudioBlob = async (blob, filename) => {
    setAiLoading(true);
    setAiMessage("Transcribiendo audio...");
    try {
      const dataUrl = await fileToDataUrl(blob);
      const response = await fetch("/api/transcribe-audio", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ dataUrl, filename }),
      });
      const result = await response.json();
      if (!response.ok) throw new Error(result.error || "No se pudo transcribir.");
      const transcript = String(result.text || "").trim();
      if (!transcript) throw new Error("No se detecto texto en el audio.");
      setAiText(transcript);
      const classificationResult = await requestClassification(transcript);
      handleAiResult(classificationResult, transcript, "Audio transcripto y clasificado. Revisalo y guardalo.");
    } catch (err) {
      setAiMessage(err.message || "No se pudo procesar el audio.");
    } finally {
      setAiLoading(false);
    }
  };

  const startAudioRecording = async () => {
    if (aiLoading || recording || recordingPending) return;
    if (!window.isSecureContext && window.location.hostname !== "localhost" && window.location.hostname !== "127.0.0.1") {
      setAiMessage("El navegador solo permite grabar audio en HTTPS o localhost.");
      return;
    }
    if (!navigator.mediaDevices?.getUserMedia || typeof MediaRecorder === "undefined") {
      setAiMessage("Este navegador no permite grabar audio desde la página.");
      return;
    }

    try {
      setRecordingPending(true);
      setAiMessage("Grabando... tocá el micrófono para terminar.");
      setAiQuestions([]);
      setAiFollowup("");
      audioChunksRef.current = [];
      const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
      const mimeType = audioRecordingMimeType();
      const recorder = new MediaRecorder(stream, mimeType ? { mimeType } : undefined);
      audioStreamRef.current = stream;
      mediaRecorderRef.current = recorder;

      recorder.ondataavailable = (event) => {
        if (event.data && event.data.size > 0) audioChunksRef.current.push(event.data);
      };
      recorder.onerror = () => {
        stopAudioStream();
        setRecording(false);
        setRecordingPending(false);
        setAiLoading(false);
        setAiMessage("No se pudo grabar el audio.");
      };
      recorder.onstop = async () => {
        const chunks = audioChunksRef.current;
        const type = recorder.mimeType || mimeType || "audio/webm";
        mediaRecorderRef.current = null;
        audioChunksRef.current = [];
        stopAudioStream();
        setRecording(false);
        if (!chunks.length) {
          setAiMessage("No se grabó audio.");
          return;
        }
        await transcribeAudioBlob(new Blob(chunks, { type }), `audio-${Date.now()}.webm`);
      };

      recorder.start();
      setRecording(true);
      setRecordingPending(false);
    } catch (err) {
      stopAudioStream();
      mediaRecorderRef.current = null;
      audioChunksRef.current = [];
      setRecording(false);
      setRecordingPending(false);
      if (err?.name === "NotAllowedError") {
        setAiMessage("Necesito permiso para usar el micrófono.");
      } else {
        setAiMessage(err?.message || "No pude iniciar la grabación.");
      }
    }
  };

  const stopAudioRecording = () => {
    const recorder = mediaRecorderRef.current;
    if (!recorder || recorder.state === "inactive") {
      setRecording(false);
      stopAudioStream();
      return;
    }
    setAiMessage("Procesando audio...");
    recorder.stop();
  };

  const toggleAudioRecording = () => {
    if (recordingPending) return;
    if (recording) stopAudioRecording();
    else startAudioRecording();
  };

  const handleReceiptFile = async (event) => {
    const file = event.target.files?.[0];
    event.target.value = "";
    if (!file) return;
    setAiLoading(true);
    setAiMessage("Leyendo comprobante...");
    try {
      const dataUrl = await fileToDataUrl(file);
      setReceiptImage(dataUrl);
      const response = await fetch("/api/read-receipt", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          dataUrl,
          filename: file.name,
          currentDate: form.date || todayISO(),
          products: productNames,
          insumos: factoryInsumos.map((insumo) => ({ id: insumo.id, name: insumo.name, unit: insumo.unit })),
        }),
      });
      const result = await response.json();
      if (!response.ok) throw new Error(result.error || "No se pudo leer el comprobante.");
      setAiText(result.rawText || result.classification.description || "");
      handleAiResult(result, result.rawText || result.classification.description || "", "Comprobante leido y clasificado. Revisalo y guardalo.");
    } catch (err) {
      setAiMessage(err.message || "No se pudo procesar el comprobante.");
    } finally {
      setAiLoading(false);
    }
  };

  const saveEntry = async (event) => {
    event.preventDefault();
    if (saving) return;
    const amount = resolveFormAmount(form);
    const qty = parseMoney(form.qty);
    const linesArr = Array.isArray(form.priceLines) ? form.priceLines : [];
    const validLines = linesArr.filter((line) => (line.product || "").trim() && parseMoney(line.qty) > 0);
    const insumoLinesArr = Array.isArray(form.insumoLines) ? form.insumoLines : [];
    const normalizedInsumoLines = insumoLinesArr
      .map((line) => {
        const match = ensureInsumo(line.insumoId || line.insumoName);
        return normalizeInsumoLine({
          ...line,
          insumoId: match?.id || line.insumoId,
          insumoName: match?.name || line.insumoName,
          unit: match?.unit || line.unit,
        });
      })
      .filter((line) => line.insumoId && parseMoney(line.qty) > 0);
    const isStock = STOCK_TYPES.has(form.type);
    const mainHasStock = (form.product || "").trim() && qty > 0;
    const isArqueo = form.type === "arqueo";
    const cashDirection = isArqueo ? form.cashDirection : "none";
    const hasCashAdjustment = !isStock && (!isArqueo || cashDirection !== "none") && amount > 0;
    const hasProductAdjustment = hasProductStock && (mainHasStock || validLines.length > 0);
    const hasInsumoAdjustment = hasInsumoStock && normalizedInsumoLines.length > 0;

    const hasProducts = hasProductStock;

    if (!form.date) {
      setError("Falta fecha");
      return;
    }
    if (isStock) {
      if (!mainHasStock && validLines.length === 0) {
        setError("Indicá al menos un producto con cantidad");
        return;
      }
    } else if (isArqueo) {
      if (amount > 0 && cashDirection === "none") {
        setError("Indicá si sobra o falta caja");
        return;
      }
      if (cashDirection !== "none" && amount <= 0) {
        setError("Indicá el monto del arqueo de caja");
        return;
      }
      if (!hasCashAdjustment && !hasProductAdjustment && !hasInsumoAdjustment) {
        setError("Indicá caja, productos o insumos para ajustar");
        return;
      }
    } else if (form.type === "insumos") {
      if (amount <= 0 && !hasInsumoAdjustment) {
        setError("Indicá el monto o los insumos comprados");
        return;
      }
    } else if (amount <= 0) {
      setError("Falta monto");
      return;
    }

    const product = hasProducts ? (ensureProduct(form.product) || form.product) : "";
    const finalQty = hasProducts ? qty : 0;
    const finalPriceLines = hasProducts ? linesArr.map((line) => ({
      ...line,
      product: ensureProduct(line.product || product) || line.product || product,
    })) : [];

    const previous = entries.find((entry) => entry.id === editingId);
    const entry = normalizeEntry({
      ...form,
      product,
      id: editingId || makeLedgerId(),
      amount: isStock || (isArqueo && cashDirection === "none") ? 0 : amount,
      qty: finalQty,
      unitPrice: hasProducts ? parseMoney(form.unitPrice) : 0,
      usd: form.type === "inversion" ? parseMoney(form.usd) : 0,
      fx: form.type === "inversion" ? parseMoney(form.fx) : 0,
      payments: showPayments ? (Array.isArray(form.payments) ? form.payments : []) : [],
      method: showPayments ? form.method : "Otro",
      priceLines: finalPriceLines,
      insumoLines: hasInsumoStock ? normalizedInsumoLines : [],
      stockTarget: isArqueo ? form.stockTarget : (form.type === "insumos" ? "insumos" : (hasProductStock ? "productos" : "none")),
      stockEffect: isArqueo ? form.stockEffect : (form.type === "insumos" || form.type === "produccion" ? "in" : "out"),
      cashDirection,
      createdAt: previous?.createdAt || new Date().toISOString(),
    });
    const insumoDeltas = [
      ...(previous ? factoryDeltasForEntry(previous, -1) : []),
      ...factoryDeltasForEntry(entry, 1),
    ];
    setSaving(true);
    setSyncMessage("");
    try {
      const result = await saveLedgerEntryToServer(entry, editingId);
      if (insumoDeltas.length) {
        setFactoryInsumos(applyFactoryInsumoDeltasForAccounting(insumoDeltas));
      }
      setEntries(Array.isArray(result.entries)
        ? result.entries.map(normalizeEntry).filter(entryHasContent)
        : (current) => editingId ? current.map((item) => item.id === editingId ? entry : item) : [entry, ...current]);
      resetForm();
      setView("dashboard");
    } catch (err) {
      setError(err.message || "No se pudo guardar en la base del servidor.");
      setSyncMessage("No se guardó: revisá la conexión con el servidor.");
    } finally {
      setSaving(false);
    }
  };

  const editEntry = (entry) => {
    setEditingId(entry.id);
    setError("");
    setForm({
      date: entry.date,
      type: entry.type,
      category: entry.category,
      description: entry.description,
      product: entry.product,
      qty: entry.qty || "",
      unitPrice: entry.unitPrice || "",
      amount: entry.amount || "",
      usd: entry.usd || "",
      fx: entry.fx || "",
      method: entry.method,
      payments: entry.payments || [],
      priceLines: entry.priceLines || [],
      insumoLines: entry.insumoLines || [],
      stockTarget: entry.stockTarget || (entry.type === "insumos" ? "insumos" : "productos"),
      stockEffect: entry.stockEffect || (entry.type === "produccion" || entry.type === "insumos" ? "in" : "out"),
      cashDirection: entry.cashDirection || "none",
      responsible: entry.responsible,
      party: entry.party,
      receipt: entry.receipt,
    });
    setView("new");
    window.scrollTo({ top: 0, behavior: "smooth" });
  };

  const deleteEntry = async (id) => {
    const entry = entries.find((item) => item.id === id);
    const insumoDeltas = entry ? factoryDeltasForEntry(entry, -1) : [];
    try {
      const result = await ledgerRequest(`/api/ledger/entries/${encodeURIComponent(id)}`, { method: "DELETE" });
      if (insumoDeltas.length) {
        setFactoryInsumos(applyFactoryInsumoDeltasForAccounting(insumoDeltas));
      }
      setEntries(Array.isArray(result.entries)
        ? result.entries.map(normalizeEntry).filter(entryHasContent)
        : (current) => current.filter((entry) => entry.id !== id));
      if (editingId === id) resetForm();
    } catch (err) {
      setSyncMessage(err.message || "No se pudo borrar en la base del servidor.");
    }
  };

  // La fábrica reporta cada lote terminado y se registra como producción real (suma stock)
  const registerProduction = React.useCallback((productName, qty) => {
    const entry = normalizeEntry({
      type: "produccion",
      category: "Produccion diaria",
      description: `Fábrica Break: ${qty} ${productName}`,
      product: productName,
      qty,
      method: "Otro",
      responsible: "Fábrica Break",
    });
    saveLedgerEntryToServer(entry)
      .then((result) => {
        setEntries(Array.isArray(result.entries)
          ? result.entries.map(normalizeEntry).filter(entryHasContent)
          : (current) => [entry, ...current]);
      })
      .catch(() => {
        setEntries((current) => [entry, ...current]);
        setSyncMessage("La producción quedó local, pero no se pudo guardar en la base del servidor.");
      });
  }, []);

  const stats = [
    { label: "Caja actual", value: BRL.format(round2(allMetrics.cash)), icon: "wallet", tone: "dark" },
    { label: "Ventas periodo", value: BRL.format(round2(periodMetrics.sales)), icon: "trend", tone: "green" },
    { label: "Costos periodo", value: BRL.format(round2(periodMetrics.expenses)), icon: "receipt", tone: "red" },
    { label: "Resultado periodo", value: BRL.format(round2(periodMetrics.result)), icon: "trend", tone: periodMetrics.result >= 0 ? "green" : "red" },
  ];

  if (view === "factory") {
    return <Factory onExit={() => setView("dashboard")} onProduced={registerProduction} />;
  }

  if (view === "new") {
    return (
      <main className="acc-page page">
        <div className="acc-max">
          <header className="acc-header">
            <button className="acc-icon-btn" onClick={() => { setView("dashboard"); resetForm(); }} aria-label="Volver">
              <Icon name="x" size={20} />
            </button>
            <h1 className="acc-title-sm">{editingId ? "Editar" : "Nuevo Movimiento"}</h1>
            <div style={{ width: 40 }} />
          </header>
          {syncMessage && <div className="acc-sync-msg">{syncMessage}</div>}

          <div className="acc-ai-hero">
            <textarea
              className="acc-ai-input"
              value={aiText}
              onChange={(e) => {
                setAiText(e.target.value);
                setAiContext("");
                setAiQuestions([]);
                setAiFollowup("");
              }}
              placeholder="Ej. Vendimos 2 focaccias por Pix a 34 cada una..."
              rows="3"
            />
            <div className="acc-ai-actions">
              <button type="button" className="btn btn-amber" onClick={classifyMovement} disabled={aiLoading || recording || recordingPending}>
                <Icon name="spark" size={18} /> {recording ? "Grabando..." : (recordingPending ? "Abriendo mic..." : (aiLoading ? "Pensando..." : "Clasificar"))}
              </button>
              <button
                type="button"
                className={`acc-ai-icon-btn ${recording ? "recording" : ""}`}
                onClick={toggleAudioRecording}
                disabled={(aiLoading || recordingPending) && !recording}
                aria-label={recording ? "Detener grabación" : "Grabar audio"}
                title={recording ? "Detener grabación" : "Grabar audio"}
              >
                <Icon name={recording ? "stop" : "mic"} size={18} />
              </button>
              <button type="button" className="acc-ai-icon-btn" onClick={() => receiptInputRef.current?.click()} disabled={aiLoading || recording || recordingPending}>
                <Icon name="image" size={18} />
              </button>
            </div>
            
            {receiptImage && (
              <div style={{marginTop: '20px', borderRadius: 'var(--r)', overflow: 'hidden', border: '1px solid rgba(255,255,255,0.1)', background: 'rgba(0,0,0,0.2)', position: 'relative'}}>
                <img src={receiptImage} alt="Comprobante" style={{display: 'block', width: '100%', maxHeight: '300px', objectFit: 'contain'}} />
                <button 
                  type="button" 
                  onClick={() => setReceiptImage(null)}
                  style={{position: 'absolute', top: 8, right: 8, background: 'rgba(0,0,0,0.6)', color: '#fff', border: 'none', borderRadius: '50%', width: 28, height: 28, display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer'}}
                >
                  <Icon name="x" size={14} />
                </button>
              </div>
            )}
            
            <input ref={receiptInputRef} style={{display: "none"}} type="file" accept="image/*" onChange={handleReceiptFile} />

            {aiQuestions.length > 0 && (
              <div className="acc-ai-followup">
                <p>Falta esto:</p>
                <ul>
                  {aiQuestions.map((q) => <li key={q}>{q}</li>)}
                </ul>
                <div className="acc-ai-reply">
                  <input
                    className="acc-input"
                    value={aiFollowup}
                    onChange={(e) => setAiFollowup(e.target.value)}
                    placeholder="Respuesta..."
                    disabled={aiLoading}
                  />
                  <button type="button" className="btn btn-pine" onClick={answerFollowup} disabled={aiLoading}>
                    Enviar
                  </button>
                </div>
              </div>
            )}
            {aiMessage && <div className="acc-ai-msg">{aiMessage}</div>}
          </div>

          <form className="acc-form mt-l" onSubmit={saveEntry}>
            <div className="acc-pills">
              {LEDGER_TYPES.map((t) => (
                <button
                  type="button"
                  key={t.id}
                  className={`acc-pill ${form.type === t.id ? "active" : ""}`}
                  onClick={() => setType(t.id)}
                >
                  {t.label}
                </button>
              ))}
            </div>

            {form.type === "arqueo" && (
              <div className="acc-lines-box acc-audit-box">
                <div className="acc-field-row acc-field-row-compact">
                  <div className="acc-field">
                    <label>Ajuste de caja</label>
                    <select
                      className="acc-input"
                      value={form.cashDirection}
                      onChange={(e) => setForm((current) => ({
                        ...current,
                        cashDirection: e.target.value,
                        category: e.target.value !== "none" && current.stockTarget === "none" ? "Ajuste caja" : current.category,
                      }))}
                    >
                      {CASH_DIRECTIONS.map((direction) => <option key={direction.id} value={direction.id}>{direction.label}</option>)}
                    </select>
                  </div>
                  <div className="acc-field">
                    <label>Stock a ajustar</label>
                    <select
                      className="acc-input"
                      value={form.stockTarget}
                      onChange={(e) => setForm((current) => ({
                        ...current,
                        stockTarget: e.target.value,
                        category: e.target.value === "insumos"
                          ? "Ajuste insumos"
                          : (e.target.value === "productos" ? "Ajuste productos" : (current.cashDirection !== "none" ? "Ajuste caja" : current.category)),
                      }))}
                    >
                      {STOCK_TARGETS.map((target) => <option key={target.id} value={target.id}>{target.label}</option>)}
                    </select>
                  </div>
                </div>
                {form.stockTarget !== "none" && (
                  <div className="acc-field">
                    <label>Resultado del conteo</label>
                    <select className="acc-input" value={form.stockEffect} onChange={(e) => update("stockEffect", e.target.value)}>
                      {STOCK_EFFECTS.map((effect) => <option key={effect.id} value={effect.id}>{effect.label}</option>)}
                    </select>
                  </div>
                )}
              </div>
            )}

            {STOCK_TYPES.has(form.type) ? (
              <div className="acc-huge-input">
                <input
                  type="text"
                  inputMode="decimal"
                  placeholder="0"
                  value={form.priceLines && form.priceLines.length > 0 ? previewQty : form.qty}
                  onChange={(e) => update("qty", e.target.value)}
                  disabled={form.priceLines && form.priceLines.length > 0}
                  style={{ textAlign: "center" }}
                />
                <span className="acc-currency" style={{fontSize: 24}}>uds</span>
              </div>
            ) : (
              <div className="acc-huge-input">
                <span className="acc-currency">R$</span>
                <input
                  type="text"
                  inputMode="decimal"
                  placeholder="0,00"
                  value={form.amount || previewAmount || ""}
                  onChange={(e) => update("amount", e.target.value)}
                />
              </div>
            )}

            <div className="acc-field-row" style={{display: 'flex', gap: 16, marginBottom: 24}}>
              <div className="acc-field" style={{flex: 1, marginBottom: 0}}>
                <label>Fecha</label>
                <input
                  type="date"
                  className="acc-input"
                  value={form.date}
                  onChange={(e) => update("date", e.target.value)}
                />
              </div>
              <div className="acc-field" style={{flex: 1, marginBottom: 0}}>
                <label>Categoría</label>
                <select
                  className="acc-input"
                  value={form.category}
                  onChange={(e) => update("category", e.target.value)}
                >
                  {(activeCategories || []).map((c) => <option key={c}>{c}</option>)}
                </select>
              </div>
            </div>

            <div className="acc-field">
              <label>Concepto / Descripción</label>
              <input
                className="acc-input"
                placeholder="Ej. Venta mostrador"
                value={form.description}
                onChange={(e) => update("description", e.target.value)}
              />
            </div>

            {hasProductStock && (
              <>
                <div className="acc-field-row" style={{display: 'flex', gap: 16, marginBottom: 24}}>
                  <div className="acc-field" style={{flex: 2, marginBottom: 0}}>
                    <label>Producto (afecta stock)</label>
                    <select
                      className="acc-input"
                      value={form.product}
                      onChange={(e) => update("product", e.target.value)}
                    >
                      <option value="">- Seleccionar -</option>
                      {productNames.map((p) => <option key={p} value={p}>{p}</option>)}
                    </select>
                  </div>
                  {!STOCK_TYPES.has(form.type) && (
                    <div className="acc-field" style={{flex: 1, marginBottom: 0}}>
                      <label>Cantidad</label>
                      <input
                        type="number"
                        step="0.01"
                        className="acc-input"
                        placeholder="1"
                        value={form.qty}
                        onChange={(e) => update("qty", e.target.value)}
                      />
                    </div>
                  )}
                </div>

                {/* Listado de múltiples productos (priceLines) si la IA o el usuario agregó más de uno */}
                {form.priceLines && form.priceLines.length > 0 && (
                  <div className="acc-lines-box" style={{background: 'var(--cream-50)', padding: 16, borderRadius: 'var(--r)', marginBottom: 24}}>
                    <label style={{display: 'block', fontSize: 13, color: 'var(--muted)', textTransform: 'uppercase', marginBottom: 12, fontWeight: 700}}>Detalle de Productos</label>
                    {form.priceLines.map((line, i) => (
                      <div key={i} style={{display: 'flex', gap: 8, marginBottom: 8}}>
                        <select className="acc-input" style={{padding: '8px 12px', flex: 2}} value={line.product} onChange={(e) => updatePriceLine(i, "product", e.target.value)}>
                          <option value="">Producto</option>
                          {productNames.map((p) => <option key={p} value={p}>{p}</option>)}
                        </select>
                        <input type="number" step="0.01" className="acc-input" style={{padding: '8px 12px', flex: 1}} placeholder="Cant" value={line.qty} onChange={(e) => updatePriceLine(i, "qty", e.target.value)} />
                        <button type="button" className="acc-icon-btn" style={{background: '#fff'}} onClick={() => removePriceLine(i)}><Icon name="x" size={14}/></button>
                      </div>
                    ))}
                    <button type="button" className="btn btn-ghost btn-sm" onClick={addPriceLine} style={{marginTop: 8}}>+ Agregar producto</button>
                  </div>
                )}
                {(!form.priceLines || form.priceLines.length === 0) && (
                  <button type="button" className="btn btn-ghost btn-sm" onClick={addPriceLine} style={{marginBottom: 24}}>+ Detallar múltiples productos</button>
                )}
              </>
            )}

            {hasInsumoStock && (
              <div className="acc-lines-box acc-lines-box-soft">
                <label className="acc-section-label">Insumos que entran o se ajustan</label>
                {form.insumoLines && form.insumoLines.length > 0 ? (
                  form.insumoLines.map((line, i) => {
                    const current = factoryInsumos.find((insumo) => insumo.id === line.insumoId);
                    return (
                      <div key={i} className="acc-line-row">
                        <select className="acc-input" value={line.insumoId} onChange={(e) => updateInsumoLine(i, "insumoId", e.target.value)}>
                          <option value="">Insumo</option>
                          {insumoOptions.map((insumo) => <option key={insumo.id} value={insumo.id}>{insumo.label}</option>)}
                        </select>
                        <input
                          type="number"
                          step="0.01"
                          className="acc-input acc-line-qty"
                          placeholder="Cant"
                          value={line.qty}
                          onChange={(e) => updateInsumoLine(i, "qty", e.target.value)}
                        />
                        <span className="acc-line-unit">{line.unit || current?.unit || ""}</span>
                        <button type="button" className="acc-icon-btn" style={{background: '#fff'}} onClick={() => removeInsumoLine(i)}><Icon name="x" size={14}/></button>
                      </div>
                    );
                  })
                ) : (
                  <div className="acc-mini-empty">Sin insumos detallados.</div>
                )}
                <button type="button" className="btn btn-ghost btn-sm" onClick={addInsumoLine} style={{marginTop: 8}}>+ Agregar insumo</button>
              </div>
            )}

            {showPayments && (
              <div className="acc-field-row" style={{display: 'flex', gap: 16, marginBottom: 24, flexDirection: 'column'}}>
                <div style={{display: 'flex', gap: 16}}>
                  <div className="acc-field" style={{flex: 1, marginBottom: 0}}>
                    <label>Método de Pago</label>
                    <select className="acc-input" value={form.method} onChange={(e) => update("method", e.target.value)}>
                      {LEDGER_METHODS.map((m) => <option key={m}>{m}</option>)}
                    </select>
                  </div>
                  {form.type === "inversion" && (
                    <div className="acc-field" style={{flex: 1, marginBottom: 0}}>
                      <label>Monto USD (Opcional)</label>
                      <input
                        type="number"
                        step="0.01"
                        className="acc-input"
                        placeholder="US$"
                        value={form.usd}
                        onChange={(e) => update("usd", e.target.value)}
                      />
                    </div>
                  )}
                </div>

                {form.method === "Mixto" && (
                  <div className="acc-lines-box" style={{background: 'var(--cream-50)', padding: 16, borderRadius: 'var(--r)', marginTop: 8}}>
                    <label style={{display: 'block', fontSize: 13, color: 'var(--muted)', textTransform: 'uppercase', marginBottom: 12, fontWeight: 700}}>Detalle de Pagos Mixtos</label>
                    {form.payments && form.payments.map((p, i) => (
                      <div key={i} style={{display: 'flex', gap: 8, marginBottom: 8}}>
                        <select className="acc-input" style={{padding: '8px 12px', flex: 2}} value={p.method} onChange={(e) => updatePayment(i, "method", e.target.value)}>
                          {LEDGER_METHODS.filter(m => m !== "Mixto" && m !== "Otro").map((m) => <option key={m} value={m}>{m}</option>)}
                        </select>
                        <input type="number" step="0.01" className="acc-input" style={{padding: '8px 12px', flex: 1}} placeholder="Monto" value={p.amount} onChange={(e) => updatePayment(i, "amount", e.target.value)} />
                        <button type="button" className="acc-icon-btn" style={{background: '#fff'}} onClick={() => removePayment(i)}><Icon name="x" size={14}/></button>
                      </div>
                    ))}
                    <button type="button" className="btn btn-ghost btn-sm" onClick={addPayment} style={{marginTop: 8}}>+ Agregar pago</button>
                  </div>
                )}
              </div>
            )}

            <div className="acc-actions">
              <button type="submit" className="btn btn-pine btn-lg btn-block" disabled={saving}>
                {saving ? "Guardando..." : "Confirmar"}
              </button>
            </div>
            {error && <p className="ledger-error" style={{textAlign:"center", marginTop:16, color: 'var(--red)'}}>{error}</p>}
          </form>
        </div>
      </main>
    );
  }

  return (
    <main className="acc-page page">
      <div className="acc-max">
        <header className="acc-header">
          <h1 className="acc-title">Caja</h1>
          <button className="btn btn-pine" style={{borderRadius: 999}} onClick={() => setView("new")}>
            <Icon name="plus" size={16} /> Nuevo
          </button>
        </header>
        {syncMessage && <div className="acc-sync-msg">{syncMessage}</div>}

        <button type="button" className="acc-factory-banner" onClick={() => setView("factory")}>
          <span className="em">🏭</span>
          <div>
            <strong>Producción Break</strong>
            <span>Monitor en vivo · máquinas, insumos y cuellos de botella</span>
          </div>
          <span className="go">Entrar</span>
        </button>

        <section className="acc-balance-card">
          <span className="acc-balance-label">Balance Total</span>
          <strong className="acc-balance-amount">{BRL.format(round2(allMetrics.cash))}</strong>

          {Object.keys(allMetrics.methods || {}).length > 0 && (
            <div className="acc-balance-methods" style={{display: 'flex', gap: '8px', flexWrap: 'wrap', marginTop: '16px'}}>
              {Object.entries(allMetrics.methods).filter(([_, val]) => Math.abs(val) > 0.01).map(([method, val]) => (
                <div key={method} style={{background: 'rgba(255,255,255,0.1)', padding: '6px 12px', borderRadius: '999px', fontSize: '13px', display: 'flex', gap: '6px'}}>
                  <span style={{opacity: 0.8}}>{method}</span>
                  <strong>{BRL.format(round2(val))}</strong>
                </div>
              ))}
            </div>
          )}
          
          <div className="acc-balance-row" style={{marginTop: '24px'}}>
            <div>
              <span>Ingresos Mes</span>
              <strong className="in">+{BRL.format(round2(periodMetrics.sales))}</strong>
            </div>
            <div>
              <span>Gastos Mes</span>
              <strong className="out">-{BRL.format(round2(periodMetrics.expenses))}</strong>
            </div>
          </div>
        </section>

        <section className="acc-stock-card" style={{background: '#fff', borderRadius: 'var(--r-lg)', padding: '20px 24px', marginBottom: '32px', boxShadow: 'var(--shadow)', border: '1px solid rgba(0,0,0,0.03)'}}>
          <div style={{display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '16px'}}>
            <h2 style={{fontFamily: 'var(--font-d)', fontSize: '16px', fontWeight: 600, color: 'var(--pine)', margin: 0}}>Stock Productos</h2>
          </div>
          <div style={{display: 'flex', gap: '12px', flexWrap: 'wrap'}}>
            {products.map(p => {
              const qty = stock[p.id] || 0;
              return (
                <div key={p.id} style={{background: 'var(--cream-50)', padding: '12px 16px', borderRadius: '12px', flex: '1 1 calc(50% - 6px)', display: 'flex', flexDirection: 'column', gap: '4px'}}>
                  <span style={{fontSize: '13px', color: 'var(--muted)', fontWeight: 600}}>{p.name}</span>
                  <strong style={{fontFamily: 'var(--font-d)', fontSize: '20px', color: qty > 0 ? 'var(--pine)' : (qty < 0 ? 'var(--red)' : 'var(--muted)')}}>
                    {qty} <span style={{fontSize: '14px', fontWeight: 500, opacity: 0.6}}>{p.unit}</span>
                  </strong>
                </div>
              );
            })}
          </div>
        </section>

        <section className="acc-stock-card" style={{background: '#fff', borderRadius: 'var(--r-lg)', padding: '20px 24px', marginBottom: '32px', boxShadow: 'var(--shadow)', border: '1px solid rgba(0,0,0,0.03)'}}>
          <div style={{display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '16px'}}>
            <h2 style={{fontFamily: 'var(--font-d)', fontSize: '16px', fontWeight: 600, color: 'var(--pine)', margin: 0}}>Stock Insumos</h2>
          </div>
          <div style={{display: 'flex', gap: '12px', flexWrap: 'wrap'}}>
            {factoryInsumos.map((insumo) => {
              const qty = Number(insumo.stock || 0);
              const low = insumo.max ? qty / insumo.max < 0.2 : false;
              return (
                <div key={insumo.id} style={{background: 'var(--cream-50)', padding: '12px 16px', borderRadius: '12px', flex: '1 1 calc(50% - 6px)', display: 'flex', flexDirection: 'column', gap: '4px'}}>
                  <span style={{fontSize: '13px', color: 'var(--muted)', fontWeight: 600}}>{insumo.name}</span>
                  <strong style={{fontFamily: 'var(--font-d)', fontSize: '20px', color: low ? 'var(--red)' : 'var(--pine)'}}>
                    {fmtNum(qty)} <span style={{fontSize: '14px', fontWeight: 500, opacity: 0.6}}>{insumo.unit}</span>
                  </strong>
                </div>
              );
            })}
          </div>
        </section>

        <section className="acc-feed">
          <div className="acc-feed-head">
            <h2>Movimientos</h2>
            <select className="acc-month-select" value={month} onChange={(e) => setMonth(e.target.value)}>
              <option value="all">Todos</option>
              {months.map((m) => <option key={m} value={m}>{monthLabel(m)}</option>)}
            </select>
          </div>

          <div className="acc-list">
            {visibleEntries.length === 0 ? (
              <div className="acc-empty">Nada por aquí aún.</div>
            ) : (
              visibleEntries.map((entry) => {
                const tone = entryTone(entry);
                const isIn = tone === "in";
                return (
                  <div className="acc-item" key={entry.id}>
                    <div className="acc-item-main">
                      <div className={`acc-item-icon ${tone}`}>
                        <Icon name={isIn ? "trend" : "receipt"} size={18} />
                      </div>
                      <div className="acc-item-text">
                        <strong>{entry.description || entry.category || "Movimiento"}</strong>
                        <span>{dateLabel(entry.date)} · {entryDetailText(entry, factoryInsumos)}</span>
                      </div>
                    </div>
                    <div className="acc-item-right">
                      <strong className={`acc-item-val ${tone}`}>
                        {entryValueText(entry, factoryInsumos)}
                      </strong>
                      <div style={{display: 'flex', gap: 4}}>
                        <button type="button" className="acc-item-del" onClick={() => editEntry(entry)} style={{color: 'var(--pine)'}}>
                          <Icon name="edit" size={14} />
                        </button>
                        <button type="button" className="acc-item-del" onClick={() => deleteEntry(entry.id)}>
                          <Icon name="trash" size={14} />
                        </button>
                      </div>
                    </div>
                  </div>
                );
              })
            )}
          </div>
        </section>
      </div>
    </main>
  );
}

Object.assign(window, { Accounting });
