// ─── Directorio · Entidades — lista + detalle (7 secciones) ────────────────
// EntidadesScreen (lista, sobre EntityListScreen) + EntidadDetailScreen
// (detalle read-only + Editar → RecordFormScreen). Movido a screens/directorio/
// (P10, junto con medicos.jsx/particulares.jsx — mismo esqueleto sobre
// dir-kit). Expone a window: EntidadesScreen, EntidadDetailScreen. Consume
// window.ENTIDADES/EXPEDIENTES/COMPRAS/COBROS/PAGOS/MEDICOS (core/
// colecciones.js), EntityListScreen/useEntityStats (ui/entity-list.jsx),
// ENTIDAD_TIPOS/ENTIDAD_ESTADO_CFG (dominio/pipeline.js), DirHeader/DirCard/
// DirFieldGrid/DirChip/DirRegistros/DirNotas (ui/dir-kit.jsx),
// formatCuit/formatMoneyShort/formatDate (dominio/formato.js),
// EntidadSubrecursos (screens/directorio/entidad-subrecursos.jsx — Contactos/
// Convenios, DECISIÓN 1 de ARBITRAJES.md ítem 1, 06/07/2026: CRUD real contra
// /api/entidad-contactos y /api/entidad-convenios, antes disabled-honesto).
//
// FIX (mapa-comprension.md §otros, bug severidad baja — "comprasEnt cruza por
// razonSocial, no por id"): `expIdsEnt` cruzaba `EXPEDIENTES` con la entidad
// por NOMBRE (`x.entidad && x.entidad.razonSocial === e.razonSocial`, frágil
// si dos entidades comparten razón social o se edita el nombre después de
// creados los expedientes) y guardaba el `publicId` del expediente — pero
// `COMPRAS`/`PAGOS` exponen `expedienteId` como el UUID real (FK, ver
// src/lib/api/serializers/compras.ts), NO el publicId: la comparación nunca
// podía coincidir contra ese formato. Ahora cruza por `entidadId === e.id`
// (columna real `expedientes.entidad_id`, ver `\d expedientes`) y guarda el
// `id` (uuid) del expediente, el mismo formato que `compra.expedienteId`.
// Verificación empírica (docker exec psql): HOY `compras.expediente_id` es
// NULL en el 100% de las 577 compras reales (0 compras migradas con
// expediente vinculado) — este fix no puede mostrar un número distinto de 0
// contra los datos actuales (es un gap de completitud de la migración, fuera
// de alcance de este paquete), pero corrige la lógica para el día que haya
// compras con expediente real: antes el bug de tipo (uuid vs publicId) lo
// hubiera dejado en 0 igual, silenciosamente, para siempre.

// FIX P13 (ítem 17, anti-patrón `|| ENTIDADES[0]` residual): `EntidadDetailScreen`
// caía silenciosamente a `ENTIDADES[0]` (OTRA entidad, sin aviso) si `entidadId`
// no estaba en la ventana hidratada — Inventario ya eliminó este patrón en P10
// (guard de fetch-and-merge, ver screens/inventario/detalle.jsx) y el fix
// id-race (RecordFormScreen) lo mitiga pero no lo erradica acá. Mismo molde:
// si no está local, se trae por `GET /api/entidades/<id>` y se mergea en
// window.ENTIDADES antes de renderizar; si tampoco existe en el server,
// pantalla de "no encontrada" honesta — nunca OTRO registro.
const { useState: _useStateEntD, useEffect: _useEffectEntD } = React;

// FIX AUDITORIA-ui-decorativa-05-07-2026.md, BAJA "KpiCard 'Convenios' del header de la
// lista de Entidades": `e.convenios` NUNCA llega poblado (GET /api/entidades hace select('*')
// sin join a entidad_convenio, que no tiene ruta) — el contador era estructuralmente siempre
// 0, mostrado como si fuera un dato real. CLAUDE.md decisión estructural #3 ("Toda info real
// y basada en la DB... lo que quede sin uso tras el pulido se puede borrar"): se saca la KPI
// en vez de fingir un conteo de una relación que hoy no existe. Vuelve el día que
// entidad_convenio tenga ruta de lectura.

