// ─── Router del ERP (History API) ──────────────────────────────────
// Extraído de app.jsx (P3, split del monolito): el mapa de permisos por ruta
// (ROUTE_SECTION), el gate de acceso denegado (RouteDenied), la tabla ruta→ítem de
// sidebar (ROUTE_SIDEBAR_KEY/sidebarKeyForRoute — fix del highlight roto en rutas
// anidadas) y el router URL↔estado en sí (useMicosRouter): rutas de texto plano sobre
// la History API real, sin lib de routing.
// Expone a window: ROUTE_SECTION, RouteDenied, ROUTE_SIDEBAR_KEY, sidebarKeyForRoute,
// useMicosRouter. Consume window.MicosPuedeVer/ROUTES (bundle-config) y las colecciones
// globales (EXPEDIENTES, FACTURAS, etc. — hidratadas por store.js). Lo usa core/app.jsx
// (único caller de useMicosRouter); se carga justo antes que core/app.jsx.

// Gate deny-by-default del ROUTER (M14 — BUGS-caza-01-07-2026.md #21): antes el gate
// solo vivía en el sidebar/header (shell.jsx MicosPuedeVer); el router de acá renderizaba
// cualquier módulo con solo setear `route` — accesible por URL directa o deep-link
// (applyPath), aunque el usuario no tuviera el permiso. Este mapa traduce cada `route`
// del switch de core/app.jsx a la LLAVE de permiso (MICOS_SECCIONES) que le corresponde,
// mismo criterio que el fix de Panel general (screens-dashboard.jsx DashboardScreen).
// Rutas que no aparecen acá (dashboard, configuracion) no son llaves de MICOS_SECCIONES →
// MicosPuedeVer las deja pasar siempre (no están en la matriz de permisos, ver
// data-usuarios.js). Las listas standalone de documentos del pipeline (presupuestos/
// ordenes-compra/remitos → 'expedientes'; facturas/recibos → 'finanzas') se atan al
// módulo dueño de esa data aunque no tengan entrada propia en el sidebar.
const ROUTE_SECTION = {
  // Expedientes (lista/detalle/alta + documentos del pipeline, anidados dentro del expediente)
  'expedientes-list': 'expedientes', 'expedientes-detail': 'expedientes', 'expedientes-nuevo': 'expedientes',
  'presupuesto': 'expedientes', 'orden-compra': 'expedientes', 'remito': 'expedientes',
  'factura': 'expedientes', 'recibo': 'expedientes',
  'documentacion-medica': 'expedientes', 'documentacion-extra': 'expedientes',
  // Compras
  'compras': 'compras', 'compras-detail': 'compras', 'compras-nuevo': 'compras', 'compras-editar': 'compras',
  // Finanzas (cobros + pagos + todos los documentos que abren adentro). P8:
  // mueren finanzas-cobro-detail/finanzas-pago-detail y sus 6 sub-rutas — ver
  // header de screens/finanzas/lista.jsx (bloque muerto, nunca alcanzable
  // desde la UI real).
  'finanzas': 'finanzas', 'finanzas-doc-detail': 'finanzas',
  'finanzas-nota-nueva': 'finanzas', 'finanzas-factura-nueva': 'finanzas', 'finanzas-recibo-nueva': 'finanzas',
  'finanzas-pago-nueva': 'finanzas', 'finanzas-facrec-nueva': 'finanzas',
  'facturas': 'finanzas', 'recibos': 'finanzas',
  // Logística
  'logistica': 'logistica', 'logistica-detail': 'logistica', 'logistica-nuevo': 'logistica', 'logistica-editar': 'logistica',
  // Inventario
  'inventario': 'inventario', 'inventario-detail': 'inventario', 'inventario-nuevo': 'inventario',
  'inventario-editar': 'inventario', 'inventario-historial': 'inventario',
  // P23 (VACs): equipo físico de alquiler + su historial global.
  // DECISIÓN 2 (06/07/2026): alta de equipo VAC.
  'inventario-activo-detail': 'inventario', 'inventario-historial-alquileres': 'inventario',
  'inventario-activo-nuevo': 'inventario',
  // Directorio
  'entidades': 'entidades', 'entidades-detail': 'entidades', 'entidades-nuevo': 'entidades', 'entidades-editar': 'entidades',
  'medicos': 'medicos', 'medicos-detail': 'medicos', 'medicos-nuevo': 'medicos', 'medicos-editar': 'medicos',
  'particulares': 'particulares', 'particulares-detail': 'particulares', 'particulares-nuevo': 'particulares', 'particulares-editar': 'particulares',
  'proveedores': 'proveedores', 'proveedores-detail': 'proveedores', 'proveedores-nuevo': 'proveedores', 'proveedores-editar': 'proveedores',
  // Documentos del pipeline (listas standalone) — expedientes es su dueño lógico.
  'presupuestos': 'expedientes', 'ordenes-compra': 'expedientes', 'remitos': 'expedientes',
  // Herramientas
  'caja-chica': 'caja-chica', 'caja-chica-nuevo': 'caja-chica', 'caja-chica-editar': 'caja-chica', 'caja-chica-detail': 'caja-chica',
  'aprobaciones': 'notificaciones-margen',
  'ajustes-inventario': 'ajustes-inventario',
};

