// Deus Cake — main app
const { useState, useEffect, useMemo, useRef, useCallback } = React;
const D = window.DEUS_DATA;
// "admin" (default, /admin/cotizador) ve precios y guarda directo en cotizaciones.
// "cliente" (público, /customer/cotizador) arma la misma configuración de
// pastel pero sin ver ningún precio, y solo envía una solicitud pendiente.
const MODE = (typeof window !== "undefined" && window.COTIZADOR_MODE) || "admin";
const IS_CLIENT = MODE === "cliente";

// === Default state
const blankTier = () => ({
  flavor: "vainilla",
  filling: "crema_pastelera",
  tierType: "normal",
  union: "buttercream",
  colorId: "crema", colorName: "Crema", colorHex: "#F8EFD9",
  pantone: "",
  special: "",
});

const defaultState = () => ({
  folio: "DC-" + Date.now().toString().slice(-6),
  // Tipo de evento (Fase 4 — Agenda + Alertas): determina las alertas
  // automáticas de seguimiento futuras (bautizo → 1er año, boda → baby
  // shower/bautizo, etc.). "otro" cuando no aplica ninguna de las anteriores.
  tipoEvento: "otro",
  client: { code: "", name: "", phone: "", email: "" },
  delivery: {
    date: "", time: "",
    locationName: "", address: "",
    foreign: false, km: 0, lat: null, lng: null,
    receiverName: "", receiverPhone: "",
  },
  cake: {
    people: 15,
    theme: "",
    hydration: "ALTO",
    covering: "buttercream",
    sameForAll: true,
    tiers: [blankTier()],
    topper: { enabled: false, text: "", material: "", paper: false, laser: false, laserColor: "" },
    figures: { fondant3d: false, arroz: false, impreso: false, relieve: false,
               funkoQty: 0, detailQty: 0, cookieQty: 0, cookieDetailQty: 0 },
    flowers: { fondant: false, natural: false, color: "", qty: 0 },
  },
  notes: "",
  advance: 0,
});

// === Path utils for setting nested state
function setPath(obj, path, value) {
  const keys = path.split(".");
  const out = Array.isArray(obj) ? [...obj] : { ...obj };
  let cur = out;
  for (let i = 0; i < keys.length - 1; i++) {
    const k = keys[i];
    cur[k] = Array.isArray(cur[k]) ? [...cur[k]] : { ...cur[k] };
    cur = cur[k];
  }
  cur[keys[keys.length - 1]] = value;
  return out;
}

