// ─── Documento del expediente: Presupuesto ───────────────────────────────
// PresupuestoScreen: primer documento del pipeline (INICIADO → PRESUPUESTO).
// Extraída de screens-docs.jsx en P7 junto con las otras 6 pantallas de
// documento. Expone a window: PresupuestoScreen. Consume window.
// resolveDocExp (screens/docs/compartido.jsx), DocumentShell/DocCard/
// PartyBlock/Totals/MedicalContextCard/buildEmisorFields/buildClienteFields
// (ui/doc-kit-shell.jsx), DocMetaStrip (ui/doc-kit-meta.jsx), ItemsTable (ui/
// doc-kit-items.jsx), useDocEdit/buildDocActions (ui/doc-kit-edit.jsx),
// emptyPresupuesto/saveDocOnExp (dominio/expediente-docs.js),
// MicosSyncMargenNotif (dominio/margen.js), window.MicosItemsMath
// (doc-itemmath.js), window.MicosDocPresupuesto/openPrintDoc (print/
// doc-presupuesto.js / print/print-viewer.jsx), COMPANY_CONFIG/formatDate (colecciones/
// dominio/formato.js), window.PartySelectorModal (ui/party-selector.jsx)/ENTIDADES
// (core/colecciones.js) — DECISIÓN 4 (06/07/2026): lápiz "Cambiar cliente", antes no-op,
// ahora reusa PartySelectorModal (mismo patrón de Factura/Recibo standalone) — guarda
// `entidadId` EN ESTE PRESUPUESTO (`presupuestos.entidad_id`, migración 20260706100000)
// sin tocar la FK del expediente; sin override sigue mostrando el cliente de siempre.
//
// FIX P7 (bug #2 de la review P5, addenda del arquitecto — demo-crítico):
// esta era la ÚNICA de las 7 pantallas de documento que NO usaba useDocEdit
// (reimplementaba a mano editing/draft/enterEdit/discardEdit/saveEdit/patch)
// y, al armar `docObj` a mano en vez de partir de `{...original, ...}` como
// las demás, era la única que NO pasaba `id: original.id` al guardar — con
// `idDocumentoExistente()` decidiendo ahora por la existencia real del doc
// (ver dominio/expediente-docs.js), un presupuesto real sin `id` en el
// payload se trataba como inexistente y se intentaba CREAR de nuevo, contra
// una tabla con `expediente_id` UNIQUE → 23505 duplicate-key. Migrar al mismo
// patrón useDocEdit que ya usan OC/Remito/Factura/Recibo (que sí propagan
// `id` al spread de `original`) resuelve las dos causas de una vez.
//
// EXCEPCIÓN AL TECHO DE ~250 LÍNEAS (regla nueva de la compuerta, P7 §3):
// ~300 líneas — UN SOLO TEMA (la pantalla completa de Presupuesto: draft +
// totales en vivo + acciones + todo el render), no dos temas mezclados; no
// hay un corte natural sin fragmentar en sub-componentes sin nombre propio.
// Bajo el techo duro de 400.
//
// FIX blocker P8 (hallado durante la verificación en vivo de H1): el
// DocMetaStrip en modo NO-editing leía `exp.vendedor.name` sin guarda —
// crash real (no F5, navegación normal "Crear presupuesto" recién creado
// el expediente) porque el objeto optimista recién creado en window.
// EXPEDIENTES trae `vendedorId` pero el embed `vendedor` todavía no está
// resuelto hasta la próxima hidratación. Mismo criterio que ya usaba esta
// función 2 líneas arriba (`exp.vendedor?.name || ''`) y que remito.jsx
// (línea análoga) ya aplicaba — acá quedó sin la guarda por asimetría.
// Fuera del alcance de archivos de P8 (este screen es de P7) pero
// bloqueaba la verificación obligatoria del flujo completo; documentado
// acá en vez de silenciarlo.
//
// FIX P13 (A0, hallazgo ALTO de la consolidación): la línea del PartyBlock
// "Cliente" leía `exp.particular.nombre`/`.apellido` SIN guarda — crash real
// (pantalla en blanco, sin error boundary) en el camino "alta → Guardar →
// Crear presupuesto" SIN recarga: la causa de fondo era el id-race de
// Expedientes (ver fix en core/app.jsx/screens/expedientes/alta.jsx, mismo
// patrón que ya resolvía RecordFormScreen/P10), que dejaba `expediente=null`
// llegar acá justo en la navegación → `resolveDocExp(null)` → `exp = {}` →
// `exp.particular` ni siquiera existe como key. Con el id-race arreglado esto
// ya no debería disparar, pero la guarda queda de cinturón (era una de las 5
// copias del baseline de esta misma expresión frágil, `77b36f5` — las otras
// 2 de docs/finanzas ya se habían blindado en P7/P8; ver orden-compra.jsx y
// remito.jsx, que tenían la misma copia sin blindar hasta este fix).

