// ROCA GOLF — shared data adapted to real SQL schema (oc_compras)
const { useState, useEffect, useMemo, useRef, useCallback } = React;

const ROCA_USER = { name: "Mariana Vega", role: "Contralor", initials: "MV" };
const DEFAULT_THRESHOLDS = { warn: 80, alert: 90, over: 100 };

// ───────────────────────── PROYECTOS (proyectos) ─────────────────────────
const PROYECTOS = [
  { id: 1, nombre: "Mantenimiento de Greens" },
  { id: 2, nombre: "Casa Club & F&B" },
  { id: 3, nombre: "Pro Shop" },
  { id: 4, nombre: "Ampliación Hoyo 14" },
  { id: 5, nombre: "Operación general" }
];

// ───────────────────────── FAMILIAS (catalogo_insumos.codigofamilia) ─────────────────────────
const FAMILIAS = [
  { codigo: "01", nombre: "Fertilizantes y agroquímicos", budget: 480000, spent: 502400, trend: [40,55,62,70,82,95,104] },
  { codigo: "02", nombre: "Mantenimiento de carros",      budget: 220000, spent: 211200, trend: [20,28,40,55,68,82,96] },
  { codigo: "03", nombre: "Riego y agua",                 budget: 360000, spent: 332000, trend: [35,48,55,62,70,80,92] },
  { codigo: "04", nombre: "Alimentos y bebidas",          budget: 540000, spent: 458600, trend: [25,38,45,55,64,72,85] },
  { codigo: "05", nombre: "Pro shop y vestuario",         budget: 320000, spent: 198400, trend: [12,18,25,32,42,52,62] },
  { codigo: "06", nombre: "Oficina y administración",     budget: 180000, spent: 84000,  trend: [8,15,22,28,34,42,47] },
  { codigo: "07", nombre: "Limpieza e higiene",           budget: 140000, spent: 112000, trend: [18,28,38,48,58,70,80] },
  { codigo: "08", nombre: "Eventos y torneos",            budget: 260000, spent: 156000, trend: [15,22,30,38,45,54,60] }
];
// Backwards-compatible alias used by older components
const CATEGORIES_BASE = FAMILIAS.map(f => ({
  id: f.codigo,
  name: f.nombre,
  meta: "—",
  budget: f.budget,
  spent: f.spent,
  trend: f.trend
}));

// ───────────────────────── PROVEEDORES (proveedores) ─────────────────────────
const PROVEEDORES = [
  { id: 1,  nombre: "AgroVerde Suministros",     rfc: "AVS990412L23", banco: "BBVA",       moneda: "MXN", contacto: "Luis Pacheco",   email: "ventas@agroverde.mx",    activo: 1, ocs: 28, monto: 482300 },
  { id: 2,  nombre: "QuímicaPro MX",             rfc: "QPM050818H44", banco: "Banamex",    moneda: "MXN", contacto: "Daniela Ríos",   email: "contacto@quimicapro.mx", activo: 1, ocs: 19, monto: 412000 },
  { id: 3,  nombre: "Hidráulica del Bajío",      rfc: "HDB070220A11", banco: "Santander",  moneda: "MXN", contacto: "Roberto López",  email: "ventas@hidrobajio.mx",   activo: 1, ocs: 14, monto: 198400 },
  { id: 4,  nombre: "Distribuidora La Reserva",  rfc: "DLR110630T55", banco: "Banorte",    moneda: "MXN", contacto: "Carmen Vidal",   email: "compras@lareserva.mx",   activo: 1, ocs: 22, monto: 356800 },
  { id: 5,  nombre: "Talleres Norte",            rfc: "TLN960115F77", banco: "BBVA",       moneda: "MXN", contacto: "Jorge Méndez",   email: "jorge@talleresnorte.mx", activo: 1, ocs: 11, monto: 142600 },
  { id: 6,  nombre: "Pro Equipment USA",         rfc: "FOREIGN",      banco: "Wells Fargo",moneda: "USD", contacto: "Karen Smith",    email: "sales@proequip.com",     activo: 1, ocs: 6,  monto: 18400 },
  { id: 7,  nombre: "Imprenta Continental",      rfc: "ICO020410B22", banco: "HSBC",       moneda: "MXN", contacto: "Ana Beltrán",    email: "ana@continental.mx",     activo: 1, ocs: 8,  monto: 64200 },
  { id: 8,  nombre: "Servicios Generales SG",    rfc: "SGS080912R33", banco: "Banorte",    moneda: "MXN", contacto: "Pedro Aguilar",  email: "pedro@sgservicios.mx",   activo: 0, ocs: 4,  monto: 22800 }
];