// Pantalla de acceso denegado: redirige (navegación completa, sale del SPA) a
// /auth/no-autorizado — mismo destino que usa Fase 1 de auth para cuentas deshabilitadas.
// El chequeo real (MicosPuedeVer) ya pasó antes de montar esto; acá solo se ejecuta la
// redirección como efecto (nunca durante el render).
function RouteDenied() {
  React.useEffect(() => {
    window.location.href = (window.ROUTES && window.ROUTES.NO_AUTORIZADO) || '/auth/no-autorizado';
  }, []);
  return null;
}

// ── Fix (a) — sidebar highlight roto en rutas anidadas (mapa-comprension.md §core,
// bug de severidad baja) ──────────────────────────────────────────────────────────
// Antes cada módulo tenía su propio caso especial a mano dentro de un ternario largo:
// Expedientes/Finanzas/Inventario cubrían cualquier ruta que empezara con su nombre,
// Compras/Logística/Caja Chica solo el -detail exacto (ni -nuevo ni -editar), Entidades/
// Médicos/Particulares también solo -detail, y Proveedores NINGÚN caso (ni -detail).
// Resultado verificable: crear o editar en casi cualquier módulo (o CUALQUIER pantalla
// de Proveedores) dejaba el Sidebar sin ítem resaltado. Ahora es una tabla pareja: toda
// ruta de un módulo (detail/nuevo/editar, y los documentos del pipeline anidados dentro
// de un expediente) resuelve al MISMO ítem. Las rutas que NO aparecen acá ya resaltan
// solas (su `route` es idéntico al `href` del ítem en NAV_SECTIONS, shell.jsx — ej.
// 'compras', 'finanzas', 'proveedores') o no tienen ítem de sidebar (configuracion,
// aprobaciones, ajustes-inventario, las listas standalone de documentos del pipeline).
const ROUTE_SIDEBAR_KEY = {
  'expedientes-list': 'expedientes', 'expedientes-detail': 'expedientes', 'expedientes-nuevo': 'expedientes',
  'presupuesto': 'expedientes', 'orden-compra': 'expedientes', 'remito': 'expedientes',
  'factura': 'expedientes', 'recibo': 'expedientes',
  'documentacion-medica': 'expedientes', 'documentacion-extra': 'expedientes',
  'compras-detail': 'compras', 'compras-nuevo': 'compras', 'compras-editar': 'compras',
  'logistica-detail': 'logistica', 'logistica-nuevo': 'logistica', 'logistica-editar': 'logistica',
  'caja-chica-detail': 'caja-chica', 'caja-chica-nuevo': 'caja-chica', 'caja-chica-editar': 'caja-chica',
  'finanzas-doc-detail': 'finanzas', 'finanzas-nota-nueva': 'finanzas', 'finanzas-factura-nueva': 'finanzas',
  'finanzas-recibo-nueva': 'finanzas', 'finanzas-pago-nueva': 'finanzas', 'finanzas-facrec-nueva': 'finanzas',
  'inventario-detail': 'inventario', 'inventario-nuevo': 'inventario', 'inventario-editar': 'inventario',
  'inventario-historial': 'inventario',
  'inventario-activo-detail': 'inventario', 'inventario-historial-alquileres': 'inventario',
  'inventario-activo-nuevo': 'inventario',
  'entidades-detail': 'entidades', 'entidades-nuevo': 'entidades', 'entidades-editar': 'entidades',
  'medicos-detail': 'medicos', 'medicos-nuevo': 'medicos', 'medicos-editar': 'medicos',
  'particulares-detail': 'particulares', 'particulares-nuevo': 'particulares', 'particulares-editar': 'particulares',
  'proveedores-detail': 'proveedores', 'proveedores-nuevo': 'proveedores', 'proveedores-editar': 'proveedores',
};
function sidebarKeyForRoute(route) { return ROUTE_SIDEBAR_KEY[route] || route; }