// ─── Lista ──────────────────────────────────────────
function EntidadesScreen({ onOpen, onNew }) {
  const total = ENTIDADES.reduce((s, e) => s + e.saldoCobrar, 0);

  // v_entidades_stats: {activas, sumSaldoCobrar, sumExpedientesActivos, byTipo}.
  // "Convenios" no está en la vista (no se persiste server-side) — queda local.
  const srvStats = window.useEntityStats('entidades');
  const kActivas = srvStats ? srvStats.activas : ENTIDADES.filter(e => (e.estado || '').toUpperCase().startsWith('ACTIV')).length;
  const kSumSaldo = srvStats ? srvStats.sumSaldoCobrar : total;
  const kSumExp = srvStats ? srvStats.sumExpedientesActivos : ENTIDADES.reduce((s, e) => s + e.expedientesActivos, 0);
  const kByTipo = srvStats ? (srvStats.byTipo || {}) : null;

  return (
    <EntityListScreen
      eyebrow="Directorio · Entidades"
      rowChevron
      title="Entidades"
      description="Obras sociales, prepagas, sanatorios, clínicas y hospitales con convenio."
      primaryAction={{ label: 'Nueva entidad', icon: IconPlus, onClick: onNew }}
      secondaryAction={{ label: 'Exportar', icon: IconDownload }}
      kpis={[
        { label: 'Activas',          value: String(kActivas), deltaTone: 'neutral', icon: IconBuilding },
        { label: 'Saldo a cobrar',   value: formatMoneyShort(kSumSaldo), deltaTone: 'neutral', icon: IconWallet },
        { label: 'Expedientes',      value: String(kSumExp), deltaTone: 'neutral', icon: IconFileText },
      ]}
      filterChips={{
        options: ENTIDAD_TIPOS.map(t => ({
          label: t, value: t,
          count: kByTipo ? (kByTipo[t] || 0) : ENTIDADES.filter(e => e.tipo === t).length,
        })).filter(o => o.count > 0),
        applyFilter: (e, v) => e.tipo === v,
      }}
      columns={[
        { key: 'entidad', label: 'Entidad', width: 'minmax(180px, 1fr)',
          render: e => (
            <div className="min-w-0">
              <p className="text-[13.5px] font-bold truncate" style={{ color: 'var(--foreground)' }}>{e.razonSocial}</p>
              <p className="font-mono text-[11px] font-bold tracking-wide uppercase mt-0.5" style={{ color: 'var(--accent)' }}>{e.publicId}</p>
            </div>
          ),
        },
        { key: 'tipo', label: 'Tipo', width: 'minmax(110px, 1fr)',
          render: e => <TablePill label={e.tipo} tone="info" /> },
        { key: 'cuit', label: 'CUIT', width: 'minmax(130px, 1fr)', mono: true,
          render: e => <span className="text-[12.5px] font-mono tabular-nums" style={{ color: 'var(--muted-foreground)' }}>{formatCuit(e.cuit)}</span> },
        { key: 'domicilio', label: 'Domicilio', width: 'minmax(170px, 1fr)',
          render: e => (
            <div className="min-w-0">
              <p className="text-[12.5px] truncate" style={{ color: 'var(--foreground)' }}>{e.domicilio}</p>
              <p className="text-[11px] mt-0.5 truncate" style={{ color: 'var(--muted-foreground)' }}>{[e.localidad, e.provincia].filter(Boolean).join(', ')}</p>
            </div>
          ),
        },
        { key: 'expedientes', label: 'Expedientes', width: 'minmax(110px, 1fr)',
          render: e => <span className="text-[14px] font-extrabold tabular-nums" style={{ color: 'var(--foreground)' }}>{e.expedientesActivos}</span> },
        { key: 'estado', label: 'Estado', width: 'minmax(120px, 1fr)',
          render: e => {
            const cfg = ENTIDAD_ESTADO_CFG[e.estado] || ENTIDAD_ESTADO_CFG.ACTIVA;
            return <TablePill label={cfg.label} tone={cfg.tone} />;
          },
        },
      ]}
      rows={ENTIDADES}
      collection="entidades"
      searchPlaceholder="Buscar por razón social, CUIT o tipo…"
      searchFilter={(e, q) =>
        (e.razonSocial || '').toLowerCase().includes(q) ||
        (e.cuit || '').toLowerCase().includes(q) ||
        (e.tipo || '').toLowerCase().includes(q) ||
        (e.localidad || '').toLowerCase().includes(q)
      }
      onRowClick={onOpen}
      dateField="fechaCreacion"
      exportFilename="Entidades"
      exportConfig={[
        { label: 'ID', value: (e) => e.publicId },
        { label: 'Razón social', value: (e) => e.razonSocial },
        { label: 'Tipo', value: (e) => e.tipo || '' },
        { label: 'CUIT', value: (e) => e.cuit },
        { label: 'Condición IVA', value: (e) => e.condicionIva || '' },
        { label: 'Domicilio', value: (e) => e.domicilio || '' },
        { label: 'Localidad', value: (e) => e.localidad || '' },
        { label: 'Provincia', value: (e) => e.provincia || '' },
        { label: 'Cond. venta', value: (e) => e.condicionVenta || '' },
        { label: 'Expedientes activos', value: (e) => e.expedientesActivos },
        { label: 'Saldo a cobrar', value: (e) => e.saldoCobrar },
        { label: 'Estado', value: (e) => (ENTIDAD_ESTADO_CFG[e.estado] || {}).label || e.estado || '' },
      ]}
    />
  );
}