function App() {
  // Estado inicial: si hay un borrador sin terminar en este navegador (solo
  // admin — ver notas abajo), lo retoma con TODO y su mismo folio, en vez de
  // generar uno nuevo cada vez que se entra a la pantalla. El borrador se
  // borra solo al dar "Nueva cotización"/"Limpiar" o al guardar con éxito,
  // así que nunca "revive" una cotización ya cerrada.
  const [s, setS] = useState(() => {
    if (IS_CLIENT) return defaultState();
    try {
      const saved = localStorage.getItem("deuscake.draft");
      if (saved) return JSON.parse(saved);
    } catch (e) {}
    return defaultState();
  });
  const [tab, setTab] = useState("resumen"); // resumen | cliente | cocina | reparto
  const [view, setView] = useState("editor"); // editor | docs

  // ====== Wizard ======
  // `maxAlcanzado` permite regresar a pasos ya visitados con un clic en la
  // barra, pero no saltarse hacia adelante sin validar lo de en medio.
  const [paso, setPaso] = useState(1);
  const [maxAlcanzado, setMaxAlcanzado] = useState(1);
  const [erroresPaso, setErroresPaso] = useState([]);

  const irAPaso = useCallback((n) => {
    setPaso(n);
    setErroresPaso([]);
    setMaxAlcanzado(prev => Math.max(prev, n));
    window.scrollTo({ top: 0, behavior: "smooth" });
  }, []);

  const pasoSiguiente = useCallback(() => {
    const faltan = validarPaso(paso, s);
    if (faltan.length) { setErroresPaso(faltan); return; }
    irAPaso(Math.min(paso + 1, WIZARD_STEPS.length));
  }, [paso, s, irAPaso]);

  const pasoAnterior = useCallback(() => irAPaso(Math.max(paso - 1, 1)), [paso, irAPaso]);
  // Se incrementa cuando el catálogo de la BD llega, para forzar un re-render
  // (D = window.DEUS_DATA se lee en cada render, pero React no sabe que cambió
  // por sí solo ya que no es state — este contador se lo hace saber).
  const [catalogVersion, setCatalogVersion] = useState(0);

  // Autosave del borrador en curso (solo admin — el cotizador público
  // siempre abre en blanco, cada visitante es alguien distinto).
  useEffect(() => {
    if (IS_CLIENT) return;
    try { localStorage.setItem("deuscake.draft", JSON.stringify(s)); } catch (e) {}
  }, [s]);

  // Catálogo real desde "Catálogo de Opciones" del admin (sabores, rellenos,
  // coberturas, tipos de piso, acabados, extras). Si falla, se queda con los
  // valores de respaldo que ya trae data.js.
  useEffect(() => {
    if (!window.COTIZADOR_API_BASE) return;
    fetch(window.COTIZADOR_API_BASE + "opciones", { method: "POST" })
      .then(r => r.json())
      .then(res => {
        window.applyCotizadorCatalog(res && res.data);
        setCatalogVersion(v => v + 1);
      })
      .catch(function (e) {
        console.warn("No se pudo cargar el catálogo desde la BD, usando valores de respaldo.", e);
      });
  }, []);
  // ====== Setter generalizado ======
  const set = useCallback((path, value) => {
    setS(prev => {
      // Casos especiales
      if (path === "cake.tiersCount") {
        const n = Math.max(1, Math.min(7, Number(value) || 1));
        const tiers = [...prev.cake.tiers];
        while (tiers.length < n) tiers.push(blankTier());
        while (tiers.length > n) tiers.pop();
        // ajustar personas sugeridas si está en valor por defecto
        let people = prev.cake.people;
        const suggested = D.servingsByTier[n];
        if (suggested && (people === D.servingsByTier[prev.cake.tiers.length] || people === 0)) {
          people = suggested;
        }
        return { ...prev, cake: { ...prev.cake, tiers, people } };
      }
      if (path === "cake.baseFlavor") {
        return { ...prev, cake: { ...prev.cake, tiers: prev.cake.tiers.map(t => ({ ...t, flavor: value })) } };
      }
      if (path === "cake.baseFilling") {
        return { ...prev, cake: { ...prev.cake, tiers: prev.cake.tiers.map(t => ({ ...t, filling: value })) } };
      }
      return setPath(prev, path, value);
    });
  }, []);

  // ====== Totales ======
  const totals = useMemo(() => {
    const people = Math.max(1, Number(s.cake.people) || 0);
    const perPerson = D.pricePerPerson(people);
    const cakeBase = people * perPerson;

    // Sabores/rellenos especiales: cuenta únicos
    const specialFlavors = new Set();
    const specialFillings = new Set();
    s.cake.tiers.forEach(t => {
      const f = D.flavors.find(x => x.id === t.flavor);
      const r = D.fillings.find(x => x.id === t.filling);
      if (f?.extra) specialFlavors.add(t.flavor);
      if (r?.extra) specialFillings.add(t.filling);
    });
    const specialCount = specialFlavors.size + specialFillings.size;
    const specialPct = specialCount > 0 ? D.SPECIAL_FLAVOR_PCT : 0;
    const specialSurcharge = cakeBase * specialPct;

    // Extras
    const items = [];
    if (s.cake.topper.enabled) items.push({ name: "Cake topper", qty: 1, price: D.extras.cake_topper.price });
    if (s.cake.figures.funkoQty > 0) items.push({ name: "Figuras tipo Funko", qty: s.cake.figures.funkoQty, price: D.extras.figura_funko.price });
    if (s.cake.figures.detailQty > 0) items.push({ name: "Figuras detalladas", qty: s.cake.figures.detailQty, price: D.extras.figura_detalle.price });
    if (s.cake.figures.cookieQty > 0) items.push({ name: "Galletas royal icing", qty: s.cake.figures.cookieQty, price: D.extras.galleta_simple.price });
    if (s.cake.figures.cookieDetailQty > 0) items.push({ name: "Galletas royal icing detalladas", qty: s.cake.figures.cookieDetailQty, price: D.extras.galleta_detalle.price });
    if (s.cake.flowers.qty > 0) items.push({ name: "Flores decorativas", qty: s.cake.flowers.qty, price: D.extras.flor.price });
    const extrasSum = items.reduce((sum, x) => sum + x.qty * x.price, 0);
    const extrasCount = items.reduce((sum, x) => sum + x.qty, 0);

    // Envío
    const shipping = D.computeShipping(Number(s.delivery.km) || 0);

    const subtotal = cakeBase + specialSurcharge + extrasSum + shipping;
    const iva = subtotal * D.IVA_RATE;
    const total = subtotal + iva;
    // Nunca mostrar saldo negativo: si un JSON importado trae un anticipo
    // viejo mayor al total recalculado, se recorta aquí también (el input
    // ya lo evita al escribir, pero un import no pasa por el onChange).
    const advance = Math.max(0, Math.min(Number(s.advance) || 0, total));
    const balance = total - advance;

    return {
      perPerson, cakeBase,
      specialPct, specialSurcharge, specialCount,
      items, extrasSum, extrasCount,
      shipping, subtotal, iva, total, advance, balance,
    };
  }, [s]);

  // Verificación con el servidor (Fase 2): el precio real lo calcula PHP con
  // los mismos datos del catálogo — aquí solo se compara en segundo plano para
  // detectar si el cálculo del navegador se desincroniza. No bloquea nada;
  // el servidor será quien mande cuando se conecte el guardado (Fase 4).
  useEffect(() => {
    if (!window.COTIZADOR_API_BASE || IS_CLIENT) return; // no hay /calcular público, y el cliente no ve precio
    const timer = setTimeout(() => {
      const payload = {
        people: s.cake.people,
        tiers: s.cake.tiers.map(t => ({ flavor: t.flavor, filling: t.filling })),
        extras: {
          topper: s.cake.topper.enabled,
          funkoQty: s.cake.figures.funkoQty,
          detailQty: s.cake.figures.detailQty,
          cookieQty: s.cake.figures.cookieQty,
          cookieDetailQty: s.cake.figures.cookieDetailQty,
          flowerQty: s.cake.flowers.qty,
        },
        km: s.delivery.km,
        advance: s.advance,
      };
      fetch(window.COTIZADOR_API_BASE + "calcular", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(payload),
      })
        .then(r => r.json())
        .then(res => {
          const serverTotal = res && res.data ? Number(res.data.total) : null;
          if (serverTotal == null) return;
          const diff = Math.abs(serverTotal - totals.total);
          if (diff > 0.01) {
            console.warn("[Cotizador] Precio del servidor distinto al del navegador:", { servidor: serverTotal, navegador: totals.total });
          } else {
            console.log("[Cotizador] Precio verificado con el servidor:", serverTotal);
          }
        })
        .catch(function () {});
    }, 600);
    return () => clearTimeout(timer);
  }, [s, totals.total]);

  // ====== Estado enriquecido para los documentos ======
  const stateForDocs = useMemo(() => {
    const totalH = s.cake.tiers.length;
    const baseTier = s.cake.tiers[0];
    const baseFlavorColor = D.flavors.find(f => f.id === baseTier?.flavor)?.color || "#F4E4BC";
    const baseTierColor = baseTier?.colorHex || "#FFFFFF";
    const tiersWithColors = s.cake.tiers.map(t => ({
      ...t,
      flavorColor: D.flavors.find(f => f.id === t.flavor)?.color || "#F4E4BC",
      color: t.colorHex,
    }));
    const preview = (
      <CakePreview
        tiers={tiersWithColors}
        sameForAll={false}
        baseFlavorColor={baseFlavorColor}
        baseTierColor={baseTierColor}
      />
    );
    return {
      folio: s.folio,
      client: s.client,
      delivery: s.delivery,
      cake: { ...s.cake, preview },
      extras: { items: totals.items },
      notes: s.notes,
    };
  }, [s, totals]);

  // ====== Guardar cliente (por folio) ======
  // Guarda con clave = folio de ESTA cotización, en el localStorage de este
  // navegador (no en la BD). El botón de "Recuperar por folio" se retiró: la
  // recuperación real de una cotización se hace desde /crm/cotizaciones, que
  // sí lee de la base de datos y no depende del navegador que se use.
  const leerDirectorio = () => {
    // localStorage puede traer JSON roto de versiones anteriores; si revienta
    // aquí, se caía toda la pantalla de "Recuperar".
    try { return JSON.parse(localStorage.getItem("deuscake.clients") || "{}") || {}; }
    catch (e) { return {}; }
  };

  const saveClient = () => {
    const key = (s.folio || "").trim().toUpperCase();
    if (!key) { alert("No hay folio para guardar."); return; }
    const dir = leerDirectorio();
    dir[key] = {
      client: s.client,
      delivery: { ...s.delivery, date: "", time: "" }, // dirección sí, fecha/hora no
      savedAt: new Date().toISOString(),
    };
    try {
      localStorage.setItem("deuscake.clients", JSON.stringify(dir));
      alert(`Cliente "${s.client.name || key}" guardado en este equipo.`);
    } catch (e) {
      alert("No se pudo guardar en este navegador (almacenamiento lleno o bloqueado).");
    }
  };

  const clearAll = () => {
    try { localStorage.removeItem("deuscake.draft"); } catch (e) {}
    setS(defaultState());
    setCotizacionId(null);
    setFotoReferencia(null);
    setTab("resumen");
    setView("editor");
    // Empezar de cero también reinicia el wizard: si no, quedabas parado en el
    // paso 5 de una cotización que ya se borró.
    setPaso(1);
    setMaxAlcanzado(1);
    setErroresPaso([]);
  };

  // ====== Precargar una solicitud del cliente (solo admin) ======
  // La lista y el botón "Abrir en Cotizador" viven en /admin/cotizador-solicitudes
  // (pantalla normal del admin, dentro del layout Dore). Ese botón abre esta
  // herramienta con ?solicitud=ID en la URL; aquí solo se detecta ese parámetro
  // al montar y se precarga la configuración completa que mandó el cliente.
  const [loadingSolicitud, setLoadingSolicitud] = useState(false);

  useEffect(() => {
    if (IS_CLIENT || !window.COTIZADOR_API_BASE) return;
    const idParam = new URLSearchParams(window.location.search).get("solicitud");
    if (!idParam) return;
    setLoadingSolicitud(true);
    const body = new FormData();
    body.append("id", idParam);
    fetch(window.COTIZADOR_API_BASE + "solicitud", { method: "POST", body })
      .then(r => r.json())
      .then(res => {
        setLoadingSolicitud(false);
        if (res && res.status === "success" && res.data) {
          setS(prev => ({ ...defaultState(), ...res.data }));
          // Ya viene todo capturado: se abren los 5 pasos para poder revisar
          // cualquiera sin tener que ir avanzando de uno en uno.
          setMaxAlcanzado(WIZARD_STEPS.length);
        } else {
          alert((res && res.message) || "No se pudo cargar la solicitud.");
        }
      })
      .catch(() => {
        setLoadingSolicitud(false);
        alert("Error de conexión al cargar la solicitud.");
      });
  }, []);

  // ====== Ver/reimprimir una cotización ya guardada ======
  // Desde /crm/cotizaciones el link "ver" abre esta misma herramienta con
  // ?cotizacion=ID; aquí se detecta al montar, se precarga el estado completo
  // guardado en su momento y se salta directo a Documentos > Cotización
  // (mismo documento con logo/folio que ya se veía en mariaschocolate).
  useEffect(() => {
    if (IS_CLIENT || !window.COTIZADOR_API_BASE) return;
    const idParam = new URLSearchParams(window.location.search).get("cotizacion");
    if (!idParam) return;
    setLoadingSolicitud(true);
    const body = new FormData();
    body.append("id", idParam);
    fetch(window.COTIZADOR_API_BASE + "ver", { method: "POST", body })
      .then(r => r.json())
      .then(res => {
        setLoadingSolicitud(false);
        if (res && res.status === "success" && res.data) {
          setS(prev => ({ ...defaultState(), ...res.data }));
          setMaxAlcanzado(WIZARD_STEPS.length);
          // Guarda con qué cotización de la BD queda vinculada esta edición,
          // para que "Guardar cotización en el sistema" actualice esta misma
          // fila en vez de crear una duplicada.
          setCotizacionId(res.id || Number(idParam) || null);
          setTab("resumen");
          setView("docs");
        } else {
          alert((res && res.message) || "No se pudo cargar la cotización.");
        }
      })
      .catch(() => {
        setLoadingSolicitud(false);
        alert("Error de conexión al cargar la cotización.");
      });
  }, []);

  // ====== Guardar cotización en la BD (Fase 4) ======
  // El servidor recalcula el total con calcularCotizacionPrecio() antes de
  // guardar, así que el precio que queda en la BD siempre es el autorizado
  // por el catálogo vigente, no el que trae el navegador.
  const [saving, setSaving] = useState(false);
  const [saveResult, setSaveResult] = useState(null);
  const [saveError, setSaveError] = useState(null);
  // Id de la cotización que se está EDITANDO (se llenó al abrir con
  // ?cotizacion=ID). Se guarda aparte de `s` a propósito: si viviera dentro
  // de `s` se colaría en "Exportar JSON" y, al importar ese archivo después,
  // el próximo "Guardar" pisaría por error la cotización original en vez de
  // crear una nueva.
  const [cotizacionId, setCotizacionId] = useState(null);
  // Foto de referencia (solo modo cliente, slide 7: "Captura datos... foto
  // referencia"). Se guarda aparte de `s` porque un File no se puede meter
  // en JSON.stringify — se sube en una segunda petición (multipart) una vez
  // que solicitar() ya creó el lead y sabemos su id.
  const [fotoReferencia, setFotoReferencia] = useState(null);

  const saveCotizacion = () => {
    if (!window.COTIZADOR_API_BASE) return;
    if (!s.client.name && !s.client.code && !s.client.phone) {
      alert("Captura al menos tu nombre o teléfono antes de enviar.");
      return;
    }
    setSaving(true);
    setSaveError(null);
    const payload = (!IS_CLIENT && cotizacionId) ? { ...s, cotizacion_id: cotizacionId } : s;
    fetch(window.COTIZADOR_API_BASE + (IS_CLIENT ? "solicitar" : "guardar"), {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(payload),
    })
      .then(r => r.json())
      .then(res => {
        setSaving(false);
        if (res && res.status === "success") {
          setSaveResult(res.data);
          if (!s.folio || s.folio !== res.data.folio) set("folio", res.data.folio);
          // A partir de aquí, si se vuelve a dar "Guardar" sin recargar la
          // página, se sigue actualizando esta misma cotización (no crea otra).
          if (!IS_CLIENT && res.data.id) setCotizacionId(res.data.id);
          // Ya se guardó en la BD — se borra el borrador local para que la
          // próxima vez que se abra el cotizador no "reviva" esta cotización
          // ya cerrada (admin). El cliente público nunca usa este borrador.
          if (!IS_CLIENT) {
            try { localStorage.removeItem("deuscake.draft"); } catch (e) {}
          }
          // Modo cliente: si adjuntó una foto de referencia, se sube aparte
          // (multipart) ligada al lead que se acaba de crear.
          if (IS_CLIENT && fotoReferencia && res.data.lead_id) {
            const fd = new FormData();
            fd.append("lead_id", res.data.lead_id);
            fd.append("archivo", fotoReferencia);
            fetch(window.COTIZADOR_API_BASE + "foto-referencia", { method: "POST", body: fd }).catch(() => {});
            setFotoReferencia(null);
          }
        } else {
          setSaveError((res && res.message) || "No se pudo guardar la cotización.");
        }
      })
      .catch(() => {
        setSaving(false);
        setSaveError("Error de conexión al guardar la cotización.");
      });
  };

  // ====== Importar / exportar JSON ======
  const fileRef = useRef(null);
  const exportJson = () => {
    const blob = new Blob([JSON.stringify(s, null, 2)], { type: "application/json" });
    const url = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = url;
    a.download = `cotizacion-${s.folio}-${(s.client.name || "cliente").replace(/\s+/g, "_")}.json`;
    a.click();
    setTimeout(() => URL.revokeObjectURL(url), 1000);
  };
  const importJson = (e) => {
    const f = e.target.files?.[0]; if (!f) return;
    const reader = new FileReader();
    reader.onload = () => {
      try {
        const parsed = JSON.parse(reader.result);
        if (parsed && parsed.cake && parsed.client) {
          setS(parsed);
          setMaxAlcanzado(WIZARD_STEPS.length);
          // Un JSON importado es un punto de partida nuevo, no la misma fila
          // de la BD que se estuviera editando — si no se limpia, "Guardar"
          // pisaría esa otra cotización por error.
          setCotizacionId(null);
          alert("Cotización cargada.");
        } else alert("Archivo no válido.");
      } catch (err) { alert("No se pudo leer el archivo."); }
    };
    reader.readAsText(f);
    e.target.value = "";
  };

  // ====== Imprimir / PDF ======
  const onPrint = () => {
    document.body.classList.add("printing-" + tab);
    setTimeout(() => {
      window.print();
      setTimeout(() => document.body.classList.remove("printing-" + tab), 500);
    }, 100);
  };
  const onPdf = onPrint; // mismo flujo: el navegador permite "Guardar como PDF"

  // ====== Switch a docs ======
  const goToTab = (t) => {
    setTab(t);
    setView("docs");
    setTimeout(() => {
      document.querySelector(".docs-stack")?.scrollTo({ top: 0, behavior: "smooth" });
    }, 50);
  };

  // ====== Preview principal (sidebar) ======
  const sidebarPreview = useMemo(() => {
    const tiersWithColors = s.cake.tiers.map(t => ({
      flavorColor: D.flavors.find(f => f.id === t.flavor)?.color || "#F4E4BC",
      color: t.colorHex,
      tierType: t.tierType,
    }));
    return <CakePreview tiers={tiersWithColors} sameForAll={false} />;
  }, [s.cake.tiers]);

  return (
    <div className="app">
      <TopBar
        folio={s.folio}
        view={view} setView={setView}
        onExport={exportJson} onImport={() => fileRef.current?.click()}
        onClear={clearAll}
        mode={MODE}
      />

      {loadingSolicitud && !IS_CLIENT && (
        <div className="modal-backdrop-cot">
          <div className="modal-cot" style={{ padding: "20px 24px" }}>
            Cargando cotización…
          </div>
        </div>
      )}
      <input type="file" accept="application/json" ref={fileRef} style={{display:"none"}} onChange={importJson} />

      {(view === "editor" || IS_CLIENT) ? (
        <div className="layout">
          <main className="forms-col">
            <WizardNav current={paso} maxAlcanzado={maxAlcanzado} onGo={irAPaso} />

            {paso === 1 && (
              <ClienteForm s={s} set={set} onSave={saveClient} onClear={clearAll} />
            )}
            {paso === 2 && <EntregaForm s={s} set={set} D={D} />}
            {paso === 3 && (
              <>
                <CakeGeneralForm s={s} set={set} D={D} totals={totals} />
                <TiersForm s={s} set={set} D={D} />
              </>
            )}
            {paso === 4 && <ExtrasForm s={s} set={set} D={D} />}
            {paso === 5 && (
              <CierreForm s={s} set={set} totals={totals}
                onPrint={() => { setView("docs"); setTimeout(onPrint, 200); }}
                onPdf={() => { setView("docs"); setTimeout(onPdf, 200); }}
                onTab={goToTab}
                onSave={saveCotizacion} saving={saving} saveResult={saveResult} saveError={saveError}
                editando={!!cotizacionId}
                onFotoChange={setFotoReferencia} fotoNombre={fotoReferencia?.name} />
            )}

            <WizardFooter current={paso} onPrev={pasoAnterior} onNext={pasoSiguiente} errores={erroresPaso} />
          </main>

          <aside className="preview-col">
            <div className="preview-sticky">
              <header className="preview-header">
                <span className="dot" /> Vista previa del pastel
              </header>
              <div className="preview-canvas">{sidebarPreview}</div>
              <div className="preview-summary">
                <div className="ps-row">
                  <span>{s.cake.tiers.length} piso{s.cake.tiers.length > 1 ? "s" : ""}</span>
                  <strong>{s.cake.people} personas</strong>
                </div>
                {!IS_CLIENT && (
                  <div className="ps-row big">
                    <span>Total</span>
                    <strong className="amount">{fmt(totals.total)}</strong>
                  </div>
                )}
                {!IS_CLIENT && totals.advance > 0 && (
                  <div className="ps-row">
                    <span>Saldo a pagar</span>
                    <strong>{fmt(totals.balance)}</strong>
                  </div>
                )}
              </div>
            </div>
          </aside>
        </div>
      ) : (
        <DocsView tab={tab} setTab={setTab} setView={setView}
          s={stateForDocs} totals={totals}
          onPrint={onPrint} />
      )}
    </div>
  );
}