// Docs de Finanzas con visor standalone propio (route 'finanzas-doc-detail' por {kind,id}).
// Estático — a diferencia de URL_DETAIL (más abajo, dentro del hook), no depende de
// estado de React: mismo mapa en cada render, se define una sola vez acá.
const URL_DOC = {
  'facturas':      { coll: 'FACTURAS',      kind: 'factura', endpoint: 'facturas' },
  'recibos':       { coll: 'RECIBOS',       kind: 'recibo',  endpoint: 'recibos' },
  'notas-credito': { coll: 'NOTAS_CREDITO', kind: 'nota',    endpoint: 'notas-credito' },
  'notas-debito':  { coll: 'NOTAS_DEBITO',  kind: 'nota',    endpoint: 'notas-debito' },
};
// Docs del pipeline: su link abre el EXPEDIENTE al que pertenecen (no tienen detalle propio).
const URL_DOC_EXP = {
  'presupuestos':   { coll: 'PRESUPUESTOS',   endpoint: 'presupuestos' },
  'ordenes-compra': { coll: 'ORDENES_COMPRA', endpoint: 'ordenes-compra' },
  'remitos':        { coll: 'REMITOS',        endpoint: 'remitos' },
};
const URL_LIST_ALIAS = { 'expedientes-list': 'expedientes', 'dashboard': 'panel' };
const URL_SEG_TO_ROUTE = { 'expedientes': 'expedientes-list', 'panel': 'dashboard' };

// Sub-rutas de documento del expediente: cuelgan de un expediente y NO tenían
// ruta propia en la URL (eran /erp/<doc> pelada), así que un F5 parado en el
// documento perdía `selectedExpediente` y la pantalla arrancaba en modo alta en
// blanco (bug transversal F5-subruta). Ahora la URL lleva el publicId del
// expediente (/erp/expedientes/<publicId>/<doc>) para que el deep-link/refresh
// lo reconstruya. Set único, consumido por routeToPath (arma la URL) y applyPath
// (la interpreta). La compuerta de carga del detalle vive en la pantalla
// (DocSubrouteGate, screens/docs/compartido.jsx).
const EXP_DOC_ROUTES = new Set([
  'presupuesto', 'orden-compra', 'remito', 'factura', 'recibo',
  'documentacion-medica', 'documentacion-extra',
]);