// ─── Detalle ────────────────────────────────────────
function EntidadDetailScreen({ entidadId, onBack, navigator, onEdit }) {
  // Guard fetch-and-merge (ver header) — nunca ENTIDADES[0].
  const foundLocal = (window.ENTIDADES || []).find(x => x.id === entidadId);
  const [, _setMergeTickEnt] = _useStateEntD(0);
  const [notFoundEnt, setNotFoundEnt] = _useStateEntD(false);
  _useEffectEntD(() => {
    if (foundLocal || !entidadId) return undefined;
    let cancelled = false;
    fetch(`/api/entidades/${encodeURIComponent(entidadId)}`, { credentials: 'same-origin' })
      .then((r) => (r.ok ? r.json() : Promise.reject(r.status)))
      .then((server) => {
        if (cancelled || !server || !server.id) throw new Error('sin id');
        const col = window.ENTIDADES || (window.ENTIDADES = []);
        if (!col.some((x) => x.id === server.id)) col.push(server);
        _setMergeTickEnt((t) => t + 1);
      })
      .catch((err) => {
        if (cancelled) return;
        console.warn('[EntidadDetailScreen] fetch-and-merge', entidadId, err);
        setNotFoundEnt(true);
      });
    return () => { cancelled = true; };
  }, [entidadId, foundLocal]);

  if (!foundLocal) {
    if (notFoundEnt) {
      return (
        <div className="space-y-6 pt-2">
          <button onClick={onBack} className="flex items-center gap-2 text-[11px] font-bold uppercase tracking-[0.12em] cursor-pointer" style={{ color: 'var(--muted-foreground)' }}>
            <IconArrowLeft size={13} />Volver a Entidades
          </button>
          <p className="text-[14px] font-semibold" style={{ color: 'var(--muted-foreground)' }}>No se encontró la entidad.</p>
        </div>
      );
    }
    return null;
  }
  const e = foundLocal;
  const estCfg = ENTIDAD_ESTADO_CFG[e.estado] || ENTIDAD_ESTADO_CFG.ACTIVA;
  const [exportToast, setExportToast] = _useStateEntD(null);

  // Registros asociados, derivados de las colecciones (no inventados):
  // expedientes de la entidad (por id real, ver FIX en el header — antes por
  // razonSocial) → compras vinculadas; cobros por entidadId.
  const expIdsEnt = new Set(
    EXPEDIENTES.filter(x => x.entidadId === e.id).map(x => x.id)
  );
  const comprasEnt = COMPRAS.filter(c => expIdsEnt.has(c.expedienteId)).length;
  const cobrosEnt = COBROS.filter(c => c.entidadId === e.id);
  // FIX AUDITORIA-ui-decorativa-05-07-2026.md MEDIA "tarjeta stat Pagos navega a Finanzas
  // sin filtrar": la tarjeta "Pagos" acá NO tenía relación real que filtrar — `pagos` no
  // tiene `entidad_id` (son egresos a PROVEEDORES, ver comentario de `toPagos` en
  // core/directorio-nav.jsx: "Pagos no se filtran por entidad") y el join que intentaba
  // vía `expedienteId` comparaba contra una columna que ni existe en `pagos`, así que el
  // contador daba 0 SIEMPRE y el click navegaba a Finanzas sin ningún filtro aplicable —
  // una tarjeta que prometía un drill-down imposible. Se saca la tarjeta (igual que la
  // KPI "Convenios" de la lista, CLAUDE.md decisión estructural #3): una Entidad
  // genuinamente no tiene Pagos propios que mostrar.

  const medicos = (e.medicosAsociados || [])
    .map(id => MEDICOS.find(m => m.id === id))
    .filter(Boolean);

  // ── Datos principales ─────────────────────────────
  const datosPrincipales = [
    { label: 'Razón social',    value: e.razonSocial },
    { label: 'CUIT',            value: e.cuit,           mono: true },
    { label: 'Tipo',            value: e.tipo,           pill: true, pillTone: 'info' },
    { label: 'Condición IVA',   value: e.condicionIva },
    { label: 'Cond. venta',     value: e.condicionVenta },
    { label: 'Plazo de pago',   value: e.plazoPago,      mono: true },
  ];

  // ── Ubicación ─────────────────────────────────────
  const ubicacion = [
    { label: 'Domicilio',  value: e.domicilio },
    { label: 'Localidad',  value: e.localidad },
    { label: 'Provincia',  value: e.provincia },
  ];

  return (
    <div className="space-y-7 pt-2">

      {/* Header */}
      <DirHeader
        backLabel="Volver a Entidades"
        eyebrow="Directorio · Entidad"
        onBack={onBack}
        title={e.razonSocial}
        subtitle={
          <>
            <span className="font-mono font-bold" style={{ color: 'var(--accent)' }}>{e.publicId}</span>
            <span className="mx-1.5 opacity-50">·</span>
            <span>{e.tipo}</span>
            <span className="mx-1.5 opacity-50">·</span>
            <span className="font-mono tabular-nums">{formatCuit(e.cuit)}</span>
          </>
        }
        statusLabel={estCfg.label}
        statusTone={estCfg.tone}
        actions={<>
          {/* FIX MEDIA "Exportar de detalle de Entidad": wired de verdad (dirExportRegistro,
              ui/dir-kit.jsx) — antes sin onClick. */}
          <button className="btn btn-outline" onClick={() => {
            const res = dirExportRegistro(`Entidad ${e.publicId}`, [datosPrincipales, ubicacion]);
            setExportToast(`Exportado · 1 fila${res.format === 'csv' ? ' (CSV)' : ''}`);
            setTimeout(() => setExportToast(null), 3600);
          }}><IconDownload size={15} /><span className="hidden sm:inline">Exportar</span></button>
          <button className="btn btn-primary" onClick={() => onEdit && onEdit()}><IconPencil size={14} /><span className="hidden sm:inline">Editar</span></button>
        </>}
      />

      {/* Datos principales */}
      <DirCard eyebrow="Datos principales">
        <DirFieldGrid fields={datosPrincipales} />
      </DirCard>

      {/* Ubicación */}
      <DirCard eyebrow="Ubicación">
        <DirFieldGrid fields={ubicacion} cols={3} />
      </DirCard>

      {/* Contactos + Convenios — DECISIÓN 1 (ARBITRAJES.md ítem 1, 06/07/2026): CRUD real
          contra /api/entidad-contactos y /api/entidad-convenios (antes disabled-honesto,
          AUDITORIA-ui-decorativa-05-07-2026.md MEDIA). Partido a su propio archivo con
          nombre propio (entidad-subrecursos.jsx) — es un tema aparte (fetch/POST/PATCH/
          DELETE + 2 modales) del layout de solo-lectura de esta ficha, y este archivo ya
          rondaba el techo de líneas (AGENTS.md: "un archivo = un tema"). */}
      <EntidadSubrecursos entidadId={e.id} />

      {/* Médicos asociados */}
      <DirCard eyebrow={`Médicos asociados · ${medicos.length}`}>
        {medicos.length > 0 ? (
          <div className="flex flex-wrap gap-2.5">
            {medicos.map(m => (
              <DirChip key={m.id}
                label={`${m.titulo ? m.titulo + ' ' : ''}${m.nombre} ${m.apellido}`}
                sub={m.especialidades?.[0]}
                tone="accent"
                onClick={() => navigator && navigator.toMedico(m.id)}
              />
            ))}
          </div>
        ) : (
          <p className="text-[13px] italic" style={{ color: 'var(--muted-foreground)' }}>Sin médicos asociados todavía.</p>
        )}
      </DirCard>

      {/* Registros asociados */}
      <div className="space-y-3">
        <p className="text-[11px] font-bold uppercase tracking-[0.2em]" style={{ color: 'var(--accent)' }}>
          Registros asociados
        </p>
        <DirRegistros items={[
          { label: 'Expedientes', value: String(e.expedientesActivos),            sublabel: 'activos', icon: <IconFileText size={16} />,    onClick: () => navigator && navigator.toExpedientes({ entidadId: e.id }) },
          { label: 'Compras',     value: String(comprasEnt),                       sublabel: 'asociadas', icon: <IconShoppingCart size={16} />, onClick: () => navigator && navigator.toCompras(e.id) },
          { label: 'Cobros',      value: String(cobrosEnt.length),                 sublabel: formatMoneyShort(e.saldoCobrar), icon: <IconDollar size={16} />,      onClick: () => navigator && navigator.toCobros(e.id) },
        ]} />
      </div>

      {/* Notas */}
      <DirNotas notas={e.notas} />

      <DirToast message={exportToast} />
    </div>
  );
}

Object.assign(window, { EntidadesScreen, EntidadDetailScreen });