// ───────────────────────── ÓRDENES DE COMPRA (ordenes_compra + oc_partidas) ─────────────────────────
// statuspago: PAGADO | PAGO_PARCIAL | PENDIENTE
// statusflujo: BORRADOR | CON_PRIORIDAD | AUTORIZADA
// prioridad: NORMAL | URGENTE | CRITICO
const ORDENES = [
  { id: 1, folio: 2604021, fecha: "2026-04-26", proyectoid: 1, proveedorid: 1, moneda: "MXN", subtotal: 72690, iva: 11630, total: 84320, statuspago: "PAGADO",       montopagado: 84320,  montopendiente: 0,     prioridad: "NORMAL",  statusflujo: "AUTORIZADA",    familia: "01" },
  { id: 2, folio: 2604020, fecha: "2026-04-26", proyectoid: 1, proveedorid: 3, moneda: "MXN", subtotal: 18793, iva: 3007,  total: 21800, statuspago: "PENDIENTE",    montopagado: 0,      montopendiente: 21800, prioridad: "URGENTE", statusflujo: "CON_PRIORIDAD", familia: "03" },
  { id: 3, folio: 2604019, fecha: "2026-04-25", proyectoid: 2, proveedorid: 4, moneda: "MXN", subtotal: 48620, iva: 7780,  total: 56400, statuspago: "PAGO_PARCIAL", montopagado: 28200,  montopendiente: 28200, prioridad: "NORMAL",  statusflujo: "AUTORIZADA",    familia: "04" },
  { id: 4, folio: 2604018, fecha: "2026-04-25", proyectoid: 1, proveedorid: 2, moneda: "MXN", subtotal: 87930, iva: 14070, total: 102000,statuspago: "PAGADO",       montopagado: 102000, montopendiente: 0,     prioridad: "CRITICO", statusflujo: "AUTORIZADA",    familia: "01" },
  { id: 5, folio: 2604017, fecha: "2026-04-24", proyectoid: 5, proveedorid: 5, moneda: "MXN", subtotal: 16078, iva: 2572,  total: 18650, statuspago: "PENDIENTE",    montopagado: 0,      montopendiente: 18650, prioridad: "NORMAL",  statusflujo: "BORRADOR",      familia: "02" },
  { id: 6, folio: 2604016, fecha: "2026-04-23", proyectoid: 3, proveedorid: 6, moneda: "USD", subtotal: 4200,  iva: 0,     total: 4200,  statuspago: "PAGADO",       montopagado: 4200,   montopendiente: 0,     prioridad: "NORMAL",  statusflujo: "AUTORIZADA",    familia: "05" },
  { id: 7, folio: 2604015, fecha: "2026-04-22", proyectoid: 2, proveedorid: 4, moneda: "MXN", subtotal: 32500, iva: 5200,  total: 37700, statuspago: "PAGADO",       montopagado: 37700,  montopendiente: 0,     prioridad: "NORMAL",  statusflujo: "AUTORIZADA",    familia: "04" },
  { id: 8, folio: 2604014, fecha: "2026-04-21", proyectoid: 4, proveedorid: 2, moneda: "MXN", subtotal: 64655, iva: 10345, total: 75000, statuspago: "PAGO_PARCIAL", montopagado: 30000,  montopendiente: 45000, prioridad: "URGENTE", statusflujo: "AUTORIZADA",    familia: "01" },
  { id: 9, folio: 2604013, fecha: "2026-04-20", proyectoid: 5, proveedorid: 7, moneda: "MXN", subtotal: 12931, iva: 2069,  total: 15000, statuspago: "PAGADO",       montopagado: 15000,  montopendiente: 0,     prioridad: "NORMAL",  statusflujo: "AUTORIZADA",    familia: "06" },
  { id:10, folio: 2604012, fecha: "2026-04-19", proyectoid: 1, proveedorid: 1, moneda: "MXN", subtotal: 41379, iva: 6621,  total: 48000, statuspago: "PENDIENTE",    montopagado: 0,      montopendiente: 48000, prioridad: "CRITICO", statusflujo: "CON_PRIORIDAD", familia: "01" },
  { id:11, folio: 2604011, fecha: "2026-04-18", proyectoid: 2, proveedorid: 4, moneda: "MXN", subtotal: 28017, iva: 4483,  total: 32500, statuspago: "PAGADO",       montopagado: 32500,  montopendiente: 0,     prioridad: "NORMAL",  statusflujo: "AUTORIZADA",    familia: "07" }
];