// `startEditing` (opcional): lo inyecta DocSubrouteGate (screens/docs/compartido.jsx)
// en un F5/deep-link directo a esta sub-ruta, para abrir el documento REAL ya en
// edición (datos reales, no un alta en blanco). En navegación normal llega false y no
// cambia nada (startEditing sigue siendo isCreate).
function PresupuestoScreen({ expediente, onBack, startEditing = false }) {
  const exp = resolveDocExp(expediente);
  const co = COMPANY_CONFIG;
  const isCreate = !exp.presupuesto;
  const original = React.useMemo(() => exp.presupuesto || emptyPresupuesto(exp), [exp.presupuesto]);
  const [toast, setToast] = React.useState(null);
  // DECISIÓN 4 (06/07/2026): lápiz "Cambiar cliente" — antes no-op (`onClick={() => {}}`).
  const [clientePicker, setClientePicker] = React.useState(false);

  const { editing, draft, setDraft, enterEdit, discardEdit, saveEdit, patch } = useDocEdit(() => ({
    numero: original.numero,
    fechaEmision: original.fechaEmision,
    fechaValidez: original.fechaValidez,
    // DECISIÓN 4: override opcional del cliente de este documento (`presupuestos.entidad_id`).
    entidadId: original.entidadId || null,
    // FIX hallazgo #6 (AUDITORIA-ui-decorativa-05-07-2026.md): si el presupuesto YA tiene su
    // propio vendedor/vendedorAux/formaPago guardado (columnas reales, texto libre — distinto
    // del vendedor_id/vendedor_aux_id del EXPEDIENTE, que es una FK), reabrirlo para editar
    // debe partir de ESO, no siempre re-derivar del vendedor asignado al expediente.
    vendedor: original.vendedor || exp.vendedor?.name || '',
    vendedorAux: original.vendedorAux || exp.vendedorAux?.name || '',
    condicionVenta: original.formaPago || exp.entidad?.condicionVenta || 'Cuenta corriente — 30 días',
    // FIX (hallazgo "Moneda", AUDITORIA-ui-decorativa-05-07-2026.md): arrancaba SIEMPRE
    // hardcodeado a 'Pesos argentinos' en vez de partir de lo persistido (original.moneda).
    moneda: original.moneda || 'Pesos argentinos',
    aprobacion: 'Pendiente del cliente',
    observaciones: original.observaciones || '',
    items: original.items.map((it) => window.MicosItemsMath.migrate(it)),
    bonificacionesTotal: original.bonificacionesTotal,
    ivaDesglose: original.ivaDesglose,
    importeLetras: original.importeLetras,
  }), {
    startEditing: isCreate || startEditing,
    onCancel: () => { if (isCreate) onBack(); },
    onSave: (d) => {
      // Cada ítem persiste su `total` = subtotal DE LÍNEA (base de venta + IVA − bonif,
      // window.MicosItemsMath.normalize().subtotal), NO el costo neto (cant×costo×(1−bonif))
      // que recalculaba `patchItem` en cada tecla. Invariante del pipeline (flujo 2 herencia):
      // sum(presupuesto_item.total) == presupuestos.total — y presupuestos.total = fin.total
      // (summary con IVA+ganancia), así que la línea persistida tiene que ser el subtotal con
      // IVA para que la suma cierre. Nada muestra este `total` crudo (el print/PDF y las
      // pantallas recalculan con normalize(); v_expediente_financiero usa las columnas de
      // cabecera, no la hija) — es una denormalización interna, hoy incoherente con la cabecera.
      const M = window.MicosItemsMath;
      const items = (d.items || []).map((it) => ({ ...it, total: M._round(M.normalize(it).subtotal) }));
      const fin = computeItemsFinancials(items);
      const docObj = {
        id: original.id, // FIX P7 (bug #2 addenda): sin esto, saveDocOnExp no podía distinguir
                          // "editar el presupuesto real" de "crear uno nuevo" tras un F5. Además
                          // (H-JB1, Juez B) es el mismo `id` el que necesita MicosSyncMargenNotif
                          // para no caer siempre en la rama "alta nueva — se omite el aviso".
        numero: d.numero,
        fechaEmision: d.fechaEmision,
        fechaValidez: d.fechaValidez,
        // DECISIÓN 4: persiste el override de cliente elegido con el lápiz (o `null` si
        // nunca se tocó / se dejó en el cliente del expediente).
        entidadId: d.entidadId || null,
        observaciones: d.observaciones || '',
        importeLetras: d.importeLetras || '',
        // FIX hallazgo #6 (AUDITORIA-ui-decorativa-05-07-2026.md): docObj NO copiaba estos 3
        // campos del draft — se perdían en el onSave aunque el usuario los tipeara (doble
        // muerte con la whitelist de PRESUPUESTO_COLUMNS, arreglada aparte en route.ts).
        vendedor: d.vendedor || '',
        vendedorAux: d.vendedorAux || '',
        formaPago: d.condicionVenta || '',
        // FIX (hallazgo "Moneda"): docObj no copiaba `d.moneda` en absoluto — el select se
        // editaba en pantalla pero el valor elegido nunca viajaba al backend.
        moneda: d.moneda || '',
        subtotal: fin.neto,
        bonificacionesTotal: fin.bonif,
        ivaTotal: fin.ivaTotal,
        // FIX P7 (H-JB3, Juez B): `total` es columna REAL de presupuestos (whitelist
        // PRESUPUESTO_COLUMNS) y el "financiero" del expediente se deriva de ella
        // (serializeExpediente, presupuestos.total vía SELECT_FULL) — sin mandarla acá
        // quedaba NULL en la DB para todo presupuesto creado desde la UI, y tras un F5
        // el expediente mostraba "sin presupuesto" pese a existir.
        total: fin.total,
        ivaDesglose: fin.ivaLines,
        items,
      };
      saveDocOnExp(exp, 'presupuesto', docObj, {
        costosSinIva: fin.costos,
        ganancia: fin.ganancia,
        gananciaPct: fin.gananciaPct,
        iva: fin.ivaTotal,
        total: fin.total,
      });
      // Aviso de margen: productos con ganancia < umbral → notificación (no bloquea).
      try { MicosSyncMargenNotif(exp, docObj, items, fin); } catch (_) {}
      setToast(`Presupuesto PRE-${docObj.numero} ${isCreate ? 'creado' : 'guardado'}`);
      setTimeout(() => setToast(null), 3600);
    },
  });

  // The "live" data being displayed — either the original or the in-flight draft.
  const p = editing && draft ? draft : original;

  // DECISIÓN 4: cliente = override del DOCUMENTO (p.entidadId) o el del expediente (de
  // siempre) — resolveClienteDocumento (compartido.jsx) compartida con orden-compra/remito.
  const cliente = resolveClienteDocumento(exp, p.entidadId);

  const patchItem = (idx, field, value) => setDraft((d) => {
    const items = d.items.map((it, i) => {
      if (i !== idx) return it;
      if (field === 'subtotal') {
        // DECISIÓN A2 (07/07/2026, pedido literal de Fran): al editar el SUBTOTAL de la línea
        // (único caso que dispara esta rama — ItemsTable solo emite field='subtotal' cuando
        // `subtotalEditable`, que acá está prendido A PROPÓSITO y en ningún otro documento) se
        // resta primero el IVA, después el precio unitario (si ya había uno cargado) y lo que
        // queda es la ganancia — MicosItemsMath.applySubtotalInverso (doc-itemmath.js, única
        // fuente de verdad) hace la cuenta. El resto de los campos de la línea no se tocan.
        const { unitario, gananciaPct } = window.MicosItemsMath.applySubtotalInverso(it, value);
        return { ...it, unitario, gananciaPct };
      }
      const next = { ...it, [field]: value };
      // Auto-recompute row total
      const cant = Number(next.cantidad) || 0;
      const unit = Number(next.unitario) || 0;
      const bonif = Number(next.bonifPct) || 0;
      next.total = cant * unit * (1 - bonif / 100);
      return next;
    });
    return { ...d, items };
  });
  const addItem = () => setDraft((d) => ({
    ...d,
    items: [...d.items, window.MicosItemsMath.blank()],
  }));
  const removeItem = (idx) => setDraft((d) => ({
    ...d,
    items: d.items.filter((_, i) => i !== idx),
  }));

  // ── Computed totals (live) ────────────────────
  const finP = window.MicosItemsMath.summary(p.items);
  const netoGravado = finP.totalBase;           // Subtotal mostrado = base de venta bruta
  const bonificacionesTotal = finP.bonifTotal;
  const ivaLines = finP.ivaLines;
  const total = finP.total;

  // ── Header actions (swap by mode) ─────────────
  const onPrint = () => {
    // Reresolver el documento fresco del store para imprimir SIEMPRE lo último guardado.
    const fresh = resolveDocExp(expediente);
    const doc = fresh.presupuesto || p;
    const data = window.MicosDocPresupuesto.fromExpediente(fresh, co, doc);
    window.openPrintDoc(data, {
      // FIX (P15): antes hardcodeaba el prefijo (`PRE-${doc.numero}`) — con el
      // namespace legacy `L-` (P15) eso mostraba "PRE-00002103" sin el `L-`
      // (doc.numero ya viene sin prefijo, ver numeroFromPublicId en rest.ts).
      // `doc.publicId` es el id real de la fila, con o sin `L-` según corresponda.
      title: `Presupuesto ${doc.publicId}`,
      subtitle: `${fresh.publicId}${fresh.entidad?.razonSocial ? ' · ' + fresh.entidad.razonSocial : ''}`,
    });
  };
  // Puente ERP→CRM (carril B-erp): "Enviar por mail" con este presupuesto adjunto.
  // `doc` = publicId real (PRE-…, o L-PRE-… legacy) para que el CRM lo matchee.
  const onEmail = window.buildDocEmailAction && window.buildDocEmailAction({
    tipo: 'Presupuesto',
    docId: original.publicId || `PRE-${original.numero}`,
    expPublicId: exp.publicId,
    fields: cliente.fields,
  });
  const actions = buildDocActions({ editing, enterEdit, discardEdit, saveEdit, onPrint, onEmail });

  return (
    <DocumentShell
      onBack={onBack}
      eyebrow="Presupuesto"
      // Pill = estado PERSISTIDO real (presupuestos.estado: BORRADOR/APROBADO/RECHAZADO/VENCIDO),
      // no un rótulo fijo. Fallback a 'Enviado al cliente' solo si todavía no tiene estado.
      statusLabel={editing ? 'Editando' : (((window.PRESUP_ESTADOS || {})[original.estado] || {}).label || original.estado || 'Enviado al cliente')}
      statusPill="pill-presupuesto"
      // FIX (P15, hallazgo H2 de la review): antes `PRE-${p.numero}` — dropeaba
      // el `L-` de un presupuesto legacy (mostraba "PRE-00002103" en vez de
      // "L-PRE-00002103", el objetivo #1 de la spec pide que se vea A SIMPLE
      // VISTA). `original.publicId` es el id real de la fila una vez creada
      // (con o sin `L-`); en modo alta (antes de guardar, sin fila real
      // todavía) cae al mismo placeholder de siempre.
      bigId={original.publicId || `PRE-${original.numero}`}
      expedienteId={exp.publicId}
      fechaEmision={p.fechaEmision}
      editing={editing}
      isCreate={isCreate}
      toast={toast}
      actions={actions}>

      {/* Paciente + Médico (read-only — viene del expediente) */}
      <MedicalContextCard exp={exp} />

      {/* Datos del documento + información comercial unificados */}
      <DocMetaStrip editing={editing} items={[
        {
          label: 'Número',
          // FIX (P15, H2): idem bigId de arriba — mostrar el publicId real
          // (con `L-` si es legacy), no reconstruir el prefijo a mano.
          value: original.publicId || `PRE-${original.numero}`,
          mono: true, accent: true,
          // Auto-generado — nunca editable
        },
        {
          label: 'Emisión',
          value: editing ? draft.fechaEmision : formatDate(original.fechaEmision),
          mono: true,
          editable: editing,
          inputType: 'date',
          onChange: (v) => patch('fechaEmision', v),
        },
        {
          label: 'Validez hasta',
          value: editing ? draft.fechaValidez : formatDate(original.fechaValidez),
          mono: true,
          editable: editing,
          inputType: 'date',
          onChange: (v) => patch('fechaValidez', v),
        },
        // Expediente never editable — tied to the parent expediente
        { label: 'Expediente', value: exp.publicId, mono: true, accent: true },
        {
          label: 'Vendedor',
          value: editing ? draft.vendedor : (original.vendedor || exp.vendedor?.name || 'Sin vendedor'),
          editable: editing,
          placeholder: 'Nombre del vendedor',
          onChange: (v) => patch('vendedor', v),
        },
        {
          label: 'Vendedor auxiliar',
          value: editing ? draft.vendedorAux : (original.vendedorAux || exp.vendedorAux?.name || '—'),
          editable: editing,
          placeholder: 'Nombre del vendedor auxiliar (opcional)',
          onChange: (v) => patch('vendedorAux', v),
        },
        {
          label: 'Forma de pago',
          value: editing ? draft.condicionVenta : (original.formaPago || exp.entidad?.condicionVenta || 'Cuenta corriente — 30 días'),
          editable: editing,
          placeholder: 'Ej: Cuenta corriente, contado…',
          onChange: (v) => patch('condicionVenta', v),
        },
        {
          label: 'Moneda',
          // FIX (hallazgo "Moneda"): modo lectura mostraba SIEMPRE 'Pesos argentinos' fijo,
          // ignorando lo persistido — mismo criterio que Forma de pago arriba.
          value: editing ? draft.moneda : (original.moneda || 'Pesos argentinos'),
          editable: editing,
          inputType: 'select',
          options: ['Pesos argentinos', 'Dólares (USD)', 'Euros (EUR)'],
          onChange: (v) => patch('moneda', v),
        },
      ]} />

      {/* Emisor (fijo, Micos) + Cliente (particular/entidad del expediente, o el override de
          ESTE presupuesto elegido con el lápiz — DECISIÓN 4, antes no-op) */}
      <DocCard>
        <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
          <PartyBlock
            eyebrow="Emisor"
            name={co.nombre}
            sub={co.situacionIva}
            fields={buildEmisorFields(co)} />

          <PartyBlock
            eyebrow="Cliente"
            name={cliente.nombre}
            sub={cliente.sub}
            fields={cliente.fields}
            onEdit={editing ? () => setClientePicker(true) : undefined}
            editLabel="Cambiar cliente" />
        </div>
      </DocCard>

      {editing && clientePicker && window.PartySelectorModal && (
        <window.PartySelectorModal
          type="entidad"
          open={true}
          onClose={() => setClientePicker(false)}
          onPick={(obj) => {
            if (obj && obj.id) patch('entidadId', obj.id);
            setClientePicker(false);
          }}
        />
      )}

      {/* Items + Totals */}
      <DocCard editing={editing}>
        <ItemsTable
          items={p.items}
          showGanancia
          showBonif
          showIva
          editing={editing}
          onItemChange={patchItem}
          onItemRemove={removeItem}
          onAdd={addItem}
          // DECISIÓN A2 (07/07/2026): subtotal por ítem editable SOLO acá (Presupuesto) — ver
          // patchItem arriba y MicosItemsMath.applySubtotalInverso (doc-itemmath.js).
          subtotalEditable
        />
        <Totals
          subtotal={netoGravado}
          bonificaciones={bonificacionesTotal}
          ivaLines={ivaLines}
          total={total}
          importeLetras={editing ? draft.importeLetras : original.importeLetras} />

      </DocCard>

      {/* Observaciones */}
      {(editing || original.observaciones) &&
      <DocCard editing={editing}>
          <p className="text-[11px] font-bold uppercase tracking-[0.2em] mb-5" style={{ color: 'var(--accent)' }}>
            Observaciones
          </p>
          {editing ? (
            <textarea
              value={draft.observaciones}
              onChange={(e) => patch('observaciones', e.target.value)}
              rows={3}
              placeholder="Notas, condiciones particulares o instrucciones para el cliente…"
              className="edit-textarea text-[14px] font-bold leading-snug"
            />
          ) : (
            <p className="text-[14px] font-bold leading-snug whitespace-pre-line" style={{ color: 'var(--foreground)' }}>
              {original.observaciones}
            </p>
          )}
        </DocCard>
      }
    </DocumentShell>);

}

// Arma { subtotalRaw/bonif/neto/ivaLines/ivaTotal/costos/ganancia/total/
// gananciaPct } desde los ítems del presupuesto — helper de presentación de
// ESTA pantalla puntual (arma el payload financiero para saveEdit), no del
// hub de persistencia (dominio/expediente-docs.js). Renombrado sin "_" al
// partirse el archivo en P7 (spec §6.3), antes `_computeItemsFinancials`.
function computeItemsFinancials(items) {
  const s = window.MicosItemsMath.summary(items);
  return {
    subtotalRaw: s.totalBase, bonif: s.bonifTotal, neto: s.neto,
    ivaLines: s.ivaLines, ivaTotal: s.ivaTotal, costos: s.costoTotal, ganancia: s.gananciaTotal,
    total: s.total,
    gananciaPct: Math.round(s.gananciaPct),
  };
}

Object.assign(window, { PresupuestoScreen });
