// ROCA GOLF — v2.0 · Administrador de Proyecto
// Presupuesto total editable + Conciliación de presupuesto + Proyecciones de costo
// (grupos modulares de claves de insumo). Espejo de la hoja "ADMINISTRADOR DE PROYECTOS".

function money(n) { try { return '$' + fmtMoney(Number(n) || 0); } catch { return '$' + (Number(n)||0).toFixed(2); } }

// Conceptos fijos de la Proyección de Utilidad (la fila "Ajustes por ahorro" y
// "Total" se calculan aparte, no se capturan).
const UTIL_CONCEPTS = ['Utilidad Original', 'Utilidad Orden de Cambio', 'Bono de finalización', 'Daños y Perjuicios', 'Ahorros con cliente'];
function buildUtilRows(saved) {
  const byName = {};
  (Array.isArray(saved) ? saved : []).forEach(r => { if (r && r.concepto) byName[r.concepto] = r; });
  return UTIL_CONCEPTS.map(c => {
    const r = byName[c] || {};
    return { concepto: c, original: r.original || 0, revisado: r.revisado || 0, pendiente: r.pendiente || 0, proyectado: r.proyectado || 0 };
  });
}

// ── Modal: crear / editar proyección (grupo modular) ──────────────────────────
function ProyeccionModal({ open, onClose, proyectoid, initial, onSaved }) {
  const editing = !!initial;
  const [nombre,   setNombre]   = useState('');
  const [revisado, setRevisado] = useState('');
  const [final,    setFinal]    = useState('');
  const [desc,     setDesc]     = useState('');
  const [saving,   setSaving]   = useState(false);

  useEffect(() => {
    if (!open) return;
    setNombre(initial?.nombre || '');
    setRevisado(initial ? String(initial.presupuesto_revisado) : '');
    setFinal(initial ? String(initial.proyectado_final) : '');
    setDesc(initial?.descripcion || '');
  }, [open, initial]);

  async function save() {
    if (!nombre.trim()) { toast.error('El nombre es requerido'); return; }
    setSaving(true);
    try {
      if (editing) {
        await apiFetch('proyecciones.php?id=' + initial.id, 'PUT', {
          nombre: nombre.trim(),
          presupuesto_revisado: parseFloat(revisado) || 0,
          proyectado_final: parseFloat(final) || 0,
          descripcion: desc,
        });
        toast.success('Proyección actualizada');
      } else {
        await apiFetch('proyecciones.php', 'POST', {
          accion: 'crear', proyectoid,
          nombre: nombre.trim(),
          presupuesto_revisado: parseFloat(revisado) || 0,
          proyectado_final: parseFloat(final) || 0,
          descripcion: desc,
        });
        toast.success('Proyección creada');
      }
      onSaved();
      onClose();
    } catch (e) { toast.error(e.message); }
    finally { setSaving(false); }
  }

  return (
    <RgModal open={open} onClose={onClose} title={editing ? 'Editar proyección' : 'Nueva proyección de costo'}
      footer={<>
        <button className="rg-btn" onClick={onClose}>Cancelar</button>
        <button className="rg-btn primary" onClick={save} disabled={saving}>{saving ? 'Guardando…' : 'Guardar'}</button>
      </>}>
      <div className="rg-field"><label>Nombre (p.ej. Movilización, Material, Maquinaria)</label>
        <input className="rg-input" value={nombre} onChange={e => setNombre(e.target.value)} placeholder="Material"/>
      </div>
      <div style={{ display:'grid', gridTemplateColumns:'1fr 1fr', gap:12 }}>
        <div className="rg-field"><label>Presupuesto revisado (MXN)</label>
          <input className="rg-input rg-mono" type="number" step="0.01" value={revisado} onChange={e => setRevisado(e.target.value)} placeholder="0.00"/>
        </div>
        <div className="rg-field"><label>Proyectado final (MXN)</label>
          <input className="rg-input rg-mono" type="number" step="0.01" value={final} onChange={e => setFinal(e.target.value)} placeholder="0.00"/>
        </div>
      </div>
      <div className="rg-field"><label>Descripción / notas</label>
        <textarea className="rg-input" rows="2" value={desc} onChange={e => setDesc(e.target.value)}/>
      </div>
      <div style={{ fontSize:11.5, color:'var(--rg-muted)', marginTop:4 }}>
        El <strong>Costo acumulado</strong> se calcula solo (suma de OC pagadas de las claves del grupo). La <strong>Diferencia</strong> = Presupuesto revisado − Proyectado final.
      </div>
    </RgModal>
  );
}