// ───────────────────────── CATÁLOGO DE INSUMOS (catalogo_insumos) ─────────────────────────
const INSUMOS = [
  { id: 1,  clave: "01-0101", descripcion: "Fertilizante NPK 18-46-00 50kg", codigofamilia: "01", nombrefamilia: "Fertilizantes y agroquímicos", activo: 1, usos: 24 },
  { id: 2,  clave: "01-0205", descripcion: "Herbicida selectivo 20L",         codigofamilia: "01", nombrefamilia: "Fertilizantes y agroquímicos", activo: 1, usos: 12 },
  { id: 3,  clave: "01-0312", descripcion: "Fungicida sistémico 5L",          codigofamilia: "01", nombrefamilia: "Fertilizantes y agroquímicos", activo: 1, usos: 8 },
  { id: 4,  clave: "02-0801", descripcion: "Aceite hidráulico cart 19L",       codigofamilia: "02", nombrefamilia: "Mantenimiento de carros",      activo: 1, usos: 18 },
  { id: 5,  clave: "02-0902", descripcion: "Llanta carrito 18x8.5-8",          codigofamilia: "02", nombrefamilia: "Mantenimiento de carros",      activo: 1, usos: 6 },
  { id: 6,  clave: "03-1101", descripcion: "Aspersor rotor 360°",              codigofamilia: "03", nombrefamilia: "Riego y agua",                 activo: 1, usos: 32 },
  { id: 7,  clave: "03-1204", descripcion: "Tubería PVC 4\" 6m",                codigofamilia: "03", nombrefamilia: "Riego y agua",                 activo: 1, usos: 14 },
  { id: 8,  clave: "04-2001", descripcion: "Botella vino tinto Reserva 750ml", codigofamilia: "04", nombrefamilia: "Alimentos y bebidas",          activo: 1, usos: 42 },
  { id: 9,  clave: "04-2150", descripcion: "Filete res Premium kg",            codigofamilia: "04", nombrefamilia: "Alimentos y bebidas",          activo: 1, usos: 38 },
  { id:10,  clave: "05-3010", descripcion: "Polo manga corta logo bordado",    codigofamilia: "05", nombrefamilia: "Pro shop y vestuario",         activo: 1, usos: 22 },
  { id:11,  clave: "06-4002", descripcion: "Tóner impresora HP CF410X",         codigofamilia: "06", nombrefamilia: "Oficina y administración",     activo: 1, usos: 9 },
  { id:12,  clave: "07-5005", descripcion: "Cloro líquido 20L",                 codigofamilia: "07", nombrefamilia: "Limpieza e higiene",           activo: 1, usos: 16 },
  { id:13,  clave: "08-6101", descripcion: "Trofeo cristal 30cm",                codigofamilia: "08", nombrefamilia: "Eventos y torneos",            activo: 1, usos: 4 },
  { id:14,  clave: "01-0420", descripcion: "Bioestimulante foliar 10L",         codigofamilia: "01", nombrefamilia: "Fertilizantes y agroquímicos", activo: 0, usos: 0 }
];

// ───────────────────────── HISTORIAL (historial) ─────────────────────────
const HISTORIAL = [
  { id: 1, fecha: "2026-04-26 14:32", usuario: "Mariana Vega",   accion: "CREAR_OC",       desc: "OC #2604021 creada — AgroVerde Suministros",    tabla: "ordenes_compra" },
  { id: 2, fecha: "2026-04-26 14:08", usuario: "Carlos Ortega",  accion: "PAGO_REGISTRADO",desc: "Pago $84,320 OC #2604021",                       tabla: "oc_pagos" },
  { id: 3, fecha: "2026-04-26 11:22", usuario: "Mariana Vega",   accion: "EDITAR_INSUMO",  desc: "Insumo 01-0101 actualizado",                     tabla: "catalogo_insumos" },
  { id: 4, fecha: "2026-04-26 10:14", usuario: "Daniel Sosa",    accion: "CAMBIO_PRIORIDAD",desc: "OC #2604018 marcada como CRÍTICA",              tabla: "ordenes_compra" },
  { id: 5, fecha: "2026-04-26 09:40", usuario: "Mariana Vega",   accion: "LOGIN",          desc: "Inicio de sesión",                              tabla: "usuarios" },
  { id: 6, fecha: "2026-04-25 18:12", usuario: "Carlos Ortega",  accion: "CREAR_OC",       desc: "OC #2604019 creada — Distribuidora La Reserva", tabla: "ordenes_compra" },
  { id: 7, fecha: "2026-04-25 16:55", usuario: "Sofía Cabrera",  accion: "EDITAR_PROVEEDOR",desc: "Proveedor #4 actualizado — datos bancarios",   tabla: "proveedores" },
  { id: 8, fecha: "2026-04-25 14:30", usuario: "Mariana Vega",   accion: "ELIMINAR_INSUMO",desc: "Insumo 01-0420 desactivado",                     tabla: "catalogo_insumos" },
  { id: 9, fecha: "2026-04-25 12:18", usuario: "Daniel Sosa",    accion: "PAGO_REGISTRADO",desc: "Pago parcial $30,000 OC #2604014",              tabla: "oc_pagos" },
  { id:10, fecha: "2026-04-25 10:02", usuario: "Carlos Ortega",  accion: "CREAR_OC",       desc: "OC #2604018 creada — QuímicaPro MX",            tabla: "ordenes_compra" },
  { id:11, fecha: "2026-04-24 17:45", usuario: "Mariana Vega",   accion: "CREAR_INSUMO",   desc: "Insumo 03-1204 creado",                         tabla: "catalogo_insumos" },
  { id:12, fecha: "2026-04-24 11:30", usuario: "Sofía Cabrera",  accion: "LOGIN",          desc: "Inicio de sesión",                              tabla: "usuarios" }
];

