// ─────────────────────────────────────────────────────
// GUT-256 — Albarán de entrega (plantilla de Juanjo). Hoja con la que el material
// sale de fábrica hacia la obra con el instalador, que la firma al recibirlo.
//
// Se abre desde Expediente › Materiales y desde la ficha del proyecto en
// Programación con:
//   window.abrirAlbaran({ pedidoId })     expediente (pedidos.id)
//   window.abrirAlbaran({ proyectoId })   proyecto de Programación
//   onSaved (opcional): se llama tras emitir cada albarán (p. ej. refrescar "Entregadas")
// El modal se monta en su propio nodo: quien lo llama no guarda estado.
// GUT-272: emitir el albarán pone sus líneas en «Enviado». Solo se deshace con
// «Anular» en Albaranes emitidos: el albarán se queda (tachado, con su número)
// y las líneas vuelven a su estado anterior.
// GUT-272 (Juanjo 8 oct): por línea se ve Cant., Enviadas, Pendientes y el stock
// disponible (artículos del catálogo). No se envía más de lo pendiente ni más del
// stock; la línea queda «Enviado» al completar la cantidad (antes, «Enviado 4/5»).
// Emitir el albarán saca las uds del stock; anularlo las devuelve.
// INC-009: las líneas de un kit salen juntas bajo una fila «KIT …» (con casilla para
// marcar todo el kit) y el albarán impreso las lista como «KIT …:» con sus componentes.
// INC-013: en «Albaranes emitidos» se despliegan las líneas y se puede deshacer UNA
// (vuelve al stock, deja de contar como enviada y no sale al imprimir).
// API: /api/albaranes (src/routes/albaranes.js).
// ─────────────────────────────────────────────────────

const albHoy = () => new Date().toISOString().slice(0, 10);
const albFecha = (v) => (v ? String(v).slice(0, 10).split('-').reverse().join('/') : '');
const albNum = (v) => (v == null || v === '' ? '' : String(Math.round(Number(v) * 100) / 100).replace('.', ','));

// PDF en una pestaña nueva (desde ahí se imprime). La pestaña se abre en el mismo
// clic, antes de cualquier await, para que el navegador no la bloquee como emergente.
async function albAbrirPdf(id, numero, w = window.open('', '_blank')) {
  try {
    const res = await API.request(`/api/albaranes/${id}/pdf`);
    if (!res.ok) throw new Error(`HTTP ${res.status}`);
    const url = URL.createObjectURL(await res.blob());
    if (w) w.location.href = url;
    else await API.download(`/api/albaranes/${id}/pdf`, `${numero}.pdf`);
  } catch (e) {
    if (w) w.close();
    throw e;
  }
}
const albExcel = (id) => API.download(`/api/albaranes/${id}/excel`);

// GUT-272: lo que se puede enviar ahora de una línea (pendientes y, si es del catálogo,
// stock disponible). `pendientes`/`disponible` = null → sin límite por ese lado.
const albMax = (l) => Math.min(l.pendientes ?? Infinity, l.disponible ?? Infinity);
const albBloqueo = (l) => {
  if (l.pendientes === 0) return 'Enviado al completo — aumenta la cantidad para enviar más';
  if (l.disponible != null && l.disponible <= 0) return `Sin stock (${albNum(l.stock) || 0})`;
  return '';
};
const albErrorUds = (l) => {
  const s = String(l.uds ?? '').trim();
  const n = Number(s.replace(',', '.'));
  if (!s || !Number.isFinite(n) || n <= 0) return 'Indica las uds';
  if (l.pendientes != null && n > l.pendientes + 1e-9) return `Máx. ${albNum(l.pendientes)} (pendientes)`;
  if (l.disponible != null && n > l.disponible + 1e-9) return `Stock insuficiente (${albNum(l.disponible)})`;
  return '';
};
// Por defecto las preparadas; si no hay ninguna, todas. Las enviadas o bloqueadas, nunca.
const albPorEnviar = (l) => l.estado_material !== 'enviado' && !albBloqueo(l);
const albPrepararLineas = (ls) => {
  const hayPreparadas = ls.some((l) => l.preparado && albPorEnviar(l));
  return ls.map((l) => {
    const max = albMax(l);
    const uds = Number.isFinite(max) ? max : l.cantidad; // input numérico: con punto decimal
    return { ...l, on: albPorEnviar(l) && (hayPreparadas ? l.preparado : true), uds: uds == null ? '' : String(Math.round(uds * 10000) / 10000) };
  });
};