// ── Modal: gestionar claves de insumo de una proyección ───────────────────────
function ClavesModal({ open, onClose, proyeccion, onChanged }) {
  const [q,        setQ]        = useState('');
  const [results,  setResults]  = useState([]);
  const [loading,  setLoading]  = useState(false);
  const [busy,     setBusy]     = useState(false);
  const [fam,      setFam]      = useState('');
  const dq = useDebounce(q, 300);
  const claves = proyeccion?.claves || [];
  const yaEnGrupo = new Set(claves.map(c => String(c.clave).toUpperCase()));

  // Cargar lista del catálogo (buscable). Sin texto = primeros activos.
  useEffect(() => {
    if (!open) return;
    let cancel = false;
    setLoading(true);
    apiFetch('insumos.php', 'GET', null, { q: dq, activo: '1', limit: 50 })
      .then(d => { if (!cancel) setResults(d?.rows || []); })
      .catch(() => { if (!cancel) setResults([]); })
      .finally(() => { if (!cancel) setLoading(false); });
    return () => { cancel = true; };
  }, [open, dq]);

  useEffect(() => { if (open) setQ(''); }, [open, proyeccion?.id]);

  async function add(clave) {
    const c = String(clave).trim().toUpperCase();
    if (!c) return;
    setBusy(true);
    try {
      await apiFetch('proyecciones.php', 'POST', { accion:'add_clave', proyeccion_id: proyeccion.id, clave: c });
      await onChanged();
      toast.success(`Clave ${c} agregada`);
    } catch (e) { toast.error(e.message); }
    finally { setBusy(false); }
  }
  async function remove(clave) {
    setBusy(true);
    try {
      await apiFetch('proyecciones.php', 'POST', { accion:'rm_clave', proyeccion_id: proyeccion.id, clave });
      await onChanged();
      toast.success('Clave quitada');
    } catch (e) { toast.error(e.message); }
    finally { setBusy(false); }
  }
  async function addFamilia() {
    if (!fam) { toast.error('Elige una familia'); return; }
    setBusy(true);
    try {
      const res = await apiFetch('proyecciones.php', 'POST', { accion:'add_familia', proyeccion_id: proyeccion.id, codigofamilia: fam });
      await onChanged();
      toast.success(`Familia ${fam}: ${res?.added ?? 0} clave(s) agregada(s)`);
    } catch (e) { toast.error(e.message); }
    finally { setBusy(false); }
  }

  if (!proyeccion) return null;
  const disponibles = results.filter(r => !yaEnGrupo.has(String(r.clave).toUpperCase()));

  return (
    <RgModal open={open} onClose={onClose} title={`Claves de insumo · ${proyeccion.nombre}`} width={780}
      footer={<button className="rg-btn" onClick={onClose}>Cerrar</button>}>

      {/* Claves ya en el grupo — lista con descripción */}
      <label style={{ fontSize:11, color:'var(--rg-muted)', textTransform:'uppercase', letterSpacing:'0.06em' }}>
        {claves.length} clave(s) en el grupo
      </label>
      <div style={{ border:'1px solid var(--rg-line-2)', borderRadius:8, maxHeight:220, overflowY:'auto', margin:'8px 0 16px' }}>
        {claves.length === 0 && <div style={{ padding:14, fontSize:12, color:'var(--rg-muted)' }}>Sin claves. El costo acumulado será $0.</div>}
        {claves.map(c => (
          <div key={c.clave} style={{ display:'flex', alignItems:'center', gap:10, padding:'7px 12px', borderBottom:'1px solid var(--rg-line-2)' }}>
            <span className="rg-mono" style={{ fontWeight:700, minWidth:84 }}>{c.clave}</span>
            <span style={{ flex:1, minWidth:0, fontSize:12.5 }} title={c.insumo_descripcion || ''}>{c.insumo_descripcion || '—'}</span>
            <span style={{ fontSize:10.5, color:'var(--rg-muted)', minWidth:90, textAlign:'right' }}>{c.nombrefamilia || ''}</span>
            <button className="rg-icon-btn danger" title="Quitar" onClick={() => remove(c.clave)} disabled={busy}
              style={{ width:20, height:20 }}>{Icon.x(12)}</button>
          </div>
        ))}
      </div>

      {/* Agregar familia completa */}
      <div className="rg-field"><label>Agregar familia completa (todas sus claves)</label>
        <div style={{ display:'flex', gap:8 }}>
          <select className="rg-select" value={fam} onChange={e => setFam(e.target.value)} style={{ flex:1 }}>
            <option value="">Selecciona una familia…</option>
            {FAMILIAS.map(f => <option key={f.codigo} value={f.codigo}>{f.codigo} · {f.nombre}</option>)}
          </select>
          <button className="rg-btn primary" onClick={addFamilia} disabled={busy || !fam}>{Icon.plus(12)} Añadir familia</button>
        </div>
      </div>

      {/* Buscador del catálogo (clave individual) */}
      <div className="rg-field" style={{ marginTop:12 }}><label>O agregar una clave desde el catálogo</label>
        <input className="rg-input" value={q} onChange={e => setQ(e.target.value)}
          placeholder="Buscar por clave o descripción… (ej. 02-0202 o &quot;cemento&quot;)"/>
      </div>

      {/* Lista seleccionable */}
      <div style={{ border:'1px solid var(--rg-line-2)', borderRadius:8, maxHeight:260, overflowY:'auto', marginTop:6 }}>
        {loading && <div style={{ padding:16, textAlign:'center', color:'var(--rg-muted)', fontSize:12 }}>Cargando…</div>}
        {!loading && disponibles.length === 0 && (
          <div style={{ padding:16, textAlign:'center', color:'var(--rg-muted)', fontSize:12 }}>
            {results.length > 0 ? 'Todas las coincidencias ya están en el grupo.' : 'Sin coincidencias en el catálogo.'}
          </div>
        )}
        {!loading && disponibles.map(r => (
          <div key={r.id} style={{ display:'flex', alignItems:'center', gap:10, padding:'8px 12px', borderBottom:'1px solid var(--rg-line-2)' }}>
            <span className="rg-mono" style={{ fontWeight:700, minWidth:78 }}>{r.clave}</span>
            <span style={{ flex:1, minWidth:0, fontSize:12.5, whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis' }} title={r.descripcion}>
              {r.descripcion}
            </span>
            <span style={{ fontSize:10.5, color:'var(--rg-muted)' }}>{r.codigofamilia}</span>
            <button className="rg-btn primary" style={{ height:24, padding:'0 8px', fontSize:11 }}
              onClick={() => add(r.clave)} disabled={busy}>{Icon.plus(11)} Añadir</button>
          </div>
        ))}
      </div>
    </RgModal>
  );
}

// ── Modal: presupuesto adicional (conciliación) ───────────────────────────────
function AdicionalModal({ open, onClose, proyectoid, initial, onSaved }) {
  const editing = !!initial;
  const [actividad, setActividad] = useState('');
  const [fSol, setFSol] = useState('');
  const [fTer, setFTer] = useState('');
  const [costo, setCosto] = useState('');
  const [saving, setSaving] = useState(false);

  useEffect(() => {
    if (!open) return;
    setActividad(initial?.actividad || '');
    setFSol(initial?.fecha_solicitud || '');
    setFTer(initial?.fecha_termino || '');
    setCosto(initial ? String(initial.costo) : '');
  }, [open, initial]);

  async function save() {
    if (!actividad.trim()) { toast.error('La actividad es requerida'); return; }
    setSaving(true);
    const body = {
      actividad: actividad.trim(),
      fecha_solicitud: fSol || null,
      fecha_termino: fTer || null,
      costo: parseFloat(costo) || 0,
    };
    try {
      if (editing) {
        await apiFetch('conciliacion.php?id=' + initial.id, 'PUT', body);
        toast.success('Adicional actualizado');
      } else {
        await apiFetch('conciliacion.php', 'POST', { ...body, proyectoid });
        toast.success('Adicional agregado');
      }
      onSaved();
      onClose();
    } catch (e) { toast.error(e.message); }
    finally { setSaving(false); }
  }

  return (
    <RgModal open={open} onClose={onClose} title={editing ? 'Editar presupuesto adicional' : 'Nuevo presupuesto adicional'}
      footer={<>
        <button className="rg-btn" onClick={onClose}>Cancelar</button>
        <button className="rg-btn primary" onClick={save} disabled={saving}>{saving ? 'Guardando…' : 'Guardar'}</button>
      </>}>
      <div className="rg-field"><label>Actividad realizada</label>
        <input className="rg-input" value={actividad} onChange={e => setActividad(e.target.value)}/>
      </div>
      <div style={{ display:'grid', gridTemplateColumns:'1fr 1fr', gap:12 }}>
        <div className="rg-field"><label>Fecha de solicitud</label>
          <input className="rg-input" type="date" value={fSol || ''} onChange={e => setFSol(e.target.value)}/>
        </div>
        <div className="rg-field"><label>Fecha de término</label>
          <input className="rg-input" type="date" value={fTer || ''} onChange={e => setFTer(e.target.value)}/>
        </div>
      </div>
      <div className="rg-field"><label>Costo (MXN)</label>
        <input className="rg-input rg-mono" type="number" step="0.01" value={costo} onChange={e => setCosto(e.target.value)} placeholder="0.00"/>
      </div>
    </RgModal>
  );
}

// ── Vista principal ───────────────────────────────────────────────────────────
function AdminProyectoView({ onNav, user, onLogout }) {
  const { data: proyectos } = useApiData('proyectos.php');
  const [pid, setPid] = useState(null);

  const [cfg, setCfg]                 = useState(null);
  const [proyecciones, setProy]       = useState([]);
  const [ajustes, setAjustes]         = useState(0);
  const [adicionales, setAdic]        = useState([]);
  const [totalAdic, setTotalAdic]     = useState(0);
  const [loading, setLoading]         = useState(false);

  // Modales
  const [proyModal, setProyModal] = useState({ open:false, initial:null });
  const [clavesModal, setClavesModal] = useState({ open:false, proy:null });
  const [adicModal, setAdicModal] = useState({ open:false, initial:null });

  // Edición de configuración (presupuesto total)
  const [editCfg, setEditCfg] = useState(false);
  const [cfgForm, setCfgForm] = useState({ presupuesto_total:'' });

  // Proyección de Utilidad (rejilla editable)
  const [util, setUtil]         = useState([]);
  const [editUtil, setEditUtil] = useState(false);

  const importRef = useRef(null);   // input de archivo CSV (oculto)

  useEffect(() => {
    if (!pid && proyectos && proyectos.length) setPid(proyectos[0].id);
  }, [proyectos]);

  async function loadAll(projId) {
    if (!projId) return;
    setLoading(true);
    try {
      const [c, p, a] = await Promise.all([
        apiFetch('proyecto_config.php', 'GET', null, { proyectoid: projId }),
        apiFetch('proyecciones.php', 'GET', null, { proyectoid: projId }),
        apiFetch('conciliacion.php', 'GET', null, { proyectoid: projId }),
      ]);
      setCfg(c);
      setCfgForm({ presupuesto_total: String(c.presupuesto_total || 0) });
      setUtil(buildUtilRows(c.utilidad));
      setEditUtil(false);
      setProy(p.proyecciones || []);
      setAjustes(p.ajustes_por_ahorro || 0);
      setAdic(a.adicionales || []);
      setTotalAdic(a.total_adicional || 0);
    } catch (e) { toast.error(e.message); }
    finally { setLoading(false); }
  }

  useEffect(() => { if (pid) loadAll(pid); }, [pid]);

  const reloadProy = async () => {
    const p = await apiFetch('proyecciones.php', 'GET', null, { proyectoid: pid });
    setProy(p.proyecciones || []);
    setAjustes(p.ajustes_por_ahorro || 0);
    // Mantener el modal de claves sincronizado
    setClavesModal(cm => cm.open && cm.proy
      ? { ...cm, proy: (p.proyecciones || []).find(x => x.id === cm.proy.id) || cm.proy }
      : cm);
  };
  const reloadAdic = async () => {
    const a = await apiFetch('conciliacion.php', 'GET', null, { proyectoid: pid });
    setAdic(a.adicionales || []);
    setTotalAdic(a.total_adicional || 0);
  };

  async function delProyeccion(p) {
    if (!window.confirm(`¿Eliminar la proyección "${p.nombre}"? Se quitará del cálculo.`)) return;
    try { await apiFetch('proyecciones.php?id=' + p.id, 'DELETE'); toast.success('Proyección eliminada'); reloadProy(); }
    catch (e) { toast.error(e.message); }
  }
  async function delAdicional(a) {
    if (!window.confirm(`¿Eliminar el adicional "${a.actividad}"?`)) return;
    try { await apiFetch('conciliacion.php?id=' + a.id, 'DELETE'); toast.success('Adicional eliminado'); reloadAdic(); }
    catch (e) { toast.error(e.message); }
  }

  // Guarda presupuesto total. (La rejilla de utilidad se incluye para no perderla.)
  async function saveCfg() {
    try {
      await apiFetch('proyecto_config.php?proyectoid=' + pid, 'PUT', {
        presupuesto_total: parseFloat(cfgForm.presupuesto_total) || 0,
        utilidad: util.map(r => ({ ...r })),
      });
      toast.success('Presupuesto guardado');
      setEditCfg(false);
      loadAll(pid);
    } catch (e) { toast.error(e.message); }
  }

  // Guarda la rejilla de Proyección de Utilidad.
  async function saveUtil() {
    try {
      await apiFetch('proyecto_config.php?proyectoid=' + pid, 'PUT', {
        presupuesto_total: parseFloat(cfgForm.presupuesto_total) || 0,
        utilidad: util.map(r => ({
          concepto: r.concepto,
          original: parseFloat(r.original) || 0,
          revisado: parseFloat(r.revisado) || 0,
          pendiente: parseFloat(r.pendiente) || 0,
          proyectado: parseFloat(r.proyectado) || 0,
        })),
      });
      toast.success('Proyección de utilidad guardada');
      setEditUtil(false);
      loadAll(pid);
    } catch (e) { toast.error(e.message); }
  }
  const setUtilCell = (i, field, val) => setUtil(u => u.map((r, idx) => idx === i ? { ...r, [field]: val } : r));

  // ── Fase 2: plantilla CSV + importación de Proyección de Costos ──
  function csvCell(v) { return '"' + String(v ?? '').replace(/"/g, '""') + '"'; }
  function downloadPlantilla() {
    const header = ['nombre', 'presupuesto_revisado', 'proyectado_final', 'descripcion', 'claves'];
    const lines = [header.map(csvCell).join(',')];
    if (proyecciones.length === 0) {
      lines.push(['Material', '0', '0', 'Ejemplo (edita o borra esta fila)', '02-0202;02-0203'].map(csvCell).join(','));
    } else {
      proyecciones.forEach(p => {
        const claves = (p.claves || []).map(c => c.clave).join(';');
        lines.push([p.nombre, p.presupuesto_revisado, p.proyectado_final, p.descripcion || '', claves].map(csvCell).join(','));
      });
    }
    const blob = new Blob(['﻿' + lines.join('\r\n')], { type: 'text/csv;charset=utf-8;' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = `proyecciones_${proyActual ? proyActual.clave : pid}.csv`;
    a.click();
    URL.revokeObjectURL(url);
  }
  async function downloadExcel() {
    if (!pid) return;
    try {
      const token = sessionStorage.getItem('rg_token');
      const res = await fetch('api/export_proyecto.php?proyectoid=' + pid, {
        headers: token ? { 'Authorization': 'Bearer ' + token } : {},
      });
      if (!res.ok) { toast.error('No se pudo generar el Excel'); return; }
      const blob = await res.blob();
      const url = URL.createObjectURL(blob);
      const a = document.createElement('a');
      a.href = url;
      a.download = `Administrador_de_Proyecto_${proyActual ? proyActual.clave : pid}.xlsx`;
      a.click();
      URL.revokeObjectURL(url);
    } catch (err) { toast.error(err.message); }
  }
  async function handleImportFile(e) {
    const file = e.target.files && e.target.files[0];
    e.target.value = '';
    if (!file) return;
    try {
      const text = await file.text();
      const res = await apiFetch('proyecciones.php', 'POST', { accion: 'import', proyectoid: pid, csv: text });
      toast.success(`Importado: ${res.creadas} nueva(s), ${res.actualizadas} actualizada(s), ${res.claves_agregadas} clave(s)`);
      if (res.errores && res.errores.length) {
        toast.error(`${res.errores.length} aviso(s). Ej: ${res.errores.slice(0, 2).join(' · ')}`);
      }
      loadAll(pid);
    } catch (err) { toast.error(err.message); }
  }

  const proyActual = proyectos?.find(p => p.id === pid);
  const presupuestoTotal = cfg?.presupuesto_total || 0;
  const presupuestoActual = presupuestoTotal + totalAdic;          // Original + Adicional
  const sumRevisado = proyecciones.reduce((s, p) => s + p.presupuesto_revisado, 0);
  const sumCosto    = proyecciones.reduce((s, p) => s + p.costo_acumulado, 0);
  const sumFinal    = proyecciones.reduce((s, p) => s + p.proyectado_final, 0);

  // ── Totales de la Proyección de Utilidad ──
  const utilCols = ['original','revisado','pendiente','proyectado'];
  const utilTot = {};
  utilCols.forEach(c => utilTot[c] = util.reduce((s, r) => s + (parseFloat(r[c]) || 0), 0));
  // La fila "Ajustes por ahorro" (auto) se suma solo en la columna Proyectado.
  utilTot.proyectado += ajustes;

  // ── Totales importantes (desglose) ──
  // TOTAL = Proyección de Utilidad (Σ filas, col. Proyectado) + Diferencia de
  //         Proyección de Costos (Σ Diferencia = ajustes) + Presupuesto Adicional.
  const utilidadProyectada = utilCols.length ? util.reduce((s, r) => s + (parseFloat(r.proyectado) || 0), 0) : 0;
  const totalImportante = utilidadProyectada + ajustes + totalAdic;

  return (
    <div className="rg-app">
      <Sidebar active="admin_proy" onNav={onNav} user={user} onLogout={onLogout}/>
      <main className="rg-main">
        <Topbar crumbs="Catálogos / Administrador de Proyecto" title="Administrador de Proyecto"
          actions={
            <div style={{ display:'flex', gap:8, alignItems:'center' }}>
              <select className="rg-select" value={pid || ''} onChange={e => setPid(Number(e.target.value))} style={{ minWidth:240 }}>
                {(proyectos || []).map(p => <option key={p.id} value={p.id}>{p.clave} · {p.nombre}</option>)}
              </select>
              <button className="rg-btn primary" onClick={downloadExcel} disabled={!pid} title="Descargar reporte Excel (hoja Administrador de Proyectos)">{Icon.excel()} Descargar Excel</button>
            </div>
          }/>
        <div className="rg-content">
          {loading && <div style={{ padding:24, textAlign:'center', color:'var(--rg-muted)' }}>Cargando…</div>}

          {!loading && pid && (
            <>
              {/* ── Conciliación de presupuesto ── */}
              <div className="rg-card" style={{ marginBottom:16 }}>
                <div className="rg-card-header">
                  <div><h3>Conciliación de Presupuesto</h3>
                    <div className="sub">{proyActual ? proyActual.nombre : ''}</div></div>
                  {!editCfg
                    ? <button className="rg-btn" onClick={() => setEditCfg(true)}>{Icon.edit()} Editar presupuesto</button>
                    : <div style={{ display:'flex', gap:8 }}>
                        <button className="rg-btn" onClick={() => { setEditCfg(false); }}>Cancelar</button>
                        <button className="rg-btn primary" onClick={saveCfg}>Guardar</button>
                      </div>}
                </div>
                <div style={{ display:'grid', gridTemplateColumns:'repeat(4,1fr)', gap:14, padding:'16px 18px' }}>
                  <KpiBox label="Presupuesto Original" edit={editCfg}
                    value={presupuestoTotal} field="presupuesto_total" form={cfgForm} setForm={setCfgForm}/>
                  <KpiBox label="Presupuesto Adicional" value={totalAdic} readOnly hint="Suma de adicionales"/>
                  <KpiBox label="Presupuesto Actual" value={presupuestoActual} readOnly strong/>
                  <KpiBox label="Ajustes por ahorro" value={ajustes} readOnly hint="Σ Diferencia" signed/>
                </div>
              </div>

              {/* ── Totales importantes (desglose) ── */}
              <div className="rg-card" style={{ marginBottom:16 }}>
                <div className="rg-card-header">
                  <div><h3>Totales importantes</h3>
                    <div className="sub">Total = Proyección de Utilidad + Diferencia de Proyección de Costos + Presupuesto Adicional</div></div>
                </div>
                <div style={{ display:'grid', gridTemplateColumns:'repeat(4,1fr)', gap:14, padding:'16px 18px', alignItems:'stretch' }}>
                  <KpiBox label="Proyección de Utilidad" value={utilidadProyectada} readOnly hint="Σ utilidad (proyectado)" signed/>
                  <KpiBox label="Diferencia Proy. de Costos" value={ajustes} readOnly hint="Σ Diferencia" signed/>
                  <KpiBox label="Presupuesto Adicional" value={totalAdic} readOnly hint="Σ adicionales"/>
                  <KpiBox label="TOTAL" value={totalImportante} readOnly strong signed/>
                </div>
              </div>

              {/* ── Proyección de Costos (grupos modulares) ── */}
              <div className="rg-card" style={{ marginBottom:16 }}>
                <div className="rg-card-header">
                  <div><h3>Proyección de Costos</h3>
                    <div className="sub">{proyecciones.length} grupo(s) · costo acumulado = OC pagadas de sus claves</div></div>
                  <div style={{ display:'flex', gap:8, flexWrap:'wrap' }}>
                    <button className="rg-btn" onClick={downloadPlantilla} title="Descargar plantilla CSV con los datos actuales">{Icon.excel()} Plantilla CSV</button>
                    <button className="rg-btn" onClick={() => importRef.current && importRef.current.click()} title="Importar CSV (agrega/actualiza, no elimina)">{Icon.excel()} Importar CSV</button>
                    <input ref={importRef} type="file" accept=".csv,text/csv" style={{ display:'none' }} onChange={handleImportFile}/>
                    <button className="rg-btn primary" onClick={() => setProyModal({ open:true, initial:null })}>{Icon.plus()} Nueva proyección</button>
                  </div>
                </div>
                <div style={{ overflowX:'auto' }}>
                  <table className="rg-table" style={{ width:'100%', minWidth:760 }}>
                    <thead>
                      <tr style={{ background:'var(--rg-cream)', fontSize:10.5, textTransform:'uppercase', letterSpacing:'0.06em', color:'var(--rg-muted)' }}>
                        <th style={{ textAlign:'left', padding:'9px 12px' }}>Concepto</th>
                        <th style={{ textAlign:'left', padding:'9px 12px' }}>Claves</th>
                        <th style={{ textAlign:'right', padding:'9px 12px' }}>Ppto. revisado</th>
                        <th style={{ textAlign:'right', padding:'9px 12px' }}>Costo acumulado</th>
                        <th style={{ textAlign:'right', padding:'9px 12px' }}>Proyectado final</th>
                        <th style={{ textAlign:'right', padding:'9px 12px' }}>Diferencia</th>
                        <th style={{ textAlign:'right', padding:'9px 12px' }}>Acciones</th>
                      </tr>
                    </thead>
                    <tbody>
                      {proyecciones.length === 0 && (
                        <tr><td colSpan="7" style={{ padding:20, textAlign:'center', color:'var(--rg-muted)' }}>
                          Sin proyecciones. Crea una (p.ej. “Material”) y agrégale claves de insumo.
                        </td></tr>
                      )}
                      {proyecciones.map(p => (
                        <tr key={p.id} style={{ borderTop:'1px solid var(--rg-line-2)' }}>
                          <td style={{ padding:'9px 12px', fontWeight:600 }}>{p.nombre}</td>
                          <td style={{ padding:'9px 12px' }}>
                            <button className="rg-btn" style={{ height:24, padding:'0 8px', fontSize:11 }}
                              onClick={() => setClavesModal({ open:true, proy:p })}>
                              {p.claves.length} clave(s)
                            </button>
                          </td>
                          <td style={{ padding:'9px 12px', textAlign:'right' }} className="rg-mono">{money(p.presupuesto_revisado)}</td>
                          <td style={{ padding:'9px 12px', textAlign:'right' }} className="rg-mono">{money(p.costo_acumulado)}</td>
                          <td style={{ padding:'9px 12px', textAlign:'right' }} className="rg-mono">{money(p.proyectado_final)}</td>
                          <td style={{ padding:'9px 12px', textAlign:'right', fontWeight:700, color: p.diferencia < 0 ? 'var(--rg-flag-over)' : 'var(--rg-flag-ok)' }} className="rg-mono">{money(p.diferencia)}</td>
                          <td style={{ padding:'9px 12px', textAlign:'right', whiteSpace:'nowrap' }}>
                            <button className="rg-icon-btn" title="Editar" onClick={() => setProyModal({ open:true, initial:p })}>{Icon.edit()}</button>
                            <button className="rg-icon-btn danger" title="Eliminar" onClick={() => delProyeccion(p)}>{Icon.x(12)}</button>
                          </td>
                        </tr>
                      ))}
                    </tbody>
                    {proyecciones.length > 0 && (
                      <tfoot>
                        <tr style={{ borderTop:'2px solid var(--rg-line)', fontWeight:700, background:'var(--rg-cream)' }}>
                          <td style={{ padding:'9px 12px' }} colSpan="2">Subtotal</td>
                          <td style={{ padding:'9px 12px', textAlign:'right' }} className="rg-mono">{money(sumRevisado)}</td>
                          <td style={{ padding:'9px 12px', textAlign:'right' }} className="rg-mono">{money(sumCosto)}</td>
                          <td style={{ padding:'9px 12px', textAlign:'right' }} className="rg-mono">{money(sumFinal)}</td>
                          <td style={{ padding:'9px 12px', textAlign:'right' }} className="rg-mono">{money(sumFinal - sumRevisado)}</td>
                          <td/>
                        </tr>
                      </tfoot>
                    )}
                  </table>
                </div>
              </div>

              {/* ── Presupuesto adicional (detalle) ── */}
              <div className="rg-card">
                <div className="rg-card-header">
                  <div><h3>Presupuesto Adicional — detalle</h3>
                    <div className="sub">Total: {money(totalAdic)} · en el Excel solo se refleja el número</div></div>
                  <button className="rg-btn primary" onClick={() => setAdicModal({ open:true, initial:null })}>{Icon.plus()} Agregar adicional</button>
                </div>
                <div style={{ overflowX:'auto' }}>
                  <table className="rg-table" style={{ width:'100%', minWidth:640 }}>
                    <thead>
                      <tr style={{ background:'var(--rg-cream)', fontSize:10.5, textTransform:'uppercase', letterSpacing:'0.06em', color:'var(--rg-muted)' }}>
                        <th style={{ textAlign:'left', padding:'9px 12px' }}>Actividad</th>
                        <th style={{ textAlign:'left', padding:'9px 12px' }}>Solicitud</th>
                        <th style={{ textAlign:'left', padding:'9px 12px' }}>Término</th>
                        <th style={{ textAlign:'right', padding:'9px 12px' }}>Costo</th>
                        <th style={{ textAlign:'right', padding:'9px 12px' }}>Acciones</th>
                      </tr>
                    </thead>
                    <tbody>
                      {adicionales.length === 0 && (
                        <tr><td colSpan="5" style={{ padding:20, textAlign:'center', color:'var(--rg-muted)' }}>Sin adicionales registrados.</td></tr>
                      )}
                      {adicionales.map(a => (
                        <tr key={a.id} style={{ borderTop:'1px solid var(--rg-line-2)' }}>
                          <td style={{ padding:'9px 12px' }}>{a.actividad}</td>
                          <td style={{ padding:'9px 12px' }} className="rg-mono">{a.fecha_solicitud || '—'}</td>
                          <td style={{ padding:'9px 12px' }} className="rg-mono">{a.fecha_termino || '—'}</td>
                          <td style={{ padding:'9px 12px', textAlign:'right' }} className="rg-mono">{money(a.costo)}</td>
                          <td style={{ padding:'9px 12px', textAlign:'right', whiteSpace:'nowrap' }}>
                            <button className="rg-icon-btn" title="Editar" onClick={() => setAdicModal({ open:true, initial:a })}>{Icon.edit()}</button>
                            <button className="rg-icon-btn danger" title="Eliminar" onClick={() => delAdicional(a)}>{Icon.x(12)}</button>
                          </td>
                        </tr>
                      ))}
                    </tbody>
                  </table>
                </div>
              </div>

              {/* ── Proyección de Utilidad ── */}
              <div className="rg-card" style={{ marginTop:16 }}>
                <div className="rg-card-header">
                  <div><h3>Proyección de Utilidad</h3>
                    <div className="sub">Columnas Original · Revisado · Pendient/aprox · Proyectado</div></div>
                  {!editUtil
                    ? <button className="rg-btn" onClick={() => setEditUtil(true)}>{Icon.edit()} Editar</button>
                    : <div style={{ display:'flex', gap:8 }}>
                        <button className="rg-btn" onClick={() => { setUtil(buildUtilRows(cfg?.utilidad)); setEditUtil(false); }}>Cancelar</button>
                        <button className="rg-btn primary" onClick={saveUtil}>Guardar</button>
                      </div>}
                </div>
                <div style={{ overflowX:'auto' }}>
                  <table className="rg-table" style={{ width:'100%', minWidth:720 }}>
                    <thead>
                      <tr style={{ background:'var(--rg-cream)', fontSize:10.5, textTransform:'uppercase', letterSpacing:'0.06em', color:'var(--rg-muted)' }}>
                        <th style={{ textAlign:'left', padding:'9px 12px' }}>Concepto</th>
                        <th style={{ textAlign:'right', padding:'9px 12px' }}>Original</th>
                        <th style={{ textAlign:'right', padding:'9px 12px' }}>Revisado</th>
                        <th style={{ textAlign:'right', padding:'9px 12px' }}>Pendient/aprox</th>
                        <th style={{ textAlign:'right', padding:'9px 12px' }}>Proyectado</th>
                      </tr>
                    </thead>
                    <tbody>
                      {util.map((r, i) => (
                        <tr key={r.concepto} style={{ borderTop:'1px solid var(--rg-line-2)' }}>
                          <td style={{ padding:'9px 12px', fontWeight:600 }}>{r.concepto}</td>
                          {utilCols.map(col => (
                            <td key={col} style={{ padding:'6px 12px', textAlign:'right' }}>
                              {editUtil
                                ? <input className="rg-input rg-mono" type="number" step="0.01" value={r[col]}
                                    onChange={e => setUtilCell(i, col, e.target.value)} style={{ textAlign:'right', maxWidth:140, marginLeft:'auto' }}/>
                                : <span className="rg-mono">{money(r[col])}</span>}
                            </td>
                          ))}
                        </tr>
                      ))}
                      {/* Fila automática: Ajustes por ahorro (solo en Proyectado) */}
                      <tr style={{ borderTop:'1px solid var(--rg-line-2)', background:'var(--rg-cream)' }}>
                        <td style={{ padding:'9px 12px', fontWeight:600 }}>Ajustes por ahorro <span style={{ fontWeight:400, color:'var(--rg-muted)', fontSize:11 }}>(auto = Σ Diferencia)</span></td>
                        <td style={{ padding:'9px 12px', textAlign:'right' }} className="rg-mono">—</td>
                        <td style={{ padding:'9px 12px', textAlign:'right' }} className="rg-mono">—</td>
                        <td style={{ padding:'9px 12px', textAlign:'right' }} className="rg-mono">—</td>
                        <td style={{ padding:'9px 12px', textAlign:'right' }} className="rg-mono">{money(ajustes)}</td>
                      </tr>
                    </tbody>
                    <tfoot>
                      <tr style={{ borderTop:'2px solid var(--rg-line)', fontWeight:700, background:'var(--rg-cream)' }}>
                        <td style={{ padding:'9px 12px' }}>Total</td>
                        <td style={{ padding:'9px 12px', textAlign:'right' }} className="rg-mono">{money(utilTot.original)}</td>
                        <td style={{ padding:'9px 12px', textAlign:'right' }} className="rg-mono">{money(utilTot.revisado)}</td>
                        <td style={{ padding:'9px 12px', textAlign:'right' }} className="rg-mono">{money(utilTot.pendiente)}</td>
                        <td style={{ padding:'9px 12px', textAlign:'right' }} className="rg-mono">{money(utilTot.proyectado)}</td>
                      </tr>
                    </tfoot>
                  </table>
                </div>
              </div>
            </>
          )}
        </div>
      </main>

      <ProyeccionModal open={proyModal.open} initial={proyModal.initial} proyectoid={pid}
        onClose={() => setProyModal({ open:false, initial:null })} onSaved={reloadProy}/>
      <ClavesModal open={clavesModal.open} proyeccion={clavesModal.proy}
        onClose={() => setClavesModal({ open:false, proy:null })} onChanged={reloadProy}/>
      <AdicionalModal open={adicModal.open} initial={adicModal.initial} proyectoid={pid}
        onClose={() => setAdicModal({ open:false, initial:null })} onSaved={reloadAdic}/>
    </div>
  );
}

// KPI editable / solo-lectura
function KpiBox({ label, value, edit, field, form, setForm, readOnly, strong, hint, signed }) {
  const display = edit && form && field !== undefined ? null : value;
  return (
    <div style={{ background:'var(--rg-cream)', border:'1px solid var(--rg-line-2)', borderRadius:8, padding:'12px 14px' }}>
      <div style={{ fontSize:10, letterSpacing:'0.08em', textTransform:'uppercase', color:'var(--rg-muted)', fontWeight:600 }}>{label}</div>
      {edit && field !== undefined ? (
        <input className="rg-input rg-mono" type="number" step="0.01" value={form[field]}
          onChange={e => setForm(f => ({ ...f, [field]: e.target.value }))} style={{ marginTop:6 }}/>
      ) : (
        <div className="rg-mono" style={{ fontSize: strong ? 22 : 18, fontWeight: strong ? 800 : 700, color:'var(--rg-ink)', marginTop:4 }}>
          {signed && value > 0 ? '+' : ''}{money(value)}
        </div>
      )}
      {hint && <div style={{ fontSize:10, color:'var(--rg-muted)', marginTop:3 }}>{hint}</div>}
    </div>
  );
}

Object.assign(window, { AdminProyectoView });