// ───────────────────────── ALERTAS DERIVADAS ─────────────────────────
const ALERTS_DATA = [
  { id: 1, level: "over",  cat: "01", title: "Fertilizantes excedió el presupuesto",
    desc: "Categoría 01 (Mant. Greens) cerró en 104.7%. OC #2604018 disparó el umbral.",
    time: "Hace 12 min · Auto" },
  { id: 2, level: "alert", cat: "02", title: "Mantenimiento de carros al 96%",
    desc: "Cruzó el umbral del 90%. Revisar OCs en revisión antes de generar nuevas.",
    time: "Hace 1 h · Auto" },
  { id: 3, level: "alert", cat: "03", title: "Riego y agua al 92%",
    desc: "Proyección de cierre 108% si se mantiene el ritmo actual.",
    time: "Hoy 09:14 · Auto" },
  { id: 4, level: "warn",  cat: "07", title: "Limpieza e higiene al 80%",
    desc: "Llegó al primer umbral. Hay 6 partidas pendientes de recibir.",
    time: "Hoy 08:02 · Auto" },
  { id: 5, level: "warn",  cat: "04", title: "Alimentos y bebidas al 85%",
    desc: "Crecimiento +14% vs mes anterior. Evento de socios programado para el 28.",
    time: "Ayer 18:40 · Auto" },
  { id: 6, level: "ok",    cat: "06", title: "Oficina y administración estable",
    desc: "Cerró en 47%. Sin acciones requeridas.",
    time: "Ayer 14:20 · Auto" }
];

// Recent OCs alias for old dashboard
const RECENT_OCS = ORDENES.slice(0, 5).map(o => {
  const prov = PROVEEDORES.find(p => p.id === o.proveedorid);
  return {
    folio: "OC-" + o.folio,
    vendor: prov.nombre,
    cat: o.familia,
    amount: o.total,
    status: o.statuspago === "PAGADO" ? "approved" : "pending"
  };
});

// ───────────────────────── HELPERS ─────────────────────────
function flagLevel(pct, t = DEFAULT_THRESHOLDS) {
  if (pct >= t.over) return "over";
  if (pct >= t.alert) return "alert";
  if (pct >= t.warn) return "warn";
  return "ok";
}
function fmtMoney(n, opts = {}) {
  const { compact, sign } = opts;
  if (compact && Math.abs(n) >= 1000) {
    const k = n / 1000;
    return (k >= 1000 ? (k/1000).toFixed(1)+"M" : k.toFixed(0)+"K");
  }
  const s = Math.round(n).toLocaleString("es-MX");
  return sign && n > 0 ? "+" + s : s;
}
function flagColor(level) {
  return ({
    ok:    "var(--rg-flag-ok)",
    warn:  "var(--rg-flag-warn)",
    alert: "var(--rg-flag-alert)",
    over:  "var(--rg-flag-over)"
  })[level];
}
function levelLabel(level) {
  return { ok: "OK", warn: "AVISO 80%", alert: "ALERTA 90%", over: "EXCEDIDO" }[level];
}
function fmtFolio(f) {
  const s = String(f);
  // Folio largo (ej. 2604021) → OC-2604-021
  if (s.length > 4) return "OC-" + s.slice(0, 4) + "-" + s.slice(4);
  // Folio corto (ej. 0, 1, 13) → OC-0001
  return "OC-" + s.padStart(4, "0");
}
function fmtFecha(d) {
  const [y,m,dd] = d.split("-");
  return `${dd}/${m}/${y.slice(2)}`;
}
function nombreProyecto(id) { return (PROYECTOS.find(p => p.id === id) || {}).nombre || "—"; }
function nombreProveedor(id) { return (PROVEEDORES.find(p => p.id === id) || {}).nombre || "—"; }
function nombreFamilia(cod) { return (FAMILIAS.find(f => f.codigo === cod) || {}).nombre || "—"; }