// ─── useMicosRouter ──────────────────────────────────────────────────────────────
// Ruteo con History API: la URL refleja la pantalla (y el ítem en los detalles). Recibe
// TODO el estado de navegación de App (route/selected*Id/selectedDoc/newDoc + setters,
// más `user` para gatear el push de URL hasta que haya sesión) y devuelve routeToPath/
// applyPath/openExpedienteFor — App los consume para el mount inicial (deep-link),
// popstate (atrás/adelante) y los onOpen de las listas de documentos.
function useMicosRouter(nav) {
  const {
    route, setRoute, user,
    selectedExpedienteId, setSelectedExpedienteId,
    selectedCompraId, setSelectedCompraId,
    selectedLogisticaId, setSelectedLogisticaId,
    selectedCajaChicaId, setSelectedCajaChicaId,
    selectedInventarioId, setSelectedInventarioId,
    selectedEntidadId, setSelectedEntidadId,
    selectedMedicoId, setSelectedMedicoId,
    selectedParticularId, setSelectedParticularId,
    selectedProveedorId, setSelectedProveedorId,
    selectedDoc, setSelectedDoc,
    newDoc, setNewDoc,
  } = nav;

  // Secciones con detalle por publicId: el `selected` guarda el uuid interno, pero la
  // URL usa el publicId (legible/copiable). El resto de pantallas va a /erp/<route>.
  // P8: mueren las entradas 'cobros'/'pagos' (URL /erp/cobros/<id> · /erp/pagos/<id>) —
  // eran el ÚNICO acceso a las rutas finanzas-cobro-detail/finanzas-pago-detail, ya
  // muertas (bloque inalcanzable desde la UI real, ver screens/finanzas/lista.jsx).
  const URL_DETAIL = {
    'expedientes':  { coll: 'EXPEDIENTES',  set: setSelectedExpedienteId,  list: 'expedientes-list', cur: selectedExpedienteId },
    'compras':      { coll: 'COMPRAS',      set: setSelectedCompraId,      list: 'compras',          cur: selectedCompraId },
    'logistica':    { coll: 'LOGISTICA',    set: setSelectedLogisticaId,   list: 'logistica',        cur: selectedLogisticaId },
    'caja-chica':   { coll: 'CAJA_CHICA',   set: setSelectedCajaChicaId,   list: 'caja-chica',       cur: selectedCajaChicaId },
    'inventario':   { coll: 'INVENTARIO',   set: setSelectedInventarioId,  list: 'inventario',       cur: selectedInventarioId },
    'entidades':    { coll: 'ENTIDADES',    set: setSelectedEntidadId,     list: 'entidades',        cur: selectedEntidadId },
    'medicos':      { coll: 'MEDICOS',      set: setSelectedMedicoId,      list: 'medicos',          cur: selectedMedicoId },
    'particulares': { coll: 'PARTICULARES', set: setSelectedParticularId,  list: 'particulares',     cur: selectedParticularId },
    'proveedores':  { coll: 'PROVEEDORES',  set: setSelectedProveedorId,   list: 'proveedores',      cur: selectedProveedorId },
  };
  // route de detalle -> seg de URL (reverso de URL_DETAIL; soporta detail routes custom, ej. finanzas-cobro-detail).
  const DETAIL_TO_SEG = {};
  Object.keys(URL_DETAIL).forEach((s) => { DETAIL_TO_SEG[URL_DETAIL[s].detail || `${s}-detail`] = s; });

  // Abre el EXPEDIENTE de un documento (presupuesto/OC/remito/factura/recibo),
  // trayéndolo por id/publicId si todavía no está en window.EXPEDIENTES (P2:
  // con las listas de documentos paginando server-side, window.EXPEDIENTES solo
  // tiene los ~500 que hidrató el boot — la mayoría de los documentos apuntan a
  // expedientes FUERA de esa ventana). Mismo patrón que `toExp` de URL_DOC_EXP
  // más abajo (deep-link); acá se reusa también para la apertura por CLICK desde
  // Presupuestos/OC/Remitos/Facturas/Recibos, que antes no re-fetcheaba nada y
  // podía terminar mostrando el expediente de EJEMPLO (Sofía) si no coincidía.
  const openExpedienteFor = (expedienteId, fallbackRoute) => {
    if (!expedienteId) { if (fallbackRoute) setRoute(fallbackRoute); return; }
    const exp = (window.EXPEDIENTES || []).find((e) => e.id === expedienteId || e.publicId === expedienteId);
    if (exp) { setSelectedExpedienteId(exp.id); setRoute('expedientes-detail'); return; }
    fetch(`/api/expedientes/${encodeURIComponent(expedienteId)}`, { credentials: 'same-origin' })
      .then((r) => (r.ok ? r.json() : Promise.reject(r.status)))
      .then((e) => {
        if (!e || !e.id) throw new Error('sin exp');
        const col = window.EXPEDIENTES || (window.EXPEDIENTES = []);
        if (!col.some((x) => x.id === e.id)) col.push(e);
        setSelectedExpedienteId(e.id);
        setRoute('expedientes-detail');
      })
      .catch((err) => {
        console.warn('[openExpedienteFor]', expedienteId, err);
        if (fallbackRoute) setRoute(fallbackRoute);
      });
  };

  // route (+ selected) -> path. Detalle con ítem: /erp/<sección>/<publicId>.
  const routeToPath = () => {
    const _erp = (window.ERP_BASE_PATH || '/erp/'); // base con barra final ('/erp/')
    const seg = DETAIL_TO_SEG[route];
    if (seg) {
      const cfg = URL_DETAIL[seg];
      const item = cfg.cur ? (window[cfg.coll] || []).find((x) => x.id === cfg.cur) : null;
      // compras expone su id público como `numero` (CMP-…); el resto como `publicId`.
      const pubId = item && (item.publicId || item.numero);
      return pubId ? `${_erp}${seg}/${encodeURIComponent(pubId)}` : `${_erp}${seg}`;
    }
    // Doc de Finanzas standalone: /erp/<tipo>/<publicId> derivado del selectedDoc {kind,id}.
    if (route === 'finanzas-doc-detail' && selectedDoc) {
      const k = selectedDoc.kind;
      let s = null, item = null;
      if (k === 'factura') { s = 'facturas'; item = (window.FACTURAS || []).find((x) => x.id === selectedDoc.id); }
      else if (k === 'recibo') { s = 'recibos'; item = (window.RECIBOS || []).find((x) => x.id === selectedDoc.id); }
      else if (k === 'nota') {
        item = (window.NOTAS_CREDITO || []).find((x) => x.id === selectedDoc.id);
        if (item) { s = 'notas-credito'; }
        else { item = (window.NOTAS_DEBITO || []).find((x) => x.id === selectedDoc.id); if (item) s = 'notas-debito'; }
      }
      if (s && item && item.publicId) return `${_erp}${s}/${encodeURIComponent(item.publicId)}`;
      return `${_erp}finanzas`;
    }
    // ── Fix (b) — 'finanzas-nota-nueva' sobrevive al refresh (mapa-comprension.md §core,
    // bug de severidad media) ── clase/lado viajan en la URL (query params) en vez de
    // vivir SOLO en `newDoc` (estado efímero de React, se pierde al remontar la app).
    // applyPath (abajo) los reconstruye desde acá en deep-link/refresh/back-forward.
    if (route === 'finanzas-nota-nueva' && newDoc) {
      const clase = newDoc.clase === 'ND' ? 'ND' : 'NC';
      const lado = newDoc.lado === 'pagos' ? 'pagos' : 'cobros';
      return `${_erp}finanzas-nota-nueva?clase=${clase}&lado=${lado}`;
    }
    // Documento del expediente (sub-ruta): la URL lleva el publicId del expediente
    // dueño, así el F5 lo reconstruye. Sin expediente resuelto todavía (ventana muy
    // breve antes de que hidrate/llegue), cae a la ruta pelada de siempre — no rompe.
    if (EXP_DOC_ROUTES.has(route)) {
      const exp = selectedExpedienteId ? (window.EXPEDIENTES || []).find((e) => e.id === selectedExpedienteId) : null;
      const pub = exp && exp.publicId;
      if (pub) return `${_erp}expedientes/${encodeURIComponent(pub)}/${route}`;
      return `${_erp}${route}`;
    }
    return `${_erp}${URL_LIST_ALIAS[route] || route}`;
  };

  // path -> estado (route + resuelve el ítem por publicId). Si el ítem no está
  // (no hidratado / inexistente), cae a la lista de esa sección sin romper.
  // `search` (window.location.search, con el '?') solo lo necesita 'finanzas-nota-nueva'
  // (fix b) — el resto de las rutas no llevan query string.
  const applyPath = (pathname, search) => {
    // Prefijo base derivado de la const (byte-idéntico a /^\/erp\/?/): quita '/erp' con o sin barra.
    const _erp = (window.ERP_BASE_PATH || '/erp/');
    const _erpRe = new RegExp('^' + _erp.replace(/\/$/, '') + '/?');
    const segs = (pathname || '').replace(_erpRe, '').split('/').filter(Boolean).map(decodeURIComponent);
    if (!segs.length) { setRoute('dashboard'); return; }
    const seg = segs[0];
    // P6 (hallazgo de la review P4, preexistente): /erp/<seccion>/nuevo NO es un
    // publicId — antes caía al branch de detalle y disparaba un fetch a
    // /api/<seccion>/nuevo (404 garantizado) antes de rendirse a la lista. Toda
    // sección con pantalla de alta propia (la matriz ROUTE_SECTION ya conoce su
    // ruta `<seccion>-nuevo`) resuelve directo a esa ruta, sin tocar la red.
    if (segs.length >= 2 && segs[1] === 'nuevo' && URL_DETAIL[seg] && ROUTE_SECTION[`${seg}-nuevo`]) {
      setRoute(`${seg}-nuevo`);
      return;
    }
    // Sub-ruta de documento del expediente: /erp/expedientes/<publicId>/<doc>.
    // Resuelve el expediente (memoria o fetch por publicId) y ABRE el documento —
    // así el F5 parado en un presupuesto/OC/remito/etc. reconstruye
    // selectedExpediente en vez de caer a un alta en blanco. Va ANTES del branch
    // genérico URL_DETAIL['expedientes'] (que si no trataría segs[1] como detalle
    // y se comería el 3er segmento). La compuerta de carga del detalle rico corre
    // ya montada la pantalla (DocSubrouteGate, screens/docs/compartido.jsx).
    if (seg === 'expedientes' && segs.length >= 3 && EXP_DOC_ROUTES.has(segs[2])) {
      const docRoute = segs[2];
      const pub = segs[1];
      const item = (window.EXPEDIENTES || []).find((x) => x.publicId === pub);
      if (item) { setSelectedExpedienteId(item.id); setRoute(docRoute); return; }
      fetch(`/api/expedientes/${encodeURIComponent(pub)}`, { credentials: 'same-origin' })
        .then((r) => (r.ok ? r.json() : Promise.reject(r.status)))
        .then((server) => {
          if (!server || !server.id) throw new Error('sin id');
          const coll = window.EXPEDIENTES || (window.EXPEDIENTES = []);
          if (!coll.some((x) => x.id === server.id)) coll.push(server);
          setSelectedExpedienteId(server.id);
          setRoute(docRoute);
        })
        .catch(() => setRoute('expedientes-list'));
      return;
    }
    if (segs.length >= 2 && URL_DETAIL[seg]) {
      const cfg = URL_DETAIL[seg];
      const detail = cfg.detail || `${seg}-detail`;
      const endpoint = cfg.endpoint || seg;
      const item = (window[cfg.coll] || []).find((x) => x.publicId === segs[1] || x.numero === segs[1]);
      if (item) { cfg.set(item.id); setRoute(detail); return; }
      // No está en memoria (refresh / link compartido / futura paginación): traerlo por id.
      // GET /api/<tipo>/[id] devuelve el MISMO shape que la lista → lo inyectamos y abrimos el detalle.
      // Si no existe / sin acceso → su lista, sin romper (nunca crash).
      fetch(`/api/${endpoint}/${encodeURIComponent(segs[1])}`)
        .then((r) => (r.ok ? r.json() : Promise.reject(r.status)))
        .then((server) => {
          if (!server || !server.id) throw new Error('sin id');
          const coll = window[cfg.coll] || (window[cfg.coll] = []);
          if (!coll.some((x) => x.id === server.id)) coll.push(server);
          cfg.set(server.id);
          setRoute(detail);
        })
        .catch(() => setRoute(cfg.list));
      return;
    }
    // Docs de Finanzas con visor standalone (finanzas-doc-detail por {kind,id}).
    if (segs.length >= 2 && URL_DOC[seg]) {
      const cfg = URL_DOC[seg];
      const open = (it) => { setSelectedDoc({ kind: cfg.kind, id: it.id }); setRoute('finanzas-doc-detail'); };
      const item = (window[cfg.coll] || []).find((x) => x.publicId === segs[1]);
      if (item) { open(item); return; }
      fetch(`/api/${cfg.endpoint}/${encodeURIComponent(segs[1])}`)
        .then((r) => (r.ok ? r.json() : Promise.reject(r.status)))
        .then((server) => {
          if (!server || !server.id) throw new Error('sin id');
          const coll = window[cfg.coll] || (window[cfg.coll] = []);
          if (!coll.some((x) => x.id === server.id)) coll.push(server);
          open(server);
        })
        .catch(() => setRoute('finanzas'));
      return;
    }
    // Docs del pipeline (presupuesto/OC/remito): el link resuelve el doc y abre su EXPEDIENTE.
    if (segs.length >= 2 && URL_DOC_EXP[seg]) {
      const cfg = URL_DOC_EXP[seg];
      const toExp = (doc) => openExpedienteFor(doc.expedienteId, seg);
      const item = (window[cfg.coll] || []).find((x) => x.publicId === segs[1]);
      if (item) { toExp(item); return; }
      fetch(`/api/${cfg.endpoint}/${encodeURIComponent(segs[1])}`)
        .then((r) => (r.ok ? r.json() : Promise.reject(r.status)))
        .then((server) => {
          if (!server || !server.id) throw new Error('sin id');
          const coll = window[cfg.coll] || (window[cfg.coll] = []);
          if (!coll.some((x) => x.id === server.id)) coll.push(server);
          toExp(server);
        })
        .catch(() => setRoute(seg));
      return;
    }
    // ── Fix (b): reconstruye `newDoc` (clase/lado) desde la query string — sin esto,
    // refrescar/deep-linkear a 'finanzas-nota-nueva' dejaba newDoc===null y la pantalla
    // en blanco (el render está gateado por `route === 'finanzas-nota-nueva' && newDoc`).
    if (seg === 'finanzas-nota-nueva') {
      const params = new URLSearchParams(search || '');
      const clase = params.get('clase') === 'ND' ? 'ND' : 'NC';
      const lado = params.get('lado') === 'pagos' ? 'pagos' : 'cobros';
      setNewDoc({ clase, lado });
      setRoute('finanzas-nota-nueva');
      return;
    }
    setRoute(URL_SEG_TO_ROUTE[seg] || seg);
  };

  // Estado inicial desde la URL (deep-link) + atrás/adelante del navegador (popstate).
  React.useEffect(() => {
    applyPath(window.location.pathname, window.location.search);
    const onPop = () => applyPath(window.location.pathname, window.location.search);
    window.addEventListener('popstate', onPop);
    return () => window.removeEventListener('popstate', onPop);
  }, []);

  // Reflejar route/ítem en la URL sin recargar. pushState = nueva entrada de historial,
  // así "atrás" navega entre pantallas en vez de salir de la app (al login). Se compara
  // contra pathname+search (no solo pathname) para que 'finanzas-nota-nueva' (con query
  // string, fix b) no dispare un pushState en cada render.
  React.useEffect(() => {
    if (!user) return;
    const path = routeToPath();
    const current = window.location.pathname + window.location.search;
    if (current !== path) window.history.pushState({ route }, '', path);
    localStorage.setItem('micos-route', route); // respaldo de "última pantalla"
  }, [route, selectedExpedienteId, selectedCompraId, selectedLogisticaId, selectedCajaChicaId, selectedInventarioId, selectedEntidadId, selectedMedicoId, selectedParticularId, selectedProveedorId, selectedDoc, newDoc]);

  return { routeToPath, applyPath, openExpedienteFor, URL_DETAIL, URL_DOC, URL_DOC_EXP, DETAIL_TO_SEG, URL_LIST_ALIAS, URL_SEG_TO_ROUTE };
}

Object.assign(window, { ROUTE_SECTION, RouteDenied, ROUTE_SIDEBAR_KEY, sidebarKeyForRoute, useMicosRouter });