const AlbaranModal = ({ pedidoId, proyectoId, onClose, onSaved }) => {
  const [ctx, setCtx] = React.useState(null);
  const [error, setError] = React.useState('');
  const [f, setF] = React.useState({ numero_pedido: '', fecha: albHoy(), instalador_ids: [], observaciones: '' });
  const [lineas, setLineas] = React.useState([]); // [{...linea, on, uds}]
  const [emitidos, setEmitidos] = React.useState([]);
  const [busy, setBusy] = React.useState(false);
  const [hecho, setHecho] = React.useState(null); // albarán recién guardado

  const qs = pedidoId ? `pedido_id=${pedidoId}` : `proyecto_id=${proyectoId}`;
  const cargarEmitidos = React.useCallback(() => API.get(`/api/albaranes?${qs}`).then(setEmitidos).catch(() => {}), [qs]);
  React.useEffect(() => {
    API.get(`/api/albaranes/contexto?${qs}`).then((c) => {
      setCtx(c);
      setF((x) => ({ ...x, numero_pedido: c.numero_pedido || '', instalador_ids: c.instaladores_asignados.map((i) => i.id) }));
      setLineas(albPrepararLineas(c.lineas));
    }).catch((e) => setError(e.message || 'No se pudo cargar'));
    cargarEmitidos();
  }, [qs]);
  React.useEffect(() => {
    const k = (e) => { if (e.key === 'Escape') onClose(); };
    window.addEventListener('keydown', k);
    return () => window.removeEventListener('keydown', k);
  }, []);

  const set = (k, v) => setF((x) => ({ ...x, [k]: v }));
  const setLinea = (id, cambios) => setLineas((ls) => ls.map((l) => (l.id === id ? { ...l, ...cambios } : l)));
  const marcadas = lineas.filter((l) => l.on);
  const enviables = lineas.filter((l) => !albBloqueo(l));
  const todas = enviables.length > 0 && marcadas.length === enviables.length;
  const conError = marcadas.filter((l) => albErrorUds(l));
  const nombreInst = (id) => ctx?.instaladores.find((i) => i.id === id)?.nombre || `#${id}`;

  const guardar = async (despues) => {
    if (busy) return;
    if (!marcadas.length) { setError('Marca al menos una línea'); return; }
    if (conError.length) { setError(`Revisa las uds: ${conError.map((l) => `${l.nombre} (${albErrorUds(l)})`).join(' · ')}`); return; }
    setBusy(true); setError('');
    const w = despues === 'pdf' ? window.open('', '_blank') : null;
    try {
      const a = await API.post('/api/albaranes', {
        pedido_id: ctx.pedido_id, programacion_proyecto_id: ctx.programacion_proyecto_id,
        numero_pedido: f.numero_pedido, fecha: f.fecha, instalador_ids: f.instalador_ids, observaciones: f.observaciones,
        lineas: marcadas.map((l) => ({ bom_linea_id: l.id, referencia: l.codigo, cortina: l.cortina, descripcion: l.nombre,
          largo: l.largo, ancho: l.ancho, alto: l.alto, ral: l.ral, uds: l.uds, unidad: l.unidad,
          kit: l.kit_nombre, kit_grupo: l.kit_grupo })),
      });
      setHecho(a);
      cargarEmitidos();
      recargarCtx();
      if (onSaved) { try { onSaved(a); } catch (_) { /* el refresco del llamante no bloquea el albarán */ } }
      if (despues === 'pdf') await albAbrirPdf(a.id, a.numero, w);
      if (despues === 'excel') await albExcel(a.id);
    } catch (e) { if (w && !w.closed && w.location.href === 'about:blank') w.close(); setError(e.message || 'Error al guardar el albarán'); }
    finally { setBusy(false); }
  };
  const avisar = () => { if (onSaved) { try { onSaved(); } catch (_) { /* el refresco del llamante no bloquea */ } } };
  // Enviadas, pendientes y stock cambian al emitir o anular: se vuelven a calcular las líneas.
  const recargarCtx = () => API.get(`/api/albaranes/contexto?${qs}`).then((c) => setLineas(albPrepararLineas(c.lineas))).catch(() => {});
  // GUT-272: deshacer un albarán. No se borra: queda anulado y sus líneas dejan de estar enviadas.
  const anular = async (a) => {
    const motivo = prompt(`¿Anular el albarán ${a.numero}?\n\nSus ${a.num_lineas} línea(s) dejarán de estar «Enviado», volverán a su estado anterior y sus uds vuelven al stock. El albarán se conserva tachado.\n\nMotivo (opcional):`, '');
    if (motivo === null) return;
    setError('');
    try { await API.post(`/api/albaranes/${a.id}/anular`, { motivo }); cargarEmitidos(); recargarCtx(); avisar(); }
    catch (e) { setError(e.message || 'No se pudo anular'); }
  };
  // INC-013: deshacer UNA línea de un albarán emitido. No sale al imprimir y sus uds vuelven al stock.
  const [abierto, setAbierto] = React.useState(null); // id del albarán con las líneas desplegadas
  const deshacerLinea = async (a, l, idx) => {
    const motivo = prompt(`¿Deshacer la línea «${l.descripcion || l.referencia}» (${albNum(l.uds)} ${l.unidad || 'uds'}) del albarán ${a.numero}?\n\nDeja de contar como enviada, sus uds vuelven al stock y ya no sale al imprimir el albarán.\n\nMotivo (opcional):`, '');
    if (motivo === null) return;
    setError('');
    try { await API.post(`/api/albaranes/${a.id}/lineas/${idx}/anular`, { motivo }); cargarEmitidos(); recargarCtx(); avisar(); }
    catch (e) { setError(e.message || 'No se pudo deshacer la línea'); }
  };
  const reimprimir = (a, tipo) => (tipo === 'pdf' ? albAbrirPdf(a.id, a.numero) : albExcel(a.id)).catch((e) => setError(e.message));

  const th = { textAlign: 'left', fontWeight: 600 };
  const thN = { ...th, textAlign: 'right' };
  const tdN = { textAlign: 'right', whiteSpace: 'nowrap' };
  return (
    <div onMouseDown={(e) => { if (e.target === e.currentTarget) onClose(); }}
      style={{ position: 'fixed', inset: 0, background: 'rgba(15,23,42,.45)', zIndex: 1000, display: 'flex', alignItems: 'flex-start', justifyContent: 'center', padding: '32px 16px', overflow: 'auto' }}>
      <div style={{ background: 'var(--surface, #fff)', border: '1px solid var(--line)', borderRadius: 8, width: 'min(1400px, 96vw)', boxShadow: '0 20px 60px rgba(0,0,0,.18)' }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '14px 18px', borderBottom: '1px solid var(--line)' }}>
          <Icon name="truck" size={16} />
          <h3 style={{ margin: 0, fontSize: 15, flex: 1 }}>Albarán de entrega{ctx?.cliente ? <span className="muted" style={{ fontWeight: 400 }}> · {ctx.cliente}</span> : null}</h3>
          <button className="icon-btn" onClick={onClose} title="Cerrar"><Icon name="x" /></button>
        </div>

        <div style={{ padding: 18 }}>
          {error && <div className="pill err" style={{ display: 'block', padding: '8px 10px', marginBottom: 12, borderRadius: 6 }}>{error}</div>}
          {!ctx && !error && <div className="muted">Cargando…</div>}

          {ctx && hecho && (
            <div style={{ border: '1px solid var(--line)', borderRadius: 8, padding: 16, marginBottom: 16, background: 'var(--bg-2, #f7f8fa)' }}>
              <div style={{ fontSize: 14, fontWeight: 600, marginBottom: 6 }}>Albarán {hecho.numero} guardado ✓</div>
              <div className="muted" style={{ fontSize: 12.5, marginBottom: 10 }}>
                {hecho.lineas.length} línea{hecho.lineas.length === 1 ? '' : 's'} · {albFecha(hecho.fecha)}{hecho.instalador ? ` · ${hecho.instalador}` : ''}. Imprímelo y que el instalador lo firme al recoger el material.
              </div>
              <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
                <button className="btn primary sm" onClick={() => reimprimir(hecho, 'pdf')}><Icon name="printer" size={12} /> Imprimir</button>
                <button className="btn ghost sm" onClick={() => reimprimir(hecho, 'excel')}><Icon name="download" size={12} /> Excel</button>
                <button className="btn ghost sm" onClick={() => setHecho(null)}>Hacer otro albarán</button>
              </div>
            </div>
          )}

          {ctx && !hecho && (
            <>
              <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))', gap: 12, marginBottom: 14 }}>
                <div className="field"><label>Nº pedido</label>
                  <input value={f.numero_pedido} onChange={(e) => set('numero_pedido', e.target.value)} placeholder="Nº de pedido" /></div>
                <div className="field"><label>Fecha</label>
                  <input type="date" value={f.fecha} onChange={(e) => set('fecha', e.target.value)} /></div>
                <div className="field" style={{ gridColumn: 'span 2' }}><label>Instalador (recibe y firma)</label>
                  <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap', alignItems: 'center' }}>
                    {f.instalador_ids.map((id) => (
                      <span key={id} className="pill info" style={{ display: 'inline-flex', alignItems: 'center', gap: 4 }}>
                        {nombreInst(id)}
                        <button type="button" onClick={() => set('instalador_ids', f.instalador_ids.filter((x) => x !== id))}
                          style={{ border: 0, background: 'none', cursor: 'pointer', padding: 0, lineHeight: 1, color: 'inherit' }} title="Quitar">×</button>
                      </span>
                    ))}
                    <select value="" onChange={(e) => e.target.value && set('instalador_ids', [...f.instalador_ids, Number(e.target.value)])} style={{ width: 'auto', minWidth: 170 }}>
                      <option value="">{f.instalador_ids.length ? '+ Añadir otro…' : 'Elegir instalador…'}</option>
                      {ctx.instaladores.filter((i) => i.activo && !f.instalador_ids.includes(i.id)).map((i) => (
                        <option key={i.id} value={i.id}>{i.nombre}{i.tipo === 'subcontrata' ? ' (subcontrata)' : ''}</option>
                      ))}
                    </select>
                  </div>
                </div>
              </div>

              <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 10, marginBottom: 6 }}>
                <div className="section-title" style={{ margin: 0 }}>Material que sale ({marcadas.length} de {lineas.length})</div>
                <span className="muted" style={{ fontSize: 12 }}>{lineas.some((l) => l.preparado && albPorEnviar(l)) ? 'Marcadas por defecto: las preparadas sin enviar' : 'No hay nada preparado: van marcadas todas las no enviadas'}</span>
              </div>
              {lineas.length === 0 ? (
                <div className="muted" style={{ fontSize: 12.5, padding: '12px 0' }}>Este pedido no tiene materiales en la lista.</div>
              ) : (
                <div style={{ maxHeight: 360, overflow: 'auto', border: '1px solid var(--line)', borderRadius: 6 }}>
                  <table className="tbl tbl-compact" style={{ fontSize: 12.5, margin: 0 }}>
                    <thead>
                      <tr>
                        <th style={{ width: 28 }}><input type="checkbox" checked={todas} title="Marcar todas"
                          onChange={(e) => setLineas((ls) => ls.map((l) => ({ ...l, on: e.target.checked && !albBloqueo(l) })))} /></th>
                        <th style={th}>ID</th><th style={th}>Descripción</th><th style={th}>Medidas</th><th style={th}>RAL</th>
                        <th style={thN}>Cant.</th><th style={thN} title="Uds en albaranes vigentes (sin las líneas deshechas)">Cant. enviadas</th><th style={thN}>Pend.</th>
                        <th style={thN} title="Stock disponible del artículo en Almacén">Stock</th>
                        <th style={{ ...th, width: 96 }} title="Uds que salen en este albarán (nunca más de lo pendiente)">Cant. a enviar</th><th style={th}>Estado</th>
                      </tr>
                    </thead>
                    <tbody>
                      {lineas.map((l, idx) => {
                        // INC-009: cabecera del kit antes de su primer componente (el servidor ya los junta).
                        const nuevoKit = l.kit_grupo && (idx === 0 || lineas[idx - 1].kit_grupo !== l.kit_grupo);
                        const delKit = nuevoKit ? lineas.filter((x) => x.kit_grupo === l.kit_grupo) : [];
                        const kitEnviables = delKit.filter((x) => !albBloqueo(x));
                        const cabKit = nuevoKit && (
                          <tr key={`kit-${l.kit_grupo}`} style={{ background: 'var(--bg-elev, #fbfbf9)' }}>
                            <td><input type="checkbox" disabled={!kitEnviables.length} checked={kitEnviables.length > 0 && kitEnviables.every((x) => x.on)}
                              title="Marcar / desmarcar todo el kit"
                              onChange={(e) => { const on = e.target.checked; setLineas((ls) => ls.map((x) => (x.kit_grupo === l.kit_grupo && !albBloqueo(x) ? { ...x, on } : x))); }} /></td>
                            <td><span className="pill info" style={{ fontSize: 10.5 }}><span className="d" />Kit</span></td>
                            <td colSpan={9} style={{ fontWeight: 700 }}>
                              {/^kit\b/i.test(l.kit_nombre || '') ? l.kit_nombre : `KIT ${l.kit_nombre || ''}`}
                              <span className="muted" style={{ fontWeight: 400, fontSize: 11.5 }}> · {delKit.filter((x) => x.on).length} de {delKit.length} componentes marcados</span>
                            </td>
                          </tr>
                        );
                        const est = (window.MAT_ESTADOS || {})[l.estado_material];
                        const bloqueo = albBloqueo(l);
                        const errUds = l.on ? albErrorUds(l) : '';
                        const parcial = l.uds_enviadas > 0 && l.estado_material !== 'enviado';
                        return (
                          <React.Fragment key={l.id}>
                          {cabKit}
                          <tr style={{ opacity: l.on ? 1 : 0.55 }}>
                            <td><input type="checkbox" checked={l.on} disabled={!!bloqueo} title={bloqueo} onChange={(e) => setLinea(l.id, { on: e.target.checked })} /></td>
                            <td className="mono" style={l.kit_grupo ? { paddingLeft: 18, boxShadow: 'inset 3px 0 var(--accent)' } : undefined}>{l.codigo || ''}</td>
                            <td style={{ whiteSpace: 'normal', minWidth: 180 }}>{l.cortina ? <span className="muted">{l.cortina} · </span> : null}{l.nombre}</td>
                            <td style={{ whiteSpace: 'nowrap' }}>{[l.largo, l.ancho, l.alto].filter(Boolean).join(' × ')}</td><td>{l.ral || ''}</td>
                            <td style={tdN}>{albNum(l.cantidad)}</td>
                            <td style={tdN}>{albNum(l.uds_enviadas)}</td>
                            <td style={{ ...tdN, fontWeight: l.pendientes ? 600 : 400 }}>{l.pendientes == null ? <span className="muted">—</span> : albNum(l.pendientes)}</td>
                            <td style={tdN}>{l.disponible == null
                              ? <span className="muted" style={{ fontSize: 11 }} title="Línea sin artículo del catálogo">sin control de stock</span>
                              : <span style={l.pendientes === 0 ? { color: 'var(--ink-3, #9ca3af)' } : { color: l.disponible <= 0 ? 'var(--err)' : undefined, fontWeight: l.disponible <= 0 ? 600 : 400 }}
                                  title={`En almacén: ${albNum(l.stock) || 0}${l.reservadas > 0 ? ` + ${albNum(l.reservadas)} ya reservadas para esta línea (De stock / OC recibida)` : ''}`}>{albNum(l.disponible) || 0}</span>}</td>
                            <td>{bloqueo
                              ? <span title={bloqueo} style={{ fontSize: 11, whiteSpace: 'nowrap', color: l.pendientes === 0 ? 'var(--ink-3, #6b7280)' : 'var(--err)' }}>{l.pendientes === 0 ? 'Enviado al completo' : bloqueo}</span>
                              : <>
                                  <input type="number" min="0" step="any" value={l.uds} onChange={(e) => setLinea(l.id, { uds: e.target.value })}
                                    style={{ width: 80, padding: '2px 6px', borderColor: errUds ? 'var(--err)' : undefined }} title={l.unidad || ''} />
                                  {errUds && <div style={{ fontSize: 11, color: 'var(--err)' }}>{errUds}</div>}
                                </>}</td>
                            <td>{parcial
                              ? <span className="pill warn" title={`Envío parcial${l.albaran_numero ? ` (último: ${l.albaran_numero})` : ''}`}><span className="d" />Enviado {albNum(l.uds_enviadas)}/{albNum(l.cantidad)}</span>
                              : est ? <span className={`pill ${est.cls}`} title={l.estado_material === 'enviado' ? 'Ya salió en otro albarán vigente' : ''}><span className="d" />{l.estado_material === 'enviado' && l.albaran_numero ? `${est.label} · ${l.albaran_numero}` : est.label}</span> : null}</td>
                          </tr>
                          </React.Fragment>
                        );
                      })}
                    </tbody>
                  </table>
                </div>
              )}

              <div className="field" style={{ marginTop: 14 }}><label>Observaciones</label>
                <textarea rows={2} value={f.observaciones} onChange={(e) => set('observaciones', e.target.value)} placeholder="Opcional: sale impreso en el albarán" /></div>

              <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 8, marginTop: 14 }}>
                <button className="btn ghost" onClick={onClose}>Cancelar</button>
                <button className="btn ghost" disabled={busy || !marcadas.length || conError.length > 0} onClick={() => guardar('excel')} title="Guarda el albarán y descarga el Excel"><Icon name="download" size={12} /> Excel</button>
                <button className="btn primary" disabled={busy || !marcadas.length || conError.length > 0} onClick={() => guardar('pdf')}><Icon name="printer" size={12} /> {busy ? 'Guardando…' : 'Guardar e imprimir'}</button>
              </div>
            </>
          )}

          {ctx && emitidos.length > 0 && (
            <div style={{ marginTop: 22 }}>
              <div className="section-title">Albaranes emitidos</div>
              <table className="tbl tbl-compact" style={{ fontSize: 12.5 }}>
                <thead><tr><th style={th}>Nº</th><th style={th}>Fecha</th><th style={th}>Pedido</th><th style={th}>Instalador</th><th style={th}>Líneas</th><th style={th}>Hecho por</th><th /></tr></thead>
                <tbody>
                  {emitidos.map((a) => (
                    <React.Fragment key={a.id}>
                    <tr style={a.anulado_at ? { textDecoration: 'line-through', opacity: 0.6 } : undefined}
                      title={a.anulado_at ? `Anulado${a.anulado_por_nombre ? ` por ${a.anulado_por_nombre}` : ''} el ${albFecha(a.anulado_at)}${a.motivo_anulacion ? `: ${a.motivo_anulacion}` : ''}. No cuenta como enviado.` : ''}>
                      <td className="mono strong">{a.numero}{a.anulado_at && <span className="pill err" style={{ marginLeft: 6, textDecoration: 'none', display: 'inline-flex' }}><span className="d" />Anulado</span>}</td>
                      <td>{albFecha(a.fecha)}</td>
                      <td className="mono">{a.numero_pedido || ''}</td>
                      <td>{a.instalador || <span className="muted">—</span>}</td>
                      <td>
                        <button className="btn ghost sm" style={{ textDecoration: 'none' }} onClick={() => setAbierto(abierto === a.id ? null : a.id)}
                          title="Ver las líneas (y deshacer una)">{a.num_lineas}{a.num_lineas_total > a.num_lineas ? <span className="muted"> (+{a.num_lineas_total - a.num_lineas} deshecha{a.num_lineas_total - a.num_lineas === 1 ? '' : 's'})</span> : null} {abierto === a.id ? '▴' : '▾'}</button>
                      </td>
                      <td className="muted">{a.created_by_nombre || ''}</td>
                      <td style={{ whiteSpace: 'nowrap', textAlign: 'right' }}>
                        <button className="btn ghost sm" onClick={() => reimprimir(a, 'pdf')} title="Abrir para imprimir"><Icon name="printer" size={12} /></button>
                        <button className="btn ghost sm" onClick={() => reimprimir(a, 'excel')} title="Descargar Excel"><Icon name="download" size={12} /></button>
                        {!a.anulado_at && <button className="btn ghost sm" style={{ color: 'var(--err)' }} onClick={() => anular(a)} title="Deshacer el envío: el albarán queda anulado y sus líneas dejan de estar «Enviado»">Anular</button>}
                      </td>
                    </tr>
                    {abierto === a.id && (
                      <tr><td colSpan={7} style={{ background: 'var(--bg-2, #f7f8fa)', padding: '6px 10px 10px' }}>
                        <table className="tbl tbl-compact" style={{ fontSize: 12, margin: 0 }}>
                          <thead><tr><th style={th}>ID</th><th style={th}>Descripción</th><th style={th}>Medidas</th><th style={thN}>Uds</th><th style={th}>Estado</th><th /></tr></thead>
                          <tbody>
                            {(a.lineas || []).map((l, idx) => (
                              <tr key={idx} style={l.anulada_at ? { opacity: 0.6 } : undefined}>
                                <td className="mono" style={l.anulada_at ? { textDecoration: 'line-through' } : undefined}>{l.referencia || ''}</td>
                                <td style={{ whiteSpace: 'normal', ...(l.anulada_at ? { textDecoration: 'line-through' } : {}) }}>{l.cortina ? <span className="muted">{l.cortina} · </span> : null}{l.descripcion}</td>
                                <td style={{ whiteSpace: 'nowrap' }}>{[l.largo, l.ancho, l.alto].filter(Boolean).join(' × ')}</td>
                                <td style={tdN}>{albNum(l.uds)} {l.unidad && !/^(ud|uds|u)$/i.test(l.unidad) ? l.unidad : ''}</td>
                                <td>{l.anulada_at
                                  ? <span className="pill err" title={`${l.anulada_por_nombre ? `Por ${l.anulada_por_nombre} ` : ''}el ${albFecha(l.anulada_at)}${l.motivo_anulacion ? `: ${l.motivo_anulacion}` : ''}`}><span className="d" />Deshecha · no se imprime</span>
                                  : a.anulado_at ? <span className="muted">—</span> : <span className="pill ok"><span className="d" />Enviada</span>}</td>
                                <td style={{ textAlign: 'right' }}>{!a.anulado_at && !l.anulada_at && (
                                  <button className="btn ghost sm" style={{ color: 'var(--err)' }} onClick={() => deshacerLinea(a, l, idx)}
                                    title="Deshacer solo esta línea: deja de estar enviada, sus uds vuelven al stock y no sale al imprimir">Deshacer línea</button>)}</td>
                              </tr>
                            ))}
                          </tbody>
                        </table>
                      </td></tr>
                    )}
                    </React.Fragment>
                  ))}
                </tbody>
              </table>
            </div>
          )}
        </div>
      </div>
    </div>
  );
};

window.abrirAlbaran = ({ pedidoId, proyectoId, onSaved } = {}) => {
  if (!pedidoId && !proyectoId) return;
  const host = document.createElement('div');
  document.body.appendChild(host);
  const root = ReactDOM.createRoot(host);
  const cerrar = () => { root.unmount(); host.remove(); };
  root.render(<AlbaranModal pedidoId={pedidoId} proyectoId={proyectoId} onClose={cerrar} onSaved={onSaved} />);
};
window.AlbaranModal = AlbaranModal;