// ───────────────────────── ICONS ─────────────────────────
const Icon = {
  dashboard: (s = 16) => (<svg width={s} height={s} viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4"><rect x="2" y="2" width="5.5" height="6" rx="1"/><rect x="8.5" y="2" width="5.5" height="3" rx="1"/><rect x="2" y="9" width="5.5" height="5" rx="1"/><rect x="8.5" y="6" width="5.5" height="8" rx="1"/></svg>),
  budget: (s = 16) => (<svg width={s} height={s} viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4"><circle cx="8" cy="8" r="6"/><path d="M8 4v8M5.5 6.5h4.2a1.5 1.5 0 0 1 0 3H6m4 0a1.5 1.5 0 0 1 0 3H5.5"/></svg>),
  oc: (s = 16) => (<svg width={s} height={s} viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4"><path d="M3 2h7l3 3v9H3z"/><path d="M10 2v3h3"/><path d="M5.5 8.5h5M5.5 11h5"/></svg>),
  vendors: (s = 16) => (<svg width={s} height={s} viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4"><path d="M2 13v-1.5a2.5 2.5 0 0 1 2.5-2.5h2A2.5 2.5 0 0 1 9 11.5V13"/><circle cx="5.5" cy="5" r="2.2"/><path d="M10 13v-1.5A2.5 2.5 0 0 1 12.5 9h.5a1 1 0 0 1 1 1v3"/><circle cx="12.5" cy="6" r="1.8"/></svg>),
  catalog: (s = 16) => (<svg width={s} height={s} viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4"><path d="M2.5 3v10l5.5-2 5.5 2V3l-5.5 2z"/><path d="M8 5v6"/></svg>),
  history: (s = 16) => (<svg width={s} height={s} viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4"><path d="M3 8a5 5 0 1 0 1.5-3.5L3 6"/><path d="M3 3v3h3"/><path d="M8 5v3.5l2 1.3"/></svg>),
  bell: (s = 16) => (<svg width={s} height={s} viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4"><path d="M3.5 11.5h9l-1-1.6V6.8a3.5 3.5 0 0 0-7 0v3.1z"/><path d="M6 11.5a2 2 0 0 0 4 0"/></svg>),
  settings: (s = 16) => (<svg width={s} height={s} viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4"><circle cx="8" cy="8" r="2"/><path d="M8 1v2M8 13v2M1 8h2M13 8h2M3 3l1.5 1.5M11.5 11.5L13 13M3 13l1.5-1.5M11.5 4.5L13 3"/></svg>),
  reports: (s = 16) => (<svg width={s} height={s} viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4"><path d="M2 13h12M4 13V8M7.5 13V5M11 13V9"/></svg>),
  search: (s = 14) => (<svg width={s} height={s} viewBox="0 0 14 14" fill="none" stroke="currentColor" strokeWidth="1.6"><circle cx="6" cy="6" r="4"/><path d="M9 9l3 3"/></svg>),
  filter: (s = 14) => (<svg width={s} height={s} viewBox="0 0 14 14" fill="none" stroke="currentColor" strokeWidth="1.5"><path d="M2 3h10M3.5 7h7M5 11h4"/></svg>),
  download: (s = 14) => (<svg width={s} height={s} viewBox="0 0 14 14" fill="none" stroke="currentColor" strokeWidth="1.5"><path d="M7 1.5v8M3.5 6.5L7 10l3.5-3.5M2 12h10"/></svg>),
  more: (s = 14) => (<svg width={s} height={s} viewBox="0 0 14 14" fill="currentColor"><circle cx="3" cy="7" r="1.2"/><circle cx="7" cy="7" r="1.2"/><circle cx="11" cy="7" r="1.2"/></svg>),
  arrowRight: (s = 12) => (<svg width={s} height={s} viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="1.6"><path d="M3 6h6M6 3l3 3-3 3"/></svg>),
  arrowUp: (s = 12) => (<svg width={s} height={s} viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="1.6"><path d="M6 9V3M3 6l3-3 3 3"/></svg>),
  arrowDown: (s = 12) => (<svg width={s} height={s} viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="1.6"><path d="M6 3v6M3 6l3 3 3-3"/></svg>),
  check: (s = 12) => (<svg width={s} height={s} viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="1.7"><path d="M2.5 6.5L5 9l4.5-5"/></svg>),
  x: (s = 12) => (<svg width={s} height={s} viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="1.5"><path d="M3 3l6 6M9 3l-6 6"/></svg>),
  plus: (s = 14) => (<svg width={s} height={s} viewBox="0 0 14 14" fill="none" stroke="currentColor" strokeWidth="1.6"><path d="M7 2v10M2 7h10"/></svg>),
  edit: (s = 14) => (<svg width={s} height={s} viewBox="0 0 14 14" fill="none" stroke="currentColor" strokeWidth="1.5"><path d="M2 12h2.5L11 5.5 8.5 3 2 9.5z"/><path d="M8 3.5L10.5 6"/></svg>),
  eye: (s = 14) => (<svg width={s} height={s} viewBox="0 0 14 14" fill="none" stroke="currentColor" strokeWidth="1.5"><path d="M1 7s2-4 6-4 6 4 6 4-2 4-6 4-6-4-6-4z"/><circle cx="7" cy="7" r="2"/></svg>),
  excel: (s = 14) => (<svg width={s} height={s} viewBox="0 0 14 14" fill="none" stroke="currentColor" strokeWidth="1.4"><rect x="2" y="2" width="10" height="10" rx="1"/><path d="M5 5l4 4M9 5l-4 4"/></svg>)
};

// ───────────────────────── GOLF FLAG ─────────────────────────
function GolfFlag({ level = "ok", size = 26, animate = true }) {
  const color = flagColor(level);
  return (
    <div className="rg-flag" title={levelLabel(level)} style={{ width: size, height: size * 1.15 }}>
      <svg width={size} height={size * 1.15} viewBox="0 0 26 30" fill="none">
        <ellipse cx="13" cy="27.5" rx="7" ry="1.4" fill="rgba(0,0,0,0.08)"/>
        <line x1="8" y1="3" x2="8" y2="28" stroke="#3a3530" strokeWidth="1.2" strokeLinecap="round"/>
        <path d="M8 3 L 22 7 L 8 11 Z" fill={color} className="triangle"/>
        <circle cx="8" cy="28.2" r="1.6" fill="#3a3530"/>
        {animate && (level === "alert" || level === "over") && (
          <circle cx="22" cy="7" r="0" fill={color} opacity="0.5">
            <animate attributeName="r" values="0;7;0" dur="2s" repeatCount="indefinite"/>
            <animate attributeName="opacity" values="0.5;0;0.5" dur="2s" repeatCount="indefinite"/>
          </circle>
        )}
      </svg>
    </div>
  );
}

function Sparkline({ data, color = "var(--rg-green-500)", width = 80, height = 24 }) {
  const max = Math.max(...data), min = Math.min(...data), range = max - min || 1;
  const step = width / (data.length - 1);
  const pts = data.map((v, i) => `${i * step},${height - ((v - min) / range) * height}`).join(" ");
  const areaPts = `0,${height} ${pts} ${width},${height}`;
  return (
    <svg className="rg-spark" width={width} height={height} viewBox={`0 0 ${width} ${height}`}>
      <polygon points={areaPts} fill={color} opacity="0.12"/>
      <polyline points={pts} fill="none" stroke={color} strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/>
      <circle cx={width} cy={height - ((data[data.length-1] - min) / range) * height} r="2" fill={color}/>
    </svg>
  );
}

// Status / priority badges
function StatusPagoBadge({ status }) {
  const map = {
    PAGADO:       { color: "var(--rg-flag-ok)",    bg: "rgba(45,106,68,0.15)",   label: "Pagado" },
    PAGO_PARCIAL: { color: "var(--rg-flag-warn)",  bg: "rgba(212,160,23,0.15)",  label: "Parcial" },
    PENDIENTE:    { color: "var(--rg-flag-over)",  bg: "rgba(178,34,34,0.12)",   label: "Pendiente" }
  }[status] || { color: "var(--rg-muted)", bg: "var(--rg-cream)", label: status };
  return <span className="rg-pill" style={{ background: map.bg, color: map.color }}>
    <span className="rg-dot" style={{ background: map.color }}/> {map.label}
  </span>;
}
function PrioridadBadge({ p }) {
  const map = {
    NORMAL:  { c: "var(--rg-muted)",      bg: "var(--rg-cream)" },
    URGENTE: { c: "var(--rg-flag-alert)", bg: "rgba(200,101,27,0.12)" },
    CRITICO: { c: "var(--rg-flag-over)",  bg: "rgba(178,34,34,0.12)" }
  }[p] || { c: "var(--rg-muted)", bg: "var(--rg-cream)" };
  return <span className="rg-pill" style={{ background: map.bg, color: map.c }}>{p}</span>;
}
function FlujoBadge({ f }) {
  const map = {
    NUEVO:         { c: "var(--rg-muted)",      bg: "var(--rg-cream)",        l: "Nuevo" },
    BORRADOR:      { c: "var(--rg-muted)",      bg: "var(--rg-cream)",        l: "Borrador" },
    CON_PRIORIDAD: { c: "var(--rg-flag-warn)",  bg: "rgba(212,160,23,0.12)",  l: "Con prioridad" },
    AUTORIZADA:    { c: "var(--rg-flag-ok)",    bg: "rgba(45,106,68,0.12)",   l: "Autorizada" }
  }[f] || { c: "var(--rg-muted)", bg: "var(--rg-cream)", l: f };
  return <span className="rg-pill" style={{ background: map.bg, color: map.c }}>{map.l}</span>;
}

// ─────────────────────────────────────────────────────────────────────────────
// SISTEMA DE TOASTS (notificaciones de error / éxito)
// ─────────────────────────────────────────────────────────────────────────────
const ToastContext = React.createContext({ addToast: () => {} });

function ToastProvider({ children }) {
  const [toasts, setToasts] = useState([]);

  const addToast = React.useCallback((msg, type = "error") => {
    const id = Date.now() + Math.random();
    setToasts(t => [...t, { id, msg, type }]);
    setTimeout(() => setToasts(t => t.filter(x => x.id !== id)), 6000);
  }, []);

  const dismiss = (id) => setToasts(t => t.filter(x => x.id !== id));

  const iconFor = (type) => {
    if (type === "error")   return "✕";
    if (type === "success") return "✓";
    return "!";
  };
  const bgFor = (type) => {
    if (type === "error")   return { bg: "#b22222", border: "#8b0000" };
    if (type === "success") return { bg: "#2d6a44", border: "#1a4a2e" };
    return { bg: "#c8651b", border: "#a0480e" };
  };

  return (
    <ToastContext.Provider value={{ addToast }}>
      {children}
      <div style={{
        position: "fixed", bottom: 28, right: 28, zIndex: 99999,
        display: "flex", flexDirection: "column", gap: 10, pointerEvents: "none"
      }}>
        {toasts.map(t => {
          const { bg, border } = bgFor(t.type);
          return (
            <div key={t.id} style={{
              background: bg,
              border: `1px solid ${border}`,
              color: "#fff",
              padding: "12px 16px",
              borderRadius: 10,
              display: "flex",
              alignItems: "flex-start",
              gap: 10,
              boxShadow: "0 8px 24px rgba(0,0,0,0.25)",
              minWidth: 300,
              maxWidth: 440,
              pointerEvents: "all",
              animation: "rg-toast-in 0.25s ease"
            }}>
              <span style={{
                width: 20, height: 20, borderRadius: "50%",
                background: "rgba(255,255,255,0.2)",
                display: "flex", alignItems: "center", justifyContent: "center",
                fontSize: 11, fontWeight: 700, flexShrink: 0, marginTop: 1
              }}>{iconFor(t.type)}</span>
              <span style={{ flex: 1, fontSize: 13.5, lineHeight: 1.45 }}>{t.msg}</span>
              <button
                onClick={() => dismiss(t.id)}
                style={{
                  background: "rgba(255,255,255,0.15)", border: "none", color: "#fff",
                  cursor: "pointer", borderRadius: 6, width: 24, height: 24,
                  display: "flex", alignItems: "center", justifyContent: "center",
                  fontSize: 14, flexShrink: 0
                }}
              >×</button>
            </div>
          );
        })}
      </div>
    </ToastContext.Provider>
  );
}

function useToast() { return React.useContext(ToastContext); }

// ─────────────────────────────────────────────────────────────────────────────
// CONTEXTO DE DATOS (API → estado global)
// ─────────────────────────────────────────────────────────────────────────────
const AppDataContext = React.createContext(null);

// Construye alertas automáticas a partir de las familias con presupuesto
function buildAlertsFromFamilias(familias, thresholds = DEFAULT_THRESHOLDS) {
  return familias
    .map(f => {
      const pct   = f.budget > 0 ? (f.spent / f.budget) * 100 : 0;
      const level = flagLevel(pct, thresholds);
      if (level === "ok") return null;
      return {
        id:    f.codigo,
        level,
        cat:   f.codigo,
        title: `${f.nombre} al ${Math.round(pct)}%`,
        desc:  level === "over"
          ? `La familia ${f.codigo} ha excedido el presupuesto (${Math.round(pct)}%).`
          : `Utilización del ${Math.round(pct)}% — revisa OCs pendientes.`,
        time: "Auto · Tiempo real"
      };
    })
    .filter(Boolean);
}

// Convierte datos de la API al shape que usan los componentes
function normalizeFamilias(apiFamilias) {
  return apiFamilias.map(f => ({
    codigo:  f.codigo,
    nombre:  f.nombre,
    budget:  f.budget  || 0,
    spent:   f.spent   || 0,
    trend:   f.trend   || [0],
    ...f
  }));
}

function normalizeOrdenes(apiOrdenes) {
  return apiOrdenes.map(o => ({
    ...o,
    familia: o.familia || "",
  }));
}

function AppDataProvider({ children }) {
  const { addToast } = React.useContext(ToastContext);
  const apiBase = window.RG_API_BASE || "";

  const [state, setState] = useState({
    proyectos:  PROYECTOS,
    familias:   FAMILIAS,
    ordenes:    ORDENES,
    insumos:    INSUMOS,
    proveedores: PROVEEDORES,
    historial:  HISTORIAL,
    alerts:     ALERTS_DATA,
    reportes:   null,
    loading:    Boolean(apiBase),
    apiReady:   false,
  });

  // Expone un helper para recargar datos individuales
  const refresh = React.useCallback(async (endpoint) => {
    if (!apiBase) return;
    try {
      const res  = await fetch(`${apiBase}/${endpoint}`);
      const json = await res.json();
      if (!json.ok) throw new Error(json.error || "Error desconocido");
      return json.data;
    } catch (err) {
      addToast(`Error al recargar ${endpoint}: ${err.message}`);
      return null;
    }
  }, [apiBase, addToast]);

  useEffect(() => {
    if (!apiBase) return;

    const controller = new AbortController();
    const signal = controller.signal;

    async function fetchAll() {
      const call = (ep) =>
        fetch(`${apiBase}/${ep}`, { signal })
          .then(r => { if (!r.ok) throw new Error(`HTTP ${r.status}`); return r.json(); })
          .catch(err => ({ ok: false, error: err.message, _ep: ep }));

      const [dashR, ordR, insR, provR, histR] = await Promise.all([
        call("dashboard.php"),
        call("ordenes.php?limit=200"),
        call("insumos.php?limit=500"),
        call("proveedores.php"),
        call("historial.php?limit=100"),
      ]);

      if (signal.aborted) return;

      const updates = { loading: false, apiReady: true };

      if (dashR.ok) {
        const fams = normalizeFamilias(dashR.data.familias || []);
        updates.familias   = fams.length ? fams : FAMILIAS;
        updates.proyectos  = dashR.data.proyectos || PROYECTOS;
        updates.alerts     = buildAlertsFromFamilias(fams, DEFAULT_THRESHOLDS);
      } else {
        addToast(`Dashboard: ${dashR.error || "Error al conectar"}`);
      }

      if (ordR.ok) {
        const rows = normalizeOrdenes(ordR.data.rows || []);
        updates.ordenes = rows.length ? rows : ORDENES;
      } else {
        addToast(`Órdenes: ${ordR.error || "Error al conectar"}`);
      }

      if (insR.ok) {
        const rows = insR.data.rows || [];
        updates.insumos = rows.length ? rows : INSUMOS;
      } else {
        addToast(`Insumos: ${insR.error || "Error al conectar"}`);
      }

      if (provR.ok) {
        const rows = provR.data || [];
        updates.proveedores = rows.length ? rows : PROVEEDORES;
      } else {
        addToast(`Proveedores: ${provR.error || "Error al conectar"}`);
      }

      if (histR.ok) {
        const rows = histR.data.rows || [];
        updates.historial = rows.length ? rows : HISTORIAL;
      } else {
        addToast(`Historial: ${histR.error || "Error al conectar"}`);
      }

      setState(s => ({ ...s, ...updates }));
    }

    fetchAll().catch(err => {
      if (err.name === "AbortError") return;
      addToast(`Error de conexión: ${err.message}`);
      setState(s => ({ ...s, loading: false }));
    });

    return () => controller.abort();
  }, [apiBase]);

  // Carga reportes por separado (se pide solo cuando el usuario va a Reports)
  const loadReportes = React.useCallback(async () => {
    if (!apiBase || state.reportes) return;
    const data = await refresh("reports.php");
    if (data) setState(s => ({ ...s, reportes: data }));
  }, [apiBase, refresh, state.reportes]);

  const ctx = { ...state, refresh, loadReportes, addToast };

  return (
    <AppDataContext.Provider value={ctx}>
      {children}
    </AppDataContext.Provider>
  );
}

function useAppData() {
  const ctx = React.useContext(AppDataContext);
  if (!ctx) {
    // Fallback sin provider (modo estático)
    return {
      proyectos: PROYECTOS, familias: FAMILIAS, ordenes: ORDENES,
      insumos: INSUMOS, proveedores: PROVEEDORES, historial: HISTORIAL,
      alerts: ALERTS_DATA, reportes: null, loading: false, apiReady: false,
      refresh: () => {}, loadReportes: () => {}, addToast: () => {}
    };
  }
  return ctx;
}

// Helper: nombre de proveedor desde lista dinámica
function nombreProveedorDyn(id, proveedores) {
  return (proveedores.find(p => p.id === id) || {}).nombre || "—";
}
function nombreProyectoDyn(id, proyectos) {
  return (proyectos.find(p => p.id === id) || {}).nombre || "—";
}
function nombreFamiliaDyn(cod, familias) {
  return (familias.find(f => f.codigo === cod) || {}).nombre || "—";
}

// Spinner de carga
function LoadingOverlay() {
  return (
    <div style={{
      position: "absolute", inset: 0, background: "rgba(245,241,232,0.7)",
      display: "flex", flexDirection: "column", alignItems: "center",
      justifyContent: "center", zIndex: 100, gap: 16
    }}>
      <div style={{
        width: 40, height: 40, border: "3px solid var(--rg-green-700)",
        borderTopColor: "transparent", borderRadius: "50%",
        animation: "rg-spin 0.8s linear infinite"
      }}/>
      <p style={{ color: "var(--rg-green-700)", fontWeight: 600, fontSize: 14 }}>
        Cargando datos…
      </p>
    </div>
  );
}

Object.assign(window, {
  ROCA_USER, DEFAULT_THRESHOLDS, FAMILIAS, CATEGORIES_BASE, PROYECTOS, PROVEEDORES,
  ORDENES, INSUMOS, HISTORIAL, ALERTS_DATA, RECENT_OCS,
  flagLevel, fmtMoney, flagColor, levelLabel, fmtFolio, fmtFecha,
  nombreProyecto, nombreProveedor, nombreFamilia,
  nombreProveedorDyn, nombreProyectoDyn, nombreFamiliaDyn,
  buildAlertsFromFamilias,
  Icon, GolfFlag, Sparkline, StatusPagoBadge, PrioridadBadge, FlujoBadge,
  ToastProvider, useToast,
  AppDataProvider, useAppData,
  LoadingOverlay,
  useState, useEffect, useMemo, useRef, React
});