function TopBar({ folio, view, setView, onExport, onImport, onClear, mode }) {
  const isClient = mode === "cliente";
  return (
    <header className="topbar">
      <div className="brand">
        <img src={(window.COTIZADOR_BASE || "") + "assets/deus-cake-logo.png"} alt="Deus Cake" />
        <div className="brand-text">
          <strong>{isClient ? "SOLICITAR COTIZACIÓN" : "COTIZADOR"}</strong>
          <em>en línea</em>
        </div>
      </div>
      <div className="topbar-right">
        {!isClient && <span className="folio-tag">Folio · {folio}</span>}
        {!isClient && (
          <div className="seg topseg">
            <button className={view === "editor" ? "seg-on" : ""} onClick={() => setView("editor")}>Editor</button>
            <button className={view === "docs" ? "seg-on" : ""} onClick={() => setView("docs")}>Documentos</button>
          </div>
        )}
        {!isClient && <button className="btn ghost" onClick={onImport}>Importar JSON</button>}
        {!isClient && <button className="btn ghost" onClick={onExport}>Exportar JSON</button>}
        <button className="btn ghost danger" onClick={onClear}>{isClient ? "Empezar de nuevo" : "Nueva cotización"}</button>
      </div>
    </header>
  );
}

function DocsView({ tab, setTab, setView, s, totals, onPrint }) {
  return (
    <div className="docs-view">
      <div className="docs-toolbar">
        <button className="btn ghost" onClick={() => setView("editor")}>← Volver al editor</button>
        <div className="seg docsseg">
          <button className={tab === "resumen" ? "seg-on" : ""} onClick={() => setTab("resumen")}>Cotización</button>
          <button className={tab === "cliente" ? "seg-on" : ""} onClick={() => setTab("cliente")}>Cliente</button>
          <button className={tab === "cocina" ? "seg-on" : ""} onClick={() => setTab("cocina")}>Cocina</button>
          <button className={tab === "reparto" ? "seg-on" : ""} onClick={() => setTab("reparto")}>Reparto</button>
        </div>
        <button className="btn dark" onClick={onPrint}>🖨  Imprimir / PDF</button>
      </div>

      <div className="docs-stack">
        {tab === "resumen" && <DocResumen s={s} totals={totals} />}
        {tab === "cliente" && <DocCliente s={s} totals={totals} />}
        {tab === "cocina" && <DocCocina s={s} />}
        {tab === "reparto" && <DocReparto s={s} totals={totals} />}
      </div>
    </div>
  );
}

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