

const CONFIG = {
  clientId: 'TU_CLIENT_ID_LOCAL',
  clientSecret: 'TU_CLIENT_SECRET_LOCAL',
  subscriptionKey: 'TU_SUBSCRIPTION_KEY_LOCAL',
  apiBaseUrl: 'http://localhost:3001/api'
};

const ROLES_ORG = {
  IPS: 'Organizacion prestadora de salud',
  EPS: 'EPS',
  GENERIC: 'Organización'
};

const ETIQUETAS_PAGINACION = {
  next: '▶️ Siguiente',
  previous: '◀️ Anterior',
  first: '⏮️ Primero',
  last: '⏭️ Último',
  self: '📍 Actual'
};

const TABLA_INMUNIZACIONES_HEADER = `
  <thead>
    <tr>
      <th>#</th><th>Vacuna</th><th>Fecha</th><th>Lote</th>
      <th>Prestador</th><th>Fabricante</th><th>Estado</th>
    </tr>
  </thead>`;

// ============================================================================
// 2. ESTADO GLOBAL
// ============================================================================

const AppState = {
  datosGlobalesFHIR: null,
  datosEncuentrosGlobales: null,
  ultimoBundleInmunizacion: null,

  reset() {
    this.datosGlobalesFHIR = null;
    this.datosEncuentrosGlobales = null;
    this.ultimoBundleInmunizacion = null;
  }
};

// ============================================================================
// 3. UTILIDADES DOM
// ============================================================================

const DOM = {
  byId: (id) => document.getElementById(id),
  qs: (sel) => document.querySelector(sel),

  get elements() {
    return {
      containerPatient: this.byId('accordion-body-patient'),
      contenedor: this.byId('accordion-body-envios'),
      contador: this.byId('contadorDocumentos'),
      contenedorEncuentros: this.byId('accordion-body-encuentros'),
      contadorEncuentros: this.byId('contadorEncuentros'),
      contadorVacunas: this.byId('contadorVacunas'),
      paginacion: this.byId('paginacion'),
      modalContent: this.byId('mostrarCompositions'),
      botonConsulta: this.byId('consultaInfoIhce'),
      inputTipoDocumento: this.byId('inputTipoDocumento'),
      inputDocumento: this.byId('inputDocumento'),
      inputFechaDesde: this.byId('inputFechaDesde'),
      filtroTipoEncuentro: this.byId('filtroTipoEncuentro')
    };
  },

  setBadgeText(id, text) {
    const el = this.byId(id);
    if (el) el.textContent = text;
  },

  setBadgeCount(id, count, { hideWhenZero = true } = {}) {
    const el = this.byId(id);
    if (!el) return;

    const n = Number.isFinite(count) ? count : 0;
    el.textContent = n;
    el.className = 'badge bg-warning';
    el.style.display = (hideWhenZero && n === 0) ? 'none' : 'inline-block';
  }
};

// ============================================================================
// 4. UTILIDADES GENERALES
// ============================================================================

const Utils = {
  safeArray: (arr) => Array.isArray(arr) ? arr : [],

  getText: (s) => typeof s === 'string' ? s : '',

  escapeHtml(str) {
    return (str ?? '').toString()
      .replaceAll('&', '&amp;')
      .replaceAll('<', '&lt;')
      .replaceAll('>', '&gt;')
      .replaceAll('"', '&quot;')
      .replaceAll("'", '&#039;');
  },

  formatearFecha(f) {
    if (!f) return '';
    if (/^\d{4}-\d{2}-\d{2}/.test(f)) return f.slice(0, 10);
    return f;
  },

  calcularEdad(birthDateStr) {
    if (!birthDateStr) return 'N/A';
    const birth = new Date(birthDateStr);
    if (Number.isNaN(birth.getTime())) return 'N/A';

    const today = new Date();
    let age = today.getFullYear() - birth.getFullYear();
    const m = today.getMonth() - birth.getMonth();

    if (m < 0 || (m === 0 && today.getDate() < birth.getDate())) age--;
    return age < 0 ? 'N/A' : age;
  },

  toText: (v) => v == null ? '' : String(v).trim(),

  field(label, value) {
    const v = this.toText(value);
    return v ? `<p><strong>${label}:</strong> ${this.escapeHtml(v)}</p>` : '';
  }
};

// ============================================================================
// 5. UTILIDADES FHIR
// ============================================================================

const FHIRUtils = {
  ensureRefArrays(ref = {}) {
    return {
      patients: Utils.safeArray(ref.patients),
      practitioners: Utils.safeArray(ref.practitioners),
      organizations: Utils.safeArray(ref.organizations || ref.organization),
      conditions: Utils.safeArray(ref.conditions),
      allergyIntolerances: Utils.safeArray(ref.allergyIntolerances),
      medicationStatements: Utils.safeArray(ref.medicationStatements),
      medicationAdministrations: Utils.safeArray(ref.medicationAdministrations),
      medicationRequests: Utils.safeArray(ref.medicationRequests),
      familyMemberHistories: Utils.safeArray(ref.familyMemberHistories),
      procedures: Utils.safeArray(ref.procedures),
      encounters: Utils.safeArray(ref.encounters),
      practitionerRoles: Utils.safeArray(ref.practitionerRoles),
      locations: Utils.safeArray(ref.locations),
      observations: Utils.safeArray(ref.observations),
      riskAssessments: Utils.safeArray(ref.riskAssessments),
      serviceRequests: Utils.safeArray(ref.serviceRequests),
      documentReferences: Utils.safeArray(ref.documentReferences)
    };
  },

  mergeById(target = [], source = []) {
    const seen = new Set(target.map((x) => x?.id));
    const toAdd = source.filter((x) => x && !seen.has(x.id));
    target.push(...toAdd);
  },

  mergeReferencedResources(globalRef, newRef) {
    const g = this.ensureRefArrays(globalRef);
    const n = this.ensureRefArrays(newRef);

    this.mergeById(g.patients, n.patients);
    this.mergeById(g.practitioners, n.practitioners);
    this.mergeById(g.organizations, n.organizations);
    this.mergeById(g.conditions, n.conditions);
    this.mergeById(g.allergyIntolerances, n.allergyIntolerances);
    this.mergeById(g.medicationStatements, n.medicationStatements);
    this.mergeById(g.medicationAdministrations, n.medicationAdministrations);
    this.mergeById(g.medicationRequests, n.medicationRequests);
    this.mergeById(g.familyMemberHistories, n.familyMemberHistories);
    this.mergeById(g.procedures, n.procedures);
    this.mergeById(g.encounters, n.encounters);
    this.mergeById(g.locations, n.locations);
    this.mergeById(g.observations, n.observations);

    return g;
  },

  collectSectionReferences(composition) {
    const ids = new Set();
    Utils.safeArray(composition?.section).forEach((sec) => {
      Utils.safeArray(sec?.entry).forEach((e) => {
        const ref = Utils.getText(e?.reference);
        if (ref.includes('/')) {
          const [, id] = ref.split('/');
          if (id) ids.add(id);
        }
      });
    });
    return ids;
  },

  extraerMensajeFHIR(responseJson) {
    try {
      if (!responseJson) return null;
      if (responseJson.resourceType === 'OperationOutcome' && Array.isArray(responseJson.issue)) {
        const texto = responseJson.issue
          .map(i => i.details?.text || i.diagnostics)
          .filter(Boolean)
          .join(' | ');
        return texto || null;
      }
      return null;
    } catch {
      return null;
    }
  },

  getRegionFromOrg(org) {
    if (!org?.address?.[0]) return 'N/A';
    const addr = org.address[0];
    return (
      addr.city ||
      addr.district ||
      addr.state ||
      (Array.isArray(addr.line) ? addr.line.join(', ') : '') ||
      'N/A'
    );
  },

  obtenerOrganizacionDelRDA(recurso, resultado) {
    const ref = Utils.getText(recurso?.custodian?.reference) ||
      Utils.getText(recurso?.attester?.party?.reference);

    if (!ref.includes('/')) return null;

    const [, orgId] = ref.split('/');
    return resultado?.referencedResources?.organizations?.find((o) => o?.id === orgId) || null;
  }
};

// ============================================================================
// 6. SERVICIOS API
// ============================================================================

const APIService = {
  async consultarComposition(tipoDocumento, documento) {
    if (!Utils.getText(documento).trim()) {
      throw new Error('❌ No se proporcionó un número de documento válido');
    }
    if (!Utils.getText(tipoDocumento).trim()) {
      throw new Error('❌ No se proporcionó un tipo de documento válido');
    }

    const fechaDesde = DOM.elements.inputFechaDesde?.value || '';

    const parametros = {
      clientId: CONFIG.clientId,
      clientSecret: CONFIG.clientSecret,
      subscriptionKey: CONFIG.subscriptionKey,
      payload: {
        resourceType: 'Parameters',
        parameter: [
          {
            name: 'identifier',
            part: [
              { name: 'type', valueString: tipoDocumento },
              { name: 'value', valueString: String(documento) }
            ]
          }
        ]
      }
    };

    if (fechaDesde) {
      parametros.payload.parameter.push({ name: 'fechaDesde', valueDate: fechaDesde });
    }

   

    const response = await fetch(`${CONFIG.apiBaseUrl}/composition`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Accept': 'application/json'
      },
      body: JSON.stringify(parametros)
    });

    const responseText = await response.text();

    if (!response.ok) {
      let msg = `Error ${response.status}: ${response.statusText}`;
      try {
        const json = JSON.parse(responseText);
        msg = json.message || json.error || msg;
      } catch {
        msg = responseText || msg;
      }
      throw new Error(msg);
    }

    return JSON.parse(responseText);
  },

  async consultarInmunizacion(tipoDocumento, documento) {
    const resp = await fetch(`${CONFIG.apiBaseUrl}/inmunizacion`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Accept': 'application/json'
      },
      body: JSON.stringify({
        clientId: CONFIG.clientId,
        clientSecret: CONFIG.clientSecret,
        subscriptionKey: CONFIG.subscriptionKey,
        payload: {
          resourceType: 'Parameters',
          parameter: [{
            name: 'identifier',
            part: [
              { name: 'type', valueString: tipoDocumento },
              { name: 'value', valueString: String(documento) }
            ]
          }]
        }
      })
    });

    let data = null;
    try {
      data = await resp.json();
    } catch (e) {
      throw new Error('Error parseando respuesta del servidor');
    }

   
    // Manejar OperationOutcome
    let operationOutcome = null;

    if (data?.error && typeof data.error === 'string') {
      try {
        operationOutcome = JSON.parse(data.error);
      } catch (e) {
        console.error('Error parseando data.error:', e);
      }
    } else if (data?.resourceType === 'OperationOutcome') {
      operationOutcome = data;
    }

    if (operationOutcome?.resourceType === 'OperationOutcome') {
      const issue = operationOutcome.issue?.[0];
      const errorCode = issue?.code;
      const errorText = issue?.details?.text || '';

      // Verificar si es "sin registros de vacunas"
      if (errorCode === 'not-found' &&
        (errorText.includes('no tiene registros de aplicación de vacunas') ||
          errorText.includes('PAIWEB-001') ||
          errorText.includes('PAIWEB'))) {
        return { resourceType: 'Bundle', entry: [] };
      }

      throw new Error(errorText || 'Error en la consulta');
    }

    if (!resp.ok) {
      throw new Error(`Error HTTP ${resp.status}: ${resp.statusText}`);
    }

    return data;
  },

  async cargarPagina(url, patientId) {
    const queryUrl =
      `${CONFIG.apiBaseUrl}/pagina` +
      `?url=${encodeURIComponent(url)}` +
      `&clientId=${encodeURIComponent(CONFIG.clientId)}` +
      `&clientSecret=${encodeURIComponent(CONFIG.clientSecret)}` +
      `&subscriptionKey=${encodeURIComponent(CONFIG.subscriptionKey)}` +
      `&patientId=${encodeURIComponent(patientId)}`;

    const response = await fetch(queryUrl);

    if (!response.ok) {
      const text = await response.text();
      throw new Error(`Backend ${response.status}: ${text}`);
    }

    return await response.json();
  },

  // ─── NUEVO: obtener PDF desde ruta FHIR ────────────────────────────────────
  async obtenerPDF(rutaDocumento) {
    // Extraer el ID dinámico del DocumentReference
    const match = rutaDocumento.match(/DocumentReference\/([^/]+)/);
    if (!match) {
      throw new Error('No se pudo extraer el ID del DocumentReference de la ruta: ' + rutaDocumento);
    }
    const docId = match[1];

    const url =
      `${CONFIG.apiBaseUrl}/DocumentReference/${docId}/0/descargar-rda-epicrisis` +
      `?clientId=${encodeURIComponent(CONFIG.clientId)}` +
      `&clientSecret=${encodeURIComponent(CONFIG.clientSecret)}` +
      `&subscriptionKey=${encodeURIComponent(CONFIG.subscriptionKey)}`;

  

    const response = await fetch(url);

    if (!response.ok) {
      const text = await response.text();
      throw new Error(`Error ${response.status}: ${text}`);
    }

    const blob = await response.blob();
    return URL.createObjectURL(blob);
  }
};

// ============================================================================
// 7. MAPPERS DE DATOS
// ============================================================================

const DataMappers = {
  mapInmunizacion(resource) {
    return {
      vacuna: resource.vaccineCode?.text || resource.vaccineCode?.coding?.[0]?.display || '',
      fecha: resource.occurrenceDateTime || resource.occurrenceString || '',
      lote: resource.lotNumber || '',
      estado: resource.status || '',
      prestador: Utils.safeArray(resource.performer)
        .map(p => p?.actor?.display)
        .filter(Boolean)
        .join(' / ') || '',
      fabricante: resource.manufacturer?.display || ''
    };
  },

  extractInmunizaciones(resourceOrBundle) {
    if (!resourceOrBundle) return [];

    const toImmu = [];

    if (resourceOrBundle.resourceType === 'Bundle' && Array.isArray(resourceOrBundle.entry)) {
      for (const e of resourceOrBundle.entry) {
        const r = e?.resource;
        if (r?.resourceType === 'Immunization') toImmu.push(r);
      }
    } else if (resourceOrBundle.resourceType === 'Immunization') {
      toImmu.push(resourceOrBundle);
    }

    return toImmu.map(this.mapInmunizacion);
  },

  mapEncounter(recurso) {
    if (!recurso) return null;

    // type[].coding puede ser objeto directo o array (perfil colombiano MinSalud RDA)
    const getCodingFromType = (t) => {
      const c = t?.coding;
      if (Array.isArray(c)) return c[0];
      if (c && typeof c === 'object') return c;
      return null;
    };

    // Participantes: tipo puede ser objeto {coding:[]} o {coding:{}} segun recurso
    const participantes = Utils.safeArray(recurso?.participant).map(p => {
      const tipoArr = Array.isArray(p?.type?.coding)
        ? p.type.coding
        : (p?.type?.coding ? [p.type.coding] : []);
      const tipoCoding = tipoArr[0];
      return {
        id: p?.id || '',
        rolCodigo: tipoCoding?.code || '',
        rolDisplay: tipoCoding?.display || '',
        referencia: Utils.getText(p?.individual?.reference) || ''
      };
    });

    // Diagnosticos del encuentro con extension de tipo diagnostico
    const diagnosticos = Utils.safeArray(recurso?.diagnosis).map(d => {
      const useCoding = Array.isArray(d?.use?.coding) ? d.use.coding[0] : d?.use?.coding;
      // Extension ExtensionDiagnosisType (tipo: confirmado nuevo, impresion, etc.)
      const extTipo = Utils.safeArray(d?.extension)
        .find(e => Utils.getText(e?.url).includes('ExtensionDiagnosisType'));
      return {
        id: d?.id || '',
        rank: d?.rank ?? null,
        rol: useCoding?.display || useCoding?.code || '',
        rolCodigo: useCoding?.code || '',
        tipoDx: extTipo?.valueCoding?.display || '',
        tipoDxCodigo: extTipo?.valueCoding?.code || '',
        condicion: Utils.getText(d?.condition?.reference) || ''
      };
    });

    // Hospitalizacion completa
    const hosp = recurso?.hospitalization;
    const hospitalizacion = hosp ? {
      // ExtensionDischargeDeceasedStatus -> estado de fallecimiento al egreso
      estadoFallecimiento: Utils.safeArray(hosp?.extension)
        .find(e => Utils.getText(e?.url).includes('DischargeDeceasedStatus'))
        ?.valueCoding?.display || '',
      viaIngreso: hosp?.admitSource?.coding?.[0]?.display || '',
      viaIngresoCodigo: hosp?.admitSource?.coding?.[0]?.code || '',
      condicionEgreso: hosp?.dischargeDisposition?.coding?.[0]?.display || '',
      condicionEgresoCodigo: hosp?.dischargeDisposition?.coding?.[0]?.code || '',
      destino: Utils.getText(hosp?.destination?.reference) || '',
      reingreso: hosp?.reAdmission?.coding?.[0]?.display || ''
    } : null;

    return {
      id: recurso?.id || '',
      status: recurso?.status || '',

      // Identificador del encuentro (ej: ADT-HS-9864463-12)
      identificador: recurso?.identifier?.[0]?.value || '',

      // Clase (IMP=inpatient, AMB=ambulatorio, EMER=urgencias)
      clase: {
        codigo: recurso?.class?.code || '',
        display: recurso?.class?.display || ''
      },

      // Tipos multiples: modalidad, grupo de servicios, entorno de atencion
      tipos: Utils.safeArray(recurso?.type).map(t => {
        const c = getCodingFromType(t);
        return { codigo: c?.code || '', display: c?.display || '', sistema: c?.system || '' };
      }).filter(t => t.codigo || t.display),

      // Periodo
      periodo: {
        inicio: recurso?.period?.start || '',
        fin: recurso?.period?.end || ''
      },

      // Motivos / causa externa (RIPSCausaExternaVersion2)
      motivos: Utils.safeArray(recurso?.reasonCode).map(rc => {
        const c = Array.isArray(rc?.coding) ? rc.coding[0] : rc?.coding;
        return { codigo: c?.code || '', display: c?.display || '', text: rc?.text || '' };
      }),

      // Participantes con rol
      participantes,

      // Diagnosticos con tipo y ranking
      diagnosticos,

      // Hospitalizacion
      hospitalizacion,

      // Ubicaciones
      ubicaciones: Utils.safeArray(recurso?.location).map(l => ({
        display: l?.location?.display || '',
        referencia: Utils.getText(l?.location?.reference) || '',
        estado: l?.status || ''
      })),

      // Prestador (serviceProvider)
      prestador: Utils.getText(recurso?.serviceProvider?.reference) || ''
    };
  },

  extractPatientData(patient) {
    if (!patient) return null;

    const calcularEdad = (birthDateStr) => {
      if (!birthDateStr) return '';
      const hoy = new Date();
      const n = new Date(birthDateStr);
      if (isNaN(n)) return '';
      let e = hoy.getFullYear() - n.getFullYear();
      const m = hoy.getMonth() - n.getMonth();
      if (m < 0 || (m === 0 && hoy.getDate() < n.getDate())) e--;
      return e < 0 ? '' : e;
    };

    const tipoDocCoding = patient?.identifier?.[0]?.type?.coding?.find((c) =>
      Utils.getText(c?.system).includes('ColombianPersonIdentifier')
    ) || patient?.identifier?.[0]?.type?.coding?.[0];

    const getExtension = (arr, urlPattern) =>
      arr?.find((e) => Utils.getText(e?.url).includes(urlPattern));

    const address0 = patient?.address?.[0];

    return {
      activo: patient?.active === true,
      fallecido: patient?.deceasedBoolean === true,
      nombres: `${Utils.safeArray(patient?.name?.[0]?.given).join(' ')} ${Utils.getText(patient?.name?.[0]?.family)}`.trim(),
      documento: patient?.identifier?.[0]?.value || '',
      tipoDocumento: tipoDocCoding ? {
        code: Utils.getText(tipoDocCoding?.code)?.toUpperCase() || '',
        display: tipoDocCoding?.display || ''
      } : null,
      fechaNacimiento: Utils.getText(patient?.birthDate) || '',
      horaNacimiento: getExtension(patient?._birthDate?.extension, 'ExtensionBirthTime')?.valueTime || '',
      edad: calcularEdad(patient?.birthDate),
      sexoBiologico: Utils.getText(patient?.gender) || '',
      sexoBiologicoExt: getExtension(patient?._gender?.extension, 'ExtensionBiologicalGender')?.valueCoding?.display || '',
      identidadGenero: getExtension(patient?.extension, 'ExtensionPatientGenderIdentity')?.valueCoding?.display || '',
      nacionalidad: getExtension(patient?.extension, 'ExtensionPatientNationality')?.valueCoding?.display || '',
      etnia: getExtension(patient?.extension, 'ExtensionPatientEthnicity')?.valueCoding?.display || '',
      discapacidad: getExtension(patient?.extension, 'ExtensionPatientDisability')?.valueCoding?.display || '',
      paisResidencia: Utils.getText(address0?.country) || '',
      municipioResidencia: Utils.getText(address0?.city) || '',
      zonaResidencia: getExtension(address0?.extension, 'ExtensionResidenceZone')?.valueCoding?.display || '',
      divipolaMunicipio: getExtension(address0?._city?.extension, 'ExtensionDivipolaMunicipality')?.valueCoding?.code || '',
      codigoPais: getExtension(address0?._country?.extension, 'ExtensionCountryCode')?.valueCoding?.code || ''
    };
  },

  extractPractitionerData(practitioner) {
    if (!practitioner) return null;

    const getExtension = (arr, urlPattern) =>
      arr?.find((e) => Utils.getText(e?.url).includes(urlPattern));

    // Obtener tipo de documento
    const tipoDocCoding = practitioner?.identifier?.[0]?.type?.coding?.find((c) =>
      Utils.getText(c?.system).includes('ColombianPersonIdentifier')
    ) || practitioner?.identifier?.[0]?.type?.coding?.[0];

    // Extraer nombres y apellidos
    const name0 = practitioner?.name?.[0];
    const apellidoPaterno = getExtension(name0?._family?.extension, 'ExtensionFathersFamilyName')?.valueString || '';
    const apellidoMaterno = getExtension(name0?._family?.extension, 'ExtensionMothersFamilyName')?.valueString || '';
    const apellidoCompleto = name0?.family || `${apellidoPaterno} ${apellidoMaterno}`.trim();

    return {
      id: practitioner?.id || '',
      documento: practitioner?.identifier?.[0]?.value || '',
      tipoDocumento: tipoDocCoding ? {
        code: Utils.getText(tipoDocCoding?.code)?.toUpperCase() || '',
        display: tipoDocCoding?.display || ''
      } : null,
      nombres: Utils.safeArray(name0?.given).join(' '),
      apellidos: apellidoCompleto,
      apellidoPaterno: apellidoPaterno,
      apellidoMaterno: apellidoMaterno,
      nombreCompleto: `${Utils.safeArray(name0?.given).join(' ')} ${apellidoCompleto}`.trim(),
      activo: practitioner?.active === true,
      genero: Utils.getText(practitioner?.gender) || '',
      fechaNacimiento: Utils.getText(practitioner?.birthDate) || '',
      telecom: Utils.safeArray(practitioner?.telecom).map(t => ({
        sistema: t?.system || '',
        valor: t?.value || '',
        uso: t?.use || ''
      })),
      direccion: Utils.safeArray(practitioner?.address).map(a => ({
        uso: a?.use || '',
        tipo: a?.type || '',
        lineas: Utils.safeArray(a?.line),
        ciudad: a?.city || '',
        estado: a?.state || '',
        codigoPostal: a?.postalCode || '',
        pais: a?.country || ''
      })),
      // Cualificaciones profesionales
      qualification: Utils.safeArray(practitioner?.qualification).map(q => ({
        identificador: q?.identifier?.[0]?.value || '',
        codigo: q?.code?.coding?.[0]?.code || '',
        display: q?.code?.coding?.[0]?.display || '',
        emisor: q?.issuer?.display || q?.issuer?.reference || '',
        periodo: {
          inicio: q?.period?.start || '',
          fin: q?.period?.end || ''
        }
      })),
      // Foto
      foto: practitioner?.photo?.[0]?.url || '',
      // Idiomas de comunicación
      communication: Utils.safeArray(practitioner?.communication).map(c =>
        c?.coding?.[0]?.display || c?.text || ''
      )
    };
  },

  extractConditionData(condition) {
    if (!condition) return null;

    // Obtener el primer coding de cada campo
    const clinicalStatusCoding = condition?.clinicalStatus?.coding?.[0];
    const verificationStatusCoding = condition?.verificationStatus?.coding?.[0];
    const categoryCoding = condition?.category?.[0]?.coding?.[0];
    const codeCoding = condition?.code?.coding?.[0];
    const severityCoding = condition?.severity?.coding?.[0];

    return {
      id: condition?.id || '',

      // Estados
      estadoClinico: {
        codigo: clinicalStatusCoding?.code || '',
        display: clinicalStatusCoding?.display || '',
        sistema: clinicalStatusCoding?.system || ''
      },

      estadoVerificacion: {
        codigo: verificationStatusCoding?.code || '',
        display: verificationStatusCoding?.display || '',
        sistema: verificationStatusCoding?.system || ''
      },

      // Categoría
      categoria: {
        codigo: categoryCoding?.code || '',
        display: categoryCoding?.display || '',
        sistema: categoryCoding?.system || ''
      },

      // Severidad (si existe)
      severidad: severityCoding ? {
        codigo: severityCoding?.code || '',
        display: severityCoding?.display || '',
        sistema: severityCoding?.system || ''
      } : null,

      // Código de la condición (diagnóstico)
      codigo: {
        codigo: codeCoding?.code || '',
        display: codeCoding?.display || '',
        sistema: codeCoding?.system || '',
        text: condition?.code?.text || ''
      },

      // Parte del cuerpo afectada (si existe)
      bodySite: Utils.safeArray(condition?.bodySite).map(bs => ({
        codigo: bs?.coding?.[0]?.code || '',
        display: bs?.coding?.[0]?.display || '',
        text: bs?.text || ''
      })),

      // Sujeto (referencia al paciente)
      sujeto: Utils.getText(condition?.subject?.reference) || '',

      // Encuentro relacionado (si existe)
      encounter: Utils.getText(condition?.encounter?.reference) || '',

      // Fechas
      fechaInicio: condition?.onsetDateTime || condition?.onsetPeriod?.start || '',
      fechaFin: condition?.abatementDateTime || condition?.abatementPeriod?.end || '',
      fechaRegistro: condition?.recordedDate || '',

      // Quién registró
      registradoPor: Utils.getText(condition?.recorder?.reference) || '',

      // Quién aseveró/diagnosticó
      aseveradoPor: Utils.getText(condition?.asserter?.reference) || '',

      // Etapa de la condición (si existe)
      stage: Utils.safeArray(condition?.stage).map(s => ({
        resumen: s?.summary?.coding?.[0]?.display || s?.summary?.text || '',
        tipo: s?.type?.coding?.[0]?.display || ''
      })),

      // Evidencia (si existe)
      evidence: Utils.safeArray(condition?.evidence).map(e => ({
        codigo: e?.code?.[0]?.coding?.[0]?.display || '',
        detalle: Utils.safeArray(e?.detail).map(d => d?.reference || '')
      })),

      // Notas
      notas: Utils.safeArray(condition?.note).map(n => n?.text || '').join(' | ')
    };
  },

  extractAllergyData(allergy) {
    if (!allergy) return null;


    const clinicalStatusCoding = allergy?.clinicalStatus?.coding?.[0];
    const verificationCoding = allergy?.verificationStatus?.coding?.[0];
    const tipoAlergiaCoding = allergy?.code?.coding?.[0];   // tipo (ej: Medicamento)
    const descripcionLibre = allergy?.code?.text || '';     // ej: "alergia a penicilina"

    return {
      id: allergy?.id || '',

      // Estado clínico
      estadoClinico: {
        codigo: clinicalStatusCoding?.code || '',
        display: clinicalStatusCoding?.display || ''
      },

      // Estado de verificación (puede no venir)
      estadoVerificacion: verificationCoding ? {
        codigo: verificationCoding?.code || '',
        display: verificationCoding?.display || ''
      } : null,

      // Tipo de alergia según TipoAlergia MinSalud  (01=Medicamento, etc.)
      tipoAlergia: {
        codigo: tipoAlergiaCoding?.code || '',
        display: tipoAlergiaCoding?.display || '',
        sistema: tipoAlergiaCoding?.system || ''
      },

      // Descripción libre de la alergia
      descripcion: descripcionLibre,

      // type FHIR (allergy | intolerance) — puede estar ausente
      tipo: allergy?.type || '',

      // category[] FHIR (food | medication | environment | biologic) — puede estar ausente
      categorias: Utils.safeArray(allergy?.category),

      // criticality FHIR (low | high | unable-to-assess) — puede estar ausente
      criticidad: allergy?.criticality || '',

      // Referencias
      paciente: Utils.getText(allergy?.patient?.reference) || '',
      encounter: Utils.getText(allergy?.encounter?.reference) || '',

      // Fechas
      fechaInicio: allergy?.onsetDateTime || allergy?.onsetPeriod?.start || '',
      fechaRegistro: allergy?.recordedDate || allergy?.meta?.lastUpdated || '',

      // Profesionales
      registradoPor: Utils.getText(allergy?.recorder?.reference) || '',
      aseveradoPor: Utils.getText(allergy?.asserter?.reference) || '',

      // Reacciones (puede ser array vacío)
      reacciones: Utils.safeArray(allergy?.reaction).map(r => ({
        sustanciaEspecifica: r?.substance?.coding?.[0]?.display || r?.substance?.text || '',
        manifestaciones: Utils.safeArray(r?.manifestation).map(m =>
          m?.coding?.[0]?.display || m?.text || ''
        ),
        descripcion: r?.description || '',
        inicio: r?.onset || '',
        severidad: r?.severity || '',   // mild | moderate | severe
        exposicion: r?.exposureRoute?.coding?.[0]?.display || ''
      })),

      // Notas libres
      notas: Utils.safeArray(allergy?.note).map(n => n?.text || '').join(' | ')
    };
  },

  extractMedicationAdministrationData(med) {
    if (!med) return null;

    // Extensiones colombianas MinSalud RDA
    const getExt = (urlFrag) =>
      Utils.safeArray(med?.extension).find(e =>
        Utils.getText(e?.url).includes(urlFrag)
      );

    const extCantidad = getExt('ExtensionMedicationQuantity'); // cantidad total
    const extDosis = getExt('ExtensionDoseQuantity');        // forma farmacéutica / dosis

    const medCoding = med?.medicationCodeableConcept?.coding?.[0];
    const catCoding = med?.category?.coding?.[0];

    return {
      id: med?.id || '',
      status: med?.status || '',

      // Categoría (ColombianHealthTechnologyCategory)
      categoria: {
        codigo: catCoding?.code || '',
        display: catCoding?.display || ''
      },

      // Medicamento (código IUM / CUMS)
      medicamento: {
        codigo: medCoding?.code || '',
        display: medCoding?.display || '',
        sistema: medCoding?.system || ''
      },

      // Referencias
      paciente: Utils.getText(med?.subject?.reference) || '',
      encuentro: Utils.getText(med?.context?.reference) || '',   // context = Encounter
      solicitud: Utils.getText(med?.request?.reference) || '',   // MedicationRequest

      // Fecha de administración
      fechaAdministracion: med?.effectiveDateTime || med?.effectivePeriod?.start || '',

      // Dosificación
      dosage: {
        dosis: med?.dosage?.dose ? {
          valor: med.dosage.dose.value ?? '',
          unidad: med.dosage.dose.unit || '',
          codigo: med.dosage.dose.code || ''
        } : null,

        via: med?.dosage?.route?.coding?.[0] ? {
          codigo: med.dosage.route.coding[0].code || '',
          display: med.dosage.route.coding[0].display || ''
        } : null,

        frecuencia: med?.dosage?.rateQuantity ? {
          valor: med.dosage.rateQuantity.value ?? '',
          unidad: med.dosage.rateQuantity.unit || ''   // "Horas"
        } : null
      },

      // Extensión: cantidad total del medicamento
      cantidadTotal: extCantidad?.valueQuantity ? {
        valor: extCantidad.valueQuantity.value ?? '',
        unidad: extCantidad.valueQuantity.unit || '',
        codigo: extCantidad.valueQuantity.code || ''
      } : null,

      // Extensión: forma farmacéutica / dosis en unidades físicas
      formaFarmaceutica: extDosis?.valueQuantity ? {
        valor: extDosis.valueQuantity.value ?? '',
        unidad: extDosis.valueQuantity.unit || '',
        codigo: extDosis.valueQuantity.code || ''
      } : null,

      // Notas
      notas: Utils.safeArray(med?.note).map(n => n?.text || '').join(' | ')
    };
  },

  extractMedicationRequestData(req) {
    if (!req) return null;

    const medCoding = req?.medicationCodeableConcept?.coding?.[0];
    const catCoding = req?.category?.[0]?.coding?.[0];

    // Primer dosageInstruction (el perfil colombiano solo maneja uno)
    const di = req?.dosageInstruction?.[0];
    const doseAndRate = di?.doseAndRate?.[0];
    const timingCoding = di?.timing?.code?.coding?.[0];
    const repeat = di?.timing?.repeat;

    return {
      id: req?.id || '',
      status: req?.status || '',           // active | completed | cancelled…
      intent: req?.intent || '',           // order | plan | proposal…
      reportado: req?.reportedBoolean ?? null, // true = reportado por paciente/cuidador

      // Categoría (ColombianHealthTechnologyCategory)
      categoria: catCoding ? {
        codigo: catCoding.code || '',
        display: catCoding.display || ''
      } : null,

      // Medicamento (MipresINN / CUMS / IUM)
      medicamento: {
        codigo: medCoding?.code || '',
        display: medCoding?.display || '',
        sistema: medCoding?.system || ''
      },

      // Referencias
      paciente: Utils.getText(req?.subject?.reference) || '',
      encuentro: Utils.getText(req?.encounter?.reference) || '',
      solicitadoPor: Utils.getText(req?.requester?.reference) || '',

      // Fecha de la orden
      fechaOrden: req?.authoredOn || '',

      // Motivo (reasonCode)
      motivos: Utils.safeArray(req?.reasonCode).map(rc => ({
        codigo: rc?.coding?.[0]?.code || '',
        display: rc?.coding?.[0]?.display || '',
        text: rc?.text || ''
      })),

      // Instrucción de dosificación
      dosage: di ? {
        // Timing
        timing: {
          duracion: repeat?.duration ?? null,
          unidadDuracion: repeat?.durationUnit || '',  // d=días, h=horas, wk=semanas…
          display: timingCoding?.display || '',        // ej: "Día"
          codigo: timingCoding?.code || ''
        },

        // Vía de administración
        via: di?.route?.coding?.[0] ? {
          codigo: di.route.coding[0].code || '',
          display: di.route.coding[0].display || ''
        } : null,

        // Dosis (doseQuantity)
        dosis: doseAndRate?.doseQuantity ? {
          valor: doseAndRate.doseQuantity.value ?? '',
          unidad: doseAndRate.doseQuantity.unit || '',
          codigo: doseAndRate.doseQuantity.code || ''
        } : null,

        // Frecuencia (rateQuantity)
        frecuencia: doseAndRate?.rateQuantity ? {
          valor: doseAndRate.rateQuantity.value ?? '',
          unidad: doseAndRate.rateQuantity.unit || ''
        } : null,

        // Texto libre de instrucción (si existe)
        texto: di?.text || ''
      } : null,

      // Notas
      notas: Utils.safeArray(req?.note).map(n => n?.text || '').join(' | ')
    };
  },

  extractLocationData(location) {
    if (!location) return null;
    return {
      id: location?.id || '',
      nombre: location?.name || '',
      codigoReps: location?.identifier?.[0]?.value || '',
      sistemaReps: location?.identifier?.[0]?.system || '',
      estado: location?.status || '',
      descripcion: location?.description || '',
      organizacion: Utils.getText(location?.managingOrganization?.reference) || '',
      tipo: location?.type?.[0]?.coding?.[0]?.display || '',
      direccion: location?.address ? {
        lineas: Utils.safeArray(location.address.line),
        ciudad: location.address.city || '',
        estado: location.address.state || '',
        pais: location.address.country || ''
      } : null,
      posicion: location?.position ? {
        latitud: location.position.latitude ?? null,
        longitud: location.position.longitude ?? null
      } : null
    };
  },

  extractProcedureData(proc) {
    if (!proc) return null;

    const catCoding = proc?.category?.coding?.[0];
    const codeCoding = proc?.code?.coding?.[0];  // puede ser undefined (OtherTechnology usa code.text)
    const exts = Utils.safeArray(proc?.extension);

    // Detectar perfil (ProcedureRDA vs OtherTechnologyProcedureRDA)
    const perfil = Utils.safeArray(proc?.meta?.profile)
      .find(p => typeof p === 'string') || '';
    const esOtraTecnologia = perfil.toLowerCase().includes('othertechnology');

    return {
      id: proc?.id || '',
      status: proc?.status || '',

      // Perfil usado (determina si tiene codigo CUPS o solo texto)
      esOtraTecnologia,

      categoria: catCoding ? {
        codigo: catCoding.code || '',
        display: catCoding.display || ''
      } : null,

      // Para ProcedureRDA: codigo CUPS en coding[0]
      // Para OtherTechnologyRDA: solo code.text, sin coding
      codigo: codeCoding ? {
        codigo: codeCoding.code || '',
        display: codeCoding.display || '',
        sistema: codeCoding.system || ''
      } : null,

      // Descripcion libre (code.text - siempre disponible en ambos perfiles)
      codigoTexto: proc?.code?.text || '',

      paciente: Utils.getText(proc?.subject?.reference) || '',
      encuentro: Utils.getText(proc?.encounter?.reference) || '',

      // Fecha de realizacion
      fechaRealizacion: proc?.performedDateTime || proc?.performedPeriod?.start || '',

      // ExtensionRequestDate: fecha en que se solicitó el procedimiento
      fechaSolicitud: exts
        .find(e => Utils.getText(e?.url).includes('ExtensionRequestDate'))
        ?.valueDate || '',

      performers: Utils.safeArray(proc?.performer).map(p => ({
        funcion: p?.function?.coding?.[0]?.display || '',
        actor: Utils.getText(p?.actor?.reference) || ''
      })),

      motivos: Utils.safeArray(proc?.reasonCode).map(rc => ({
        codigo: rc?.coding?.[0]?.code || '',
        display: rc?.coding?.[0]?.display || '',
        text: rc?.text || ''
      })),

      // MainDiagnosis, Comobility-N
      razonesReferencia: Utils.safeArray(proc?.reasonReference).map(rr => ({
        id: rr?.id || '',
        referencia: Utils.getText(rr?.reference) || ''
      })),

      // ExtensionSurgicalMethod (si existe)
      metodoQuirurgico: exts
        .find(e => Utils.getText(e?.url).toLowerCase().includes('surgicalmethod'))
        ?.valueCoding?.display || '',

      bodySite: Utils.safeArray(proc?.bodySite).map(bs =>
        bs?.coding?.[0]?.display || bs?.text || ''
      ),

      notas: Utils.safeArray(proc?.note).map(n => n?.text || '').join(' | ')
    };
  },

  extractObservationData(obs) {
    if (!obs) return null;

    const exts = Utils.safeArray(obs?.extension);
    const comps = Utils.safeArray(obs?.component);
    const perfil = Utils.safeArray(obs?.meta?.profile).find(p => typeof p === 'string') || '';

    // Detectar tipo de observation por perfil
    const tipoObs = (() => {
      if (perfil.includes('AttendanceAllowanceRDA')) return 'incapacidad';
      if (perfil.includes('PatientOccupationAtEncounterRDA')) return 'ocupacion';
      if (perfil.includes('ProcedureResultRDA')) return 'resultado-procedimiento';
      if (perfil.includes('ObservationClarificationNoteRDA')) return 'nota-aclaratoria';
      return 'otro';
    })();

    return {
      id: obs?.id || '',
      status: obs?.status || '',
      tipoObs,

      // Codigo principal (SNOMED CT / CUPS)
      valor: obs?.valueCodeableConcept?.coding?.[0] ? {
        codigo: obs.valueCodeableConcept.coding[0].code || '',
        display: obs.valueCodeableConcept.coding[0].display || '',
        sistema: obs.valueCodeableConcept.coding[0].system || ''
      } : (obs?.valueString !== undefined ? obs.valueString : obs?.valueQuantity?.value ?? null),
      codigoTexto: obs?.code?.text || '',

      // Referencias
      paciente: Utils.getText(obs?.subject?.reference) || '',
      encuentro: Utils.getText(obs?.encounter?.reference) || '',

      // PartOf: procedimiento relacionado (para ProcedureResultRDA)
      parteDe: Utils.safeArray(obs?.partOf).map(p => Utils.getText(p?.reference)).filter(Boolean),

      // Fecha efectiva
      fechaEfectiva: obs?.effectiveDateTime || obs?.effectivePeriod?.start || '',

      // Performer (quien realizó)
      performers: Utils.safeArray(obs?.performer).map(p => Utils.getText(p?.reference)).filter(Boolean),

      // Device (equipo usado - ProcedureResultRDA)
      dispositivo: obs?.device?.identifier?.value || obs?.device?.display || '',

      // Value (para ocupacion)
      valor: obs?.valueCodeableConcept?.coding?.[0] ? {
        codigo: obs.valueCodeableConcept.coding[0].code || '',
        display: obs.valueCodeableConcept.coding[0].display || '',
        sistema: obs.valueCodeableConcept.coding[0].system || ''
      } : (obs?.valueString || obs?.valueQuantity?.value || null),

      // Components (para incapacidad, resultado-procedimiento)
      componentes: comps.map(c => {
        const codComp = c?.code?.coding?.[0];
        return {
          id: c?.id || '',
          codigo: codComp?.code || '',
          display: codComp?.display || '',
          texto: c?.code?.text || '',
          // value puede ser CodeableConcept, Quantity, String, etc.
          valor: c?.valueCodeableConcept?.coding?.[0]?.display
            || c?.valueString
            || (c?.valueQuantity ? `${c.valueQuantity.value} ${c.valueQuantity.unit}` : '')
            || ''
        };
      }),

      // Notas
      notas: Utils.safeArray(obs?.note).map(n => n?.text || '').join(' | ')
    };
  },

  extractRiskAssessmentData(risk) {
    if (!risk) return null;

    const codeCoding = risk?.code?.coding?.[0];

    return {
      id: risk?.id || '',
      status: risk?.status || '',

      // Código del factor de riesgo (FactorRiesgo)
      codigo: codeCoding ? {
        codigo: codeCoding.code || '',
        display: codeCoding.display || '',
        sistema: codeCoding.system || ''
      } : null,

      // Descripción libre
      codigoTexto: risk?.code?.text || '',

      // Referencias
      paciente: Utils.getText(risk?.subject?.reference) || '',
      encuentro: Utils.getText(risk?.encounter?.reference) || '',

      // Fecha de evaluación
      fechaEvaluacion: risk?.occurrenceDateTime || risk?.occurrencePeriod?.start || '',

      // Evaluador/Performer
      evaluador: Utils.getText(risk?.performer?.reference) || '',

      // Método de evaluación
      metodo: risk?.method?.coding?.[0]?.display || risk?.method?.text || '',

      // Basis: razón o fundamento
      basis: Utils.safeArray(risk?.basis).map(b => Utils.getText(b?.reference)).filter(Boolean),

      // Predicción (si existe)
      predicciones: Utils.safeArray(risk?.prediction).map(p => ({
        resultado: p?.outcome?.coding?.[0]?.display || p?.outcome?.text || '',
        probabilidad: p?.probabilityDecimal !== undefined
          ? `${(p.probabilityDecimal * 100).toFixed(1)}%`
          : (p?.probabilityRange
            ? `${p.probabilityRange.low?.value || '?'}-${p.probabilityRange.high?.value || '?'}`
            : ''),
        whenPeriod: p?.whenPeriod
          ? `${p.whenPeriod.start || ''} a ${p.whenPeriod.end || ''}`
          : (p?.whenRange ? `${p.whenRange.low?.value || ''}-${p.whenRange.high?.value || ''}` : ''),
        razon: p?.rationale || ''
      })),

      // Mitigación (recomendaciones)
      mitigacion: risk?.mitigation || '',

      // Notas
      notas: Utils.safeArray(risk?.note).map(n => n?.text || '').join(' | ')
    };
  },

  extractServiceRequestData(svc) {
    if (!svc) return null;

    const catCoding = svc?.category?.[0]?.coding?.[0];
    const codeCoding = svc?.code?.coding?.[0];

    return {
      id: svc?.id || '',
      status: svc?.status || '',
      intent: svc?.intent || '',

      // Categoría (ColombianHealthTechnologyCategory)
      categoria: catCoding ? {
        codigo: catCoding.code || '',
        display: catCoding.display || ''
      } : null,

      // Código del servicio (CUPS)
      codigo: codeCoding ? {
        codigo: codeCoding.code || '',
        display: codeCoding.display || '',
        sistema: codeCoding.system || ''
      } : null,

      // Descripción libre
      codigoTexto: svc?.code?.text || '',

      // Referencias
      paciente: Utils.getText(svc?.subject?.reference) || '',
      encuentro: Utils.getText(svc?.encounter?.reference) || '',
      solicitante: Utils.getText(svc?.requester?.reference) || '',

      // Fecha de solicitud
      fechaSolicitud: svc?.authoredOn || '',

      // Fecha deseada (occurrence)
      fechaDeseada: svc?.occurrenceDateTime
        || svc?.occurrencePeriod?.start
        || svc?.occurrenceTiming?.event?.[0]
        || '',

      // Prioridad
      prioridad: svc?.priority || '',

      // Motivo (reasonCode)
      motivos: Utils.safeArray(svc?.reasonCode).map(rc => ({
        codigo: rc?.coding?.[0]?.code || '',
        display: rc?.coding?.[0]?.display || '',
        text: rc?.text || ''
      })),

      // Referencias a razones (conditions, observations)
      razonesReferencia: Utils.safeArray(svc?.reasonReference).map(rr =>
        Utils.getText(rr?.reference)
      ).filter(Boolean),

      // Instrucciones al paciente
      instruccionesPaciente: Utils.safeArray(svc?.patientInstruction).map(pi => pi?.text || '').join(' | '),

      // Especímenes requeridos
      especimenes: Utils.safeArray(svc?.specimen).map(s => Utils.getText(s?.reference)).filter(Boolean),

      // Body site
      bodySite: Utils.safeArray(svc?.bodySite).map(bs =>
        bs?.coding?.[0]?.display || bs?.text || ''
      ),

      // Notas
      notas: Utils.safeArray(svc?.note).map(n => n?.text || '').join(' | ')
    };
  },

  extractDocumentReferenceData(doc) {
    if (!doc) return null;

    const typeCoding = doc?.type?.coding?.[0];
    const catCoding = doc?.category?.[0]?.coding?.[0];

    return {
      id: doc?.id || '',
      status: doc?.status || '',

      // Tipo de documento (LOINC / ColombianDocumentTypes)
      tipo: typeCoding ? {
        codigo: typeCoding.code || '',
        display: typeCoding.display || '',
        sistema: typeCoding.system || ''
      } : null,

      // Tipos adicionales (puede haber múltiples)
      tiposAdicionales: Utils.safeArray(doc?.type?.coding).slice(1).map(c => ({
        codigo: c?.code || '',
        display: c?.display || ''
      })),

      // Categoría
      categoria: catCoding ? {
        codigo: catCoding.code || '',
        display: catCoding.display || ''
      } : null,

      // Referencias
      paciente: Utils.getText(doc?.subject?.reference) || '',
      custodio: Utils.getText(doc?.custodian?.reference) || '',

      // Fecha del documento
      fecha: doc?.date || '',

      // Autores
      autores: Utils.safeArray(doc?.author).map(a =>
        Utils.getText(a?.reference)
      ).filter(Boolean),

      // Descripción
      descripcion: doc?.description || '',

      // Security Label (confidencialidad)
      confidencialidad: Utils.safeArray(doc?.securityLabel).map(sl =>
        sl?.coding?.[0]?.display || sl?.coding?.[0]?.code || ''
      ).filter(Boolean),

      // Content (adjuntos)
      contenidos: Utils.safeArray(doc?.content).map(c => {
        const att = c?.attachment;
        return {
          contentType: att?.contentType || '',
          formato: c?.format?.display || c?.format?.code || '',
          url: att?.url || '',
          titulo: att?.title || '',
          // Tamaño del adjunto
          tamanio: att?.size || null,
          // Hash del contenido
          hash: att?.hash || '',
          // Idioma
          idioma: att?.language || '',
          // Indica si tiene data base64
          tieneData: !!att?.data
        };
      }),

      // Context (encuentros relacionados)
      encuentros: Utils.safeArray(doc?.context?.encounter).map(e =>
        Utils.getText(e?.reference)
      ).filter(Boolean),

      // Period del contexto
      periodoContexto: doc?.context?.period ? {
        inicio: doc.context.period.start || '',
        fin: doc.context.period.end || ''
      } : null
    };
  }
};

// ============================================================================
// 8. SERVICIOS DE ORGANIZACIÓN Y PROFESIONALES
// ============================================================================

const PractitionerService = {
  extractFromComposition(resultado, composition) {
    const refs = FHIRUtils.ensureRefArrays(resultado?.referencedResources);
    const practSet = new Set();

    const addRefIfPractitioner = (refStr) => {
      const r = Utils.getText(refStr);
      if (r && r.includes('/')) {
        const [type, id] = r.split('/');
        if (type === 'Practitioner' && id) practSet.add(id);
      }
    };

    // 1) Referencias directas en Composition
    Utils.safeArray(composition?.author).forEach(a => addRefIfPractitioner(a?.reference));
    Utils.safeArray(composition?.attester).forEach(a => addRefIfPractitioner(a?.party?.reference));

    // 2) Referencias en sections
    const sectionIds = FHIRUtils.collectSectionReferences(composition);

    // 3) Buscar en recursos relacionados
    const pool = [
      ...refs.encounters,
      ...refs.procedures,
      ...refs.medicationAdministrations,
      ...refs.medicationStatements,
      ...refs.medicationRequests,
      ...refs.allergyIntolerances,
      ...refs.conditions
    ].filter(r => r && sectionIds.has(r.id));

    // 4) Expandir referencias
    pool.forEach(r => {
      // Practitioner directo
      addRefIfPractitioner(r?.recorder?.reference);
      addRefIfPractitioner(r?.asserter?.reference);

      // Performers
      Utils.safeArray(r?.performer).forEach(p => {
        addRefIfPractitioner(p?.actor?.reference);
        addRefIfPractitioner(p?.reference);
      });

      // Participants en Encounter
      Utils.safeArray(r?.participant).forEach(p => {
        const refStr = p?.individual?.reference;
        addRefIfPractitioner(refStr);

        // Si es PractitionerRole, obtener el practitioner de ahí
        if (refStr && refStr.includes('/')) {
          const [t, id] = refStr.split('/');
          if (t === 'PractitionerRole') {
            const pr = Utils.safeArray(refs.practitionerRoles).find(x => x?.id === id);
            if (pr) addRefIfPractitioner(pr?.practitioner?.reference);
          }
        }
      });

      // Requester
      addRefIfPractitioner(r?.requester?.reference);
    });

    // 5) Mapear IDs a recursos reales
    const practitioners = refs.practitioners.filter(p => p && practSet.has(p.id));

    return practitioners;
  },

  // Buscar practitioner por ID en los recursos referenciados
  findById(resultado, practitionerId) {
    if (!practitionerId) return null;
    const refs = FHIRUtils.ensureRefArrays(resultado?.referencedResources);
    return refs.practitioners.find(p => p?.id === practitionerId) || null;
  },

  // Buscar practitioners relacionados a un encounter
  findByEncounter(resultado, encounter) {
    if (!encounter) return [];
    const practIds = new Set();

    Utils.safeArray(encounter?.participant).forEach(p => {
      const refStr = p?.individual?.reference;
      if (refStr && refStr.includes('/')) {
        const [type, id] = refStr.split('/');
        if (type === 'Practitioner' && id) {
          practIds.add(id);
        } else if (type === 'PractitionerRole' && id) {
          const refs = FHIRUtils.ensureRefArrays(resultado?.referencedResources);
          const pr = refs.practitionerRoles.find(x => x?.id === id);
          if (pr?.practitioner?.reference) {
            const [, practId] = pr.practitioner.reference.split('/');
            if (practId) practIds.add(practId);
          }
        }
      }
    });

    const refs = FHIRUtils.ensureRefArrays(resultado?.referencedResources);
    return refs.practitioners.filter(p => p && practIds.has(p.id));
  }
};

const OrganizationService = {
  isIPS(org) {
    const profiles = Utils.safeArray(org?.meta?.profile).join('|').toLowerCase();
    const hasCareDeliveryProfile = profiles.includes('caredeliveryorganizationrda');

    const hasCodigoPrestador = Utils.safeArray(org?.identifier).some(id =>
      Utils.safeArray(id?.type?.coding).some(c =>
        String(c?.code || '').toUpperCase() === 'CODIGOPRESTADOR' ||
        Utils.toText(c?.display).toLowerCase().includes('habilitación') ||
        Utils.toText(c?.display).toLowerCase().includes('habilitacion')
      ) || String(id?.system || '').toUpperCase().includes('REPS')
    );

    const tipoOrgDisp = Utils.toText(org?.type?.[0]?.coding?.[0]?.display).toLowerCase();
    const tipoSugiereIPS =
      tipoOrgDisp.includes('institución prestadora') ||
      tipoOrgDisp.includes('prestador') ||
      tipoOrgDisp.includes('Organizacion prestadora de salud');

    return hasCareDeliveryProfile || hasCodigoPrestador || tipoSugiereIPS;
  },

  isEPS(org) {
    const profiles = Utils.safeArray(org?.meta?.profile).join('|').toLowerCase();
    const hasHBPAProfile = profiles.includes('healthbenefitplanadminorganizationrda');

    const tipoOrgDisp = Utils.toText(org?.type?.[0]?.coding?.[0]?.display).toLowerCase();
    const tipoSugiereEPS =
      tipoOrgDisp.includes('asegurador') ||
      tipoOrgDisp.includes('entidad administradora') ||
      tipoOrgDisp.includes('eps') ||
      tipoOrgDisp.includes('plan');

    return hasHBPAProfile || tipoSugiereEPS;
  },

  getRol(org) {
    if (this.isIPS(org) && !this.isEPS(org)) return ROLES_ORG.IPS;
    if (this.isEPS(org) && !this.isIPS(org)) return ROLES_ORG.EPS;

    const tienePrestador = Utils.safeArray(org?.identifier).some(id =>
      Utils.safeArray(id?.type?.coding).some(c =>
        String(c?.code || '').toUpperCase() === 'CODIGOPRESTADOR'
      )
    );

    return tienePrestador ? ROLES_ORG.IPS : ROLES_ORG.GENERIC;
  },

  getNIT(org) {
    const idNit = Utils.safeArray(org?.identifier).find(id =>
      Utils.safeArray(id?.type?.coding).some(c =>
        String(c?.system || '').includes('ColombianOrganizationIdentifiers') &&
        (String(c?.code || '').toUpperCase() === 'NIT' ||
          Utils.toText(c?.display).toUpperCase().includes('NIT'))
      )
    ) || Utils.safeArray(org?.identifier).find(id =>
      Utils.safeArray(id?.type?.coding).some(c =>
        String(c?.code || '').toUpperCase() === 'TAX'
      )
    );

    return Utils.toText(idNit?.value);
  },

  getCodigoPrestador(org) {
    const idPrestador = Utils.safeArray(org?.identifier).find(id =>
      Utils.safeArray(id?.type?.coding).some(c =>
        String(c?.system || '').includes('ColombianOrganizationIdentifiers') &&
        (String(c?.code || '').toUpperCase() === 'CODIGOPRESTADOR' ||
          Utils.toText(c?.display).toLowerCase().includes('código de habilitación') ||
          Utils.toText(c?.display).toLowerCase().includes('codigo de habilitacion'))
      )
    ) || Utils.safeArray(org?.identifier).find(id =>
      Utils.safeArray(id?.type?.coding).some(c =>
        String(c?.code || '').toUpperCase() === 'PRN'
      ) &&
      String(id?.system || '').toUpperCase().includes('REPS')
    );

    return Utils.toText(idPrestador?.value);
  },

  collectFromComposition(resultado, composition) {
    const refs = FHIRUtils.ensureRefArrays(resultado?.referencedResources);
    const orgSet = new Set();

    const addRefIfOrg = (refStr) => {
      const r = Utils.getText(refStr);
      if (r && r.includes('/')) {
        const [type, id] = r.split('/');
        if (type === 'Organization' && id) orgSet.add(id);
      }
    };

    // Referencias directas en Composition
    addRefIfOrg(composition?.custodian?.reference);
    Utils.safeArray(composition?.attester).forEach(a => addRefIfOrg(a?.party?.reference));
    Utils.safeArray(composition?.author).forEach(a => addRefIfOrg(a?.reference));

    Utils.safeArray(composition?.section).forEach(sec => {
      Utils.safeArray(sec?.entry).forEach(e => addRefIfOrg(e?.reference));
    });

    // IDs referenciados en secciones
    const sectionIds = FHIRUtils.collectSectionReferences(composition);

    // Recursos relacionados
    const pool = [
      ...refs.encounters,
      ...refs.conditions,
      ...refs.procedures,
      ...refs.medicationAdministrations,
      ...refs.medicationStatements,
      ...refs.medicationRequests,
      ...refs.familyMemberHistories,
      ...refs.allergyIntolerances
    ].filter(r => r && sectionIds.has(r.id));

    // Expandir referencias a organizaciones
    pool.forEach(r => {
      addRefIfOrg(r?.serviceProvider?.reference);
      Utils.safeArray(r?.coverage).forEach(cv => addRefIfOrg(cv?.coverage?.reference));
      addRefIfOrg(r?.insurer?.reference);
      addRefIfOrg(r?.owner?.reference);

      if (r?.resourceType === 'PractitionerRole') {
        addRefIfOrg(r?.organization?.reference);
      }

      Utils.safeArray(r?.participant).forEach(p => {
        const refStr = p?.individual?.reference;
        if (refStr && refStr.includes('/')) {
          const [t, id] = refStr.split('/');
          if (t === 'PractitionerRole') {
            const pr = Utils.safeArray(refs.practitionerRoles).find(x => x?.id === id);
            if (pr) addRefIfOrg(pr?.organization?.reference);
          }
        }
      });

      addRefIfOrg(r?.provider?.reference);
      addRefIfOrg(r?.requester?.reference);
      addRefIfOrg(r?.performer?.organization?.reference);
      Utils.safeArray(r?.payor).forEach(p => addRefIfOrg(p?.reference));
    });

    return refs.organizations.filter(o => o && orgSet.has(o.id));
  }
};

// ============================================================================
// 9. RENDERIZADORES UI
// ============================================================================

const UIRenderers = {
  renderPatient(infoPatient) {
    const container = DOM.elements.containerPatient;
    if (!container) {
      console.error('No se encontró containerPatient');
      return;
    }

    if (!infoPatient) {
      container.innerHTML = '<p>No se encontró información del paciente.</p>';
      return;
    }

    const nombres = Utils.safeArray(infoPatient.name?.[0]?.given).join(' ');
    const apellidos = Utils.getText(infoPatient.name?.[0]?.family);
    const nombreCompleto = `${nombres} ${apellidos}`.trim();

    const identificador =
      infoPatient.identifier?.find((id) =>
        Utils.safeArray(id.type?.coding).some((c) =>
          c?.code === 'CC' || Utils.getText(c?.display).includes('dula')
        )
      )?.value || infoPatient.identifier?.[0]?.value || '';

    const fechaNacimiento = Utils.getText(infoPatient.birthDate);

    container.innerHTML = `
      <table class="table table-bordered table-striped">
        <thead>
          <tr>
            <th scope="col">Paciente</th>
            <th scope="col">No. de Documento</th>
            <th scope="col">Fecha de Nacimiento</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>${nombreCompleto || 'Sin nombre'}</td>
            <td>${identificador || 'Sin documento'}</td>
            <td>${fechaNacimiento || 'Sin fecha'}</td>
          </tr>
        </tbody>
      </table>
    `;
  },

  renderDocumentos(entries) {
    const contenedor = DOM.elements.contenedor;
    if (!contenedor) {
      console.error('❌ No se encontró contenedor');
      return;
    }

    contenedor.innerHTML = '';

    const entriesPaciente = Utils.safeArray(entries).filter((e) => !e?.resource?.encounter);

    console.log(`📊 Mostrando ${entriesPaciente.length} documentos de RDA Paciente`);

    if (entriesPaciente.length > 0) {
      DOM.setBadgeCount('contadorDocumentos', entriesPaciente.length);

      const tabla = document.createElement('table');
      tabla.className = 'table table-bordered table-striped';
      tabla.innerHTML = `
        <thead class="table-dark">
          <tr>
            <th>#</th>
            <th>Formato</th>
            <th>Región</th>
            <th>Autor</th>
            <th>Fecha Bundle</th>
          </tr>
        </thead>
        <tbody></tbody>
      `;

      const cuerpo = tabla.querySelector('tbody');

      entriesPaciente.forEach((entry, index) => {
        const r = entry.resource;
        const org = AppState.datosGlobalesFHIR ?
          FHIRUtils.obtenerOrganizacionDelRDA(r, AppState.datosGlobalesFHIR) : null;

        const tr = document.createElement('tr');
        tr.style.cursor = 'pointer';
        tr.innerHTML = `
          <td>${index + 1}</td>
          <td>${r?.title || 'RDA'}</td>
          <td>${FHIRUtils.getRegionFromOrg(org)}</td>
          <td>${org?.name || 'N/A'}</td>
          <td>${r?.date || r?.meta?.lastUpdated || 'Sin fecha'}</td>
        `;
        tr.addEventListener('click', () => ModalService.mostrarDetalle(entry));
        cuerpo.appendChild(tr);
      });

      contenedor.appendChild(tabla);
    } else {
      DOM.setBadgeCount('contadorDocumentos', 0);
      const msg = document.createElement('div');
      msg.className = 'alert alert-info';
      msg.innerHTML = '<i class="bx bx-info-circle"></i> No se encontraron documentos de RDA Paciente para este paciente.';
      contenedor.appendChild(msg);
    }
  },

  renderEncuentros(entries) {
    const cont = DOM.elements.contenedorEncuentros;
    const count = DOM.elements.contadorEncuentros;

    if (!cont) return;

    cont.innerHTML = '';

    if (!Array.isArray(entries) || entries.length === 0) return;

    const refs = FHIRUtils.ensureRefArrays(AppState.datosGlobalesFHIR?.referencedResources || {});

    const tabla = document.createElement('table');
    tabla.className = 'table table-bordered table-striped';
    tabla.innerHTML = `
      <thead class="table-dark">
        <tr>
          <th>#</th><th>Formato</th><th>Región</th><th>Autor</th>
          <th>Fecha</th><th>Epicrisis / PDF</th><th>Origen</th>
        </tr>
      </thead>
      <tbody></tbody>
    `;

    const tbody = tabla.querySelector('tbody');

    entries.forEach((entry, index) => {
      const r = entry?.resource;
      if (!r) return;

      const compositionId = r.id;

      let urlPDF = '';

      // === BÚSQUEDA POR REFERENCIA DENTRO DE LA COMPOSITION ===
      const docRef = refs.documentReferences.find(doc => {
        const url = Utils.getText(doc?.content?.[0]?.attachment?.url);
        if (!url) return false;

        // 1. Referencia directa en section.entry de la Composition
        const hasDirectRef = Utils.safeArray(r.section).some(section =>
          Utils.safeArray(section?.entry).some(e => {
            const ref = Utils.getText(e?.reference);
            return ref.includes(doc.id) || doc.id.includes(ref);
          })
        );

        // 2. Match por Encounter (por si acaso)
        const docEncounters = Utils.safeArray(doc?.context?.encounter);
        const matchEncounter = docEncounters.some(enc => {
          const dRef = Utils.getText(enc?.reference);
          const eRef = Utils.getText(r?.encounter?.reference);
          return dRef && eRef && (dRef.includes(eRef) || eRef.includes(dRef));
        });

        return hasDirectRef || matchEncounter || doc.id === compositionId;
      });

      if (docRef) {
        urlPDF = Utils.getText(docRef?.content?.[0]?.attachment?.url);
      }

      let botonPDF = '<span class="text-muted small">—</span>';

      if (urlPDF) {
        const esFHIR = urlPDF.includes('/DocumentReference/') || urlPDF.includes('/descargar-rda');

        if (esFHIR) {
          botonPDF = `
            <button class="btn btn-sm btn-danger"
                    onclick="event.stopPropagation(); PDFViewerService.mostrar('${urlPDF.replace(/'/g, "\\'")}')"
                    title="Ver PDF">
              <i class="bx bxs-file-pdf"></i>
            </button>`;
        }
      }

      const org = AppState.datosGlobalesFHIR ?
        FHIRUtils.obtenerOrganizacionDelRDA(r, AppState.datosGlobalesFHIR) : null;

      const tr = document.createElement('tr');
      tr.style.cursor = 'pointer';
      tr.innerHTML = `
        <td>${index + 1}</td>
        <td>${r.title || 'RDA'}</td>
        <td>${FHIRUtils.getRegionFromOrg(org)}</td>
        <td>${org?.name || 'N/A'}</td>
        <td>${r.date || r.meta?.lastUpdated || 'Sin fecha'}</td>
        <td>${botonPDF}</td>
        <td><span class="badge bg-success">Encuentros</span></td>
      `;

      tr.addEventListener('click', () => ModalService.mostrarDetalle(entry));
      tbody.appendChild(tr);
    });

    cont.appendChild(tabla);
  },

  renderPaginacion(bundle) {
    const cont = DOM.elements.paginacion;
    if (!cont) {
      console.warn('⚠️ No se encontró contenedor de paginación');
      return;
    }

    cont.innerHTML = '';
    const links = Utils.safeArray(bundle?.link);

    if (links.length === 0) {
     
      return;
    }


    links.forEach((link) => {
      const btn = document.createElement('button');
      btn.className = 'btn btn-outline-primary m-1';
      btn.textContent = ETIQUETAS_PAGINACION[link.relation] || link.relation;

      if (link.relation === 'self') {
        btn.classList.add('active');
        btn.disabled = true;
      }

      btn.addEventListener('click', async () => {
        await PaginationService.cargarPagina(btn, link.url);
      });

      cont.appendChild(btn);
    });
  }
};

// ============================================================================
// 10. SERVICIO DE INMUNIZACIONES
// ============================================================================

const InmunizacionService = {
  getContainer() {
    let cont = DOM.byId('accordion-body-certificado') || DOM.byId('contenedorInmunizaciones');
    if (!cont) {
      cont = document.createElement('div');
      cont.id = 'accordion-body-certificado';
      document.body.appendChild(cont);
    }

    let tabla = DOM.byId('tablaInmunizaciones');
    if (!tabla) {
      tabla = document.createElement('table');
      tabla.id = 'tablaInmunizaciones';
      tabla.className = 'table table-striped table-sm';
      cont.appendChild(tabla);
    }

    return { cont, tabla };
  },

  pintarMensaje(htmlMsg, tipo = 'muted') {
    const { tabla } = this.getContainer();
    tabla.innerHTML = `${TABLA_INMUNIZACIONES_HEADER}
      <tbody><tr><td colspan="7" class="text-center text-${tipo}">${htmlMsg}</td></tr></tbody>`;
  },

  renderTabla(items) {
    const { tabla } = this.getContainer();

    if (!items || items.length === 0) {
      this.pintarMensaje('<i class="bx bx-info-circle"></i> Sin resultados');
      DOM.setBadgeCount('contadorVacunas', 0, { hideWhenZero: true });
      return;
    }

    const rows = items.map((it, i) => `
      <tr>
        <td>${i + 1}</td>
        <td>${Utils.escapeHtml(it.vacuna || '')}</td>
        <td>${Utils.escapeHtml(Utils.formatearFecha(it.fecha || ''))}</td>
        <td>${Utils.escapeHtml(it.lote || '')}</td>
        <td>${Utils.escapeHtml(it.prestador || '')}</td>
        <td>${Utils.escapeHtml(it.fabricante || '')}</td>
        <td>${Utils.escapeHtml(it.estado || '')}</td>
      </tr>
    `).join('');

    tabla.innerHTML = `${TABLA_INMUNIZACIONES_HEADER}<tbody>${rows}</tbody>`;
    DOM.setBadgeCount('contadorVacunas', items.length, { hideWhenZero: true });
  },

  limpiar() {
    AppState.ultimoBundleInmunizacion = null;
    this.pintarMensaje('<i class="bx bx-info-circle"></i> Sin datos de inmunización');
    DOM.setBadgeCount('contadorVacunas', 0, { hideWhenZero: true });
  },

  async consultar() {
    const elements = DOM.elements;
    const tipo = Utils.getText(elements.inputTipoDocumento?.value).trim();
    const num = Utils.getText(elements.inputDocumento?.value).trim();

    if (!tipo || !num) return;

    this.pintarMensaje('<i class="bx bx-loader-alt bx-spin"></i> Consultando inmunizaciones…');

    try {
      const bundle = await APIService.consultarInmunizacion(tipo, num);
      AppState.ultimoBundleInmunizacion = bundle;

      const inmunizaciones = DataMappers.extractInmunizaciones(bundle);
   

      if (!inmunizaciones || inmunizaciones.length === 0) {
        this.pintarMensaje(
          '<i class="bx bx-info-circle"></i> No se encontraron registros de vacunación para este paciente.',
          'info'
        );
        DOM.setBadgeCount('contadorVacunas', 0, { hideWhenZero: true });
        return;
      }

      this.renderTabla(inmunizaciones);

    } catch (e) {
      console.error('Error en consulta de inmunización:', e);
      this.pintarMensaje(
        `<i class="bx bx-error-circle"></i> ${Utils.escapeHtml(e?.message || 'Error consultando inmunizaciones')}`,
        'danger'
      );
      DOM.setBadgeCount('contadorVacunas', 0, { hideWhenZero: true });
    }
  }
};

// ============================================================================
// 11. SERVICIO DE PAGINACIÓN
// ============================================================================

const PaginationService = {
  async cargarPagina(boton, url) {
    try {
      boton.disabled = true;
      const txt = boton.innerHTML;
      boton.innerHTML = '<i class="bx bx-loader-alt bx-spin"></i>';

      const numeroDocumento = DOM.elements.inputDocumento?.value || 'session';
      const newBundle = await APIService.cargarPagina(url, numeroDocumento);

      // Fusionar recursos referenciados
      if (AppState.datosGlobalesFHIR?.referencedResources && newBundle?.referencedResources) {
        const merged = FHIRUtils.mergeReferencedResources(
          AppState.datosGlobalesFHIR.referencedResources,
          newBundle.referencedResources
        );
        AppState.datosGlobalesFHIR.referencedResources = merged;
      }

      const todos = Utils.safeArray(newBundle?.entry);

      AppState.datosGlobalesFHIR = {
        ...newBundle,
        entry: todos,
        referencedResources: FHIRUtils.ensureRefArrays(
          AppState.datosGlobalesFHIR?.referencedResources
        )
      };

      UIRenderers.renderDocumentos(todos);
      UIRenderers.renderPaginacion(newBundle);

      boton.innerHTML = txt;
      boton.disabled = false;
    } catch (error) {
      console.error('❌ Error cargando página:', error);
      alert('No se pudo cargar la página');
      boton.disabled = false;
    }
  }
};

// ============================================================================
// 12. SERVICIO DE MODAL
// ============================================================================

const ModalService = {
  mostrarDetalle(entry) {
    if (!AppState.datosGlobalesFHIR) {
      console.error('❌ No hay datos globales disponibles');
      return;
    }

    const r = entry.resource;
    const resultado = AppState.datosGlobalesFHIR;

    // Datos principales
    const organizations = OrganizationService.collectFromComposition(resultado, r);
    const orgRDA = FHIRUtils.obtenerOrganizacionDelRDA(r, resultado);
    const organizationsToShow = organizations.length ? organizations : (orgRDA ? [orgRDA] : []);

    // Practitioners
    const practitioners = PractitionerService.extractFromComposition(resultado, r);

    // Encounter
    let encounterData = null;
    const encounterRef = Utils.getText(r?.encounter?.reference);
    if (encounterRef && resultado?.referencedResources?.encounters) {
      const [tipoEnc, idEnc] = encounterRef.split('/');
      encounterData = resultado.referencedResources.encounters.find(
        (e) => e?.resourceType === tipoEnc && e?.id === idEnc
      );
    }
    const enc = encounterData ? DataMappers.mapEncounter(encounterData) : null;

    // Si hay encounter, agregar practitioners de ese encounter
    if (encounterData) {
      const encounterPractitioners = PractitionerService.findByEncounter(resultado, encounterData);
      // Fusionar sin duplicados
      const practIds = new Set(practitioners.map(p => p.id));
      encounterPractitioners.forEach(p => {
        if (!practIds.has(p.id)) {
          practitioners.push(p);
          practIds.add(p.id);
        }
      });
    }

    // Patient
    let patient = null;
    if (Utils.getText(r?.subject?.reference).includes('/')) {
      const [tipoPat, idPat] = r.subject.reference.split('/');
      patient = resultado?.referencedResources?.patients?.find(
        (p) => p?.resourceType === tipoPat && p?.id === idPat
      );
    }
    const patientData = DataMappers.extractPatientData(patient);

    // Recursos relacionados
    const refsIds = FHIRUtils.collectSectionReferences(r);
    const refs = FHIRUtils.ensureRefArrays(resultado?.referencedResources);

    const resources = {
      procedures: refs.procedures.filter((P) => refsIds.has(P.id)),
      medicationAdministrations: refs.medicationAdministrations.filter((M) => refsIds.has(M.id)),
      medicationRequests: refs.medicationRequests.filter((MR) => refsIds.has(MR.id)),
      antecedentes: refs.familyMemberHistories.filter((A) => refsIds.has(A.id)),
      medicationStatements: refs.medicationStatements.filter((MS) => refsIds.has(MS.id)),
      allergyIntolerances: refs.allergyIntolerances.filter((AL) => refsIds.has(AL.id)),
      conditions: refs.conditions.filter((C) => refsIds.has(C.id)),
      locations: refs.locations.filter((L) => refsIds.has(L.id)),
      // Filtrar observations y riskAssessments:
      // 1) Por encounter.reference si existe
      // 2) Si no, por si están en refsIds (sections)
      // Reemplaza las líneas de observations y agrega clarificationNotes:
      observations: refs.observations.filter(o => {
        if (o?.meta?.profile?.some(p => p.includes('ObservationClarificationNoteRDA'))) return false;
        const oEncRef = Utils.getText(o?.encounter?.reference);
        if (oEncRef) {
          const normalizedOEnc = oEncRef.replace(/^#/, '').replace(/^.*\//, '');
          const normalizedCurrentEnc = encounterRef.replace(/^#/, '').replace(/^.*\//, '');
          return normalizedOEnc === normalizedCurrentEnc;
        }
        return refsIds.has(o.id);
      }),
      clarificationNotes: refs.observations.filter(o => {
        if (!o?.meta?.profile?.some(p => p.includes('ObservationClarificationNoteRDA'))) return false;
        const oEncRef = Utils.getText(o?.encounter?.reference);
        if (oEncRef) {
          const normalizedOEnc = oEncRef.replace(/^#/, '').replace(/^.*\//, '');
          const normalizedCurrentEnc = encounterRef.replace(/^#/, '').replace(/^.*\//, '');
          return normalizedOEnc === normalizedCurrentEnc;
        }
        return refsIds.has(o.id);
      }),
      riskAssessments: refs.riskAssessments.filter(r => {
        // Si tiene encounter.reference, verificar que coincida
        const rEncRef = Utils.getText(r?.encounter?.reference);
        if (rEncRef) {
          // Normalizar referencias: eliminar # y /
          const normalizedREnc = rEncRef.replace(/^#/, '').replace(/^.*\//, '');
          const normalizedCurrentEnc = encounterRef.replace(/^#/, '').replace(/^.*\//, '');
          return normalizedREnc === normalizedCurrentEnc;
        }
        // Si no tiene encounter, verificar si está en sections
        return refsIds.has(r.id);
      }),
      serviceRequests: refs.serviceRequests.filter(s => {
        // Filtrar por encounter igual que observations y riskAssessments
        const sEncRef = Utils.getText(s?.encounter?.reference);
        if (sEncRef) {
          const normalizedSEnc = sEncRef.replace(/^#/, '').replace(/^.*\//, '');
          const normalizedCurrentEnc = encounterRef.replace(/^#/, '').replace(/^.*\//, '');
          return normalizedSEnc === normalizedCurrentEnc;
        }
        return refsIds.has(s.id);
      }),
      documentReferences: refs.documentReferences.filter(d => {
        // Filtrar por encuentros en context.encounter[]
        const encounters = Utils.safeArray(d?.context?.encounter);
        if (encounters.length > 0) {
          return encounters.some(e => {
            const dEncRef = Utils.getText(e?.reference);
            if (dEncRef) {
              const normalizedDEnc = dEncRef.replace(/^#/, '').replace(/^.*\//, '');
              const normalizedCurrentEnc = encounterRef.replace(/^#/, '').replace(/^.*\//, '');
              return normalizedDEnc === normalizedCurrentEnc;
            }
            return false;
          });
        }
        return refsIds.has(d.id);
      }),
      practitioners: practitioners
    };

    // Construir HTML
    // Capturar secciones con emptyReason
    const seccionesVacias = Utils.safeArray(r?.section).filter(sec => sec?.emptyReason);

    const html = this.construirHTML(entry, enc, patientData, organizationsToShow, resources, seccionesVacias);

    // Mostrar modal
    const contModal = DOM.elements.modalContent;
    if (!contModal) {
      console.error('❌ No existe el contenedor del modal');
      return;
    }
    contModal.innerHTML = html;

    const modalEl = DOM.byId('exampleModal');
    if (!modalEl || !window.bootstrap?.Modal) {
      console.warn('⚠️ Bootstrap Modal no disponible');
      return;
    }
    const modal = new bootstrap.Modal(modalEl);
    modal.show();
  },

  construirHTML(entry, enc, patientData, organizations, resources, seccionesVacias = []) {
    let html = `
      <div class="alert alert-info d-flex align-items-center" role="alert">
        <i class="bx bx-info-circle me-2"></i>
        <div>
          <strong>Origen del documento:</strong>
          <span class="badge ${entry._source === 'rda-paciente' ? 'bg-primary' : 'bg-success'} ms-2">
            ${entry._source === 'rda-paciente' ? '👤' : '🏥'} ${entry._sourceLabel || 'No especificado'}
          </span>
        </div>
      </div>
    `;

    // Encounter
    if (enc) {
      html += this.renderEncounter(enc, organizations[0]);
    }

    // Patient
    if (patientData) {
      html += this.renderPatient(patientData);
    }

    // Organizations
    html += this.renderOrganizations(organizations);

    // Practitioners (nuevo)
    if (resources.practitioners && resources.practitioners.length > 0) {
      html += this.renderPractitioners(resources.practitioners);
    }

    // Recursos relacionados
    html += this.renderRelatedResources(resources);

    if (resources.clarificationNotes?.length > 0) {
      html += this.renderClarificationNotes(resources.clarificationNotes);
    }

    if (seccionesVacias.length > 0) {
      html += `
        <div class="card mb-3 shadow-sm">
          <div class="card-header bg-light text-dark border">
            <h4 class="mb-0"><i class="bx bx-info-circle"></i> Secciones sin información o que no se le declaran al paciente</h4>
          </div>
          <div class="card-body">
            <ul class="list-group list-group-flush">
              ${seccionesVacias.map(sec => {
        const titulo = sec?.title || 'Sección sin título';
        const razonDisplay = sec?.emptyReason?.coding?.[0]?.display || '';
        const razonCodigo = sec?.emptyReason?.coding?.[0]?.code || '';
        const textoDiv = sec?.text?.div
          ? sec.text.div.replace(/<[^>]+>/g, '').trim()
          : '';
        return `
                  <li class="list-group-item d-flex justify-content-between align-items-start" 
                      style="border-left: 6px solid #ffc107; background-color: #fffbea;">
                    <div>
                      <strong>${Utils.escapeHtml(titulo)}</strong>
                      ${textoDiv ? `<p class="mb-0 text-muted small mt-1">${Utils.escapeHtml(textoDiv)}</p>` : ''}
                    </div>
                    ${razonDisplay
            ? `<span class="badge bg-dark ms-2">${Utils.escapeHtml(razonDisplay)}${razonCodigo ? ` [${Utils.escapeHtml(razonCodigo)}]` : ''}</span>`
            : ''}
                  </li>
                `;
      }).join('')}
            </ul>
          </div>
        </div>
      `;



    }

    return html;
  },

  renderEncounter(enc, orgRDA) {
    if (!enc) return '';

    const statusBadge = (() => {
      const s = (enc.status || '').toLowerCase();
      if (s === 'finished') return ['bg-success', 'Finalizado'];
      if (s === 'in-progress') return ['bg-primary', 'En progreso'];
      if (s === 'planned') return ['bg-warning text-dark', 'Planificado'];
      if (s === 'cancelled') return ['bg-danger', 'Cancelado'];
      if (s === 'arrived') return ['bg-info', 'Llegada'];
      return enc.status ? ['bg-secondary', enc.status] : null;
    })();

    const claseBadge = (() => {
      const c = (enc.clase?.codigo || '').toUpperCase();
      if (c === 'IMP') return ['bg-danger', 'Hospitalización'];
      if (c === 'AMB') return ['bg-success', 'Ambulatorio'];
      if (c === 'EMER') return ['bg-warning text-dark', 'Urgencias'];
      if (c === 'VR') return ['bg-info', 'Virtual'];
      if (c === 'SS') return ['bg-secondary', 'Especialidad'];
      return enc.clase?.display ? ['bg-secondary', enc.clase.display] : null;
    })();

    const SISTEMA_LABELS = {
      'ColombianTechModality': 'Modalidad',
      'GrupoServicios': 'Grupo de Servicio',
      'EntornoAtencion': 'Entorno de Atención'
    };
    const tipoLabel = (s) => {
      for (const [k, v] of Object.entries(SISTEMA_LABELS)) {
        if ((s || '').includes(k)) return v;
      }
      return 'Tipo';
    };

    const ROL_LABELS = {
      'DIS': '🩺 Médico egresador',
      'ATND': '🩺 Médico tratante',
      'PART': '👤 Participante',
      'ADM': '🏥 Médico admisión',
      'CON': '🔬 Consultor',
      'REF': '📋 Médico remitente'
    };

    const TIPO_DX_BADGE = (codigo) => {
      if (codigo === '01') return 'bg-warning text-dark';
      if (codigo === '02') return 'bg-success';
      if (codigo === '03') return 'bg-info';
      return 'bg-secondary';
    };

    return `
      <div class="card mb-3 shadow-sm">
        <div class="card-header bg-primary text-white">
          <div class="d-flex justify-content-between align-items-center flex-wrap gap-1">
            <h4 class="mb-0"><i class="bx bx-clinic"></i> Encuentro Clínico</h4>
            <div class="d-flex gap-1 flex-wrap">
              ${claseBadge ? `<span class="badge ${claseBadge[0]}">${claseBadge[1]}</span>` : ''}
              ${statusBadge ? `<span class="badge ${statusBadge[0]}">${statusBadge[1]}</span>` : ''}
            </div>
          </div>
        </div>
        <div class="card-body">

          <div class="row g-3">
            <!-- Columna izquierda: identificacion y fechas -->
            <div class="col-12 col-md-6">

              ${enc.identificador ? `
                <p class="mb-2"><strong>N° Encuentro:</strong>
                  <code>${Utils.escapeHtml(enc.identificador)}</code>
                </p>` : ''}

              ${enc.periodo.inicio ? `
                <p class="mb-2"><strong>Fecha de Ingreso:</strong>
                  ${Utils.escapeHtml(Utils.formatearFecha(enc.periodo.inicio))}
                </p>` : ''}

              ${enc.periodo.fin ? `
                <p class="mb-2"><strong>Fecha de Egreso:</strong>
                  ${Utils.escapeHtml(Utils.formatearFecha(enc.periodo.fin))}
                </p>` : ''}

              ${enc.motivos.length > 0 ? `
                <p class="mb-2"><strong>Causa Externa / Motivo:</strong><br>
                  ${enc.motivos.map(m =>
      `<span class="badge bg-secondary me-1">
                      ${Utils.escapeHtml(m.display || m.text || m.codigo)}
                      ${m.codigo ? `[${Utils.escapeHtml(m.codigo)}]` : ''}
                    </span>`
    ).join('')}
                </p>` : ''}

              ${orgRDA?.name ? `
                <p class="mb-2"><strong>Prestador:</strong>
                  ${Utils.escapeHtml(orgRDA.name)}
                </p>` : ''}

            </div>

            <!-- Columna derecha: tipos y participantes -->
            <div class="col-12 col-md-6">

              ${enc.tipos.length > 0 ? enc.tipos.map(t => `
                <p class="mb-2"><strong>${tipoLabel(t.sistema)}:</strong>
                  ${Utils.escapeHtml(t.display)}
                
                </p>`).join('') : ''}

            </div>
          </div>

         
          <!-- Diagnosticos del encuentro -->
          ${enc.diagnosticos.length > 0 ? `
            <hr>
            <p class="mb-2 fw-bold">Diagnósticos del Encuentro:</p>
            <div class="table-responsive">
              <table class="table table-sm table-bordered align-middle mb-0">
                <thead class="table-light">
                  <tr>
                    <th>#</th>
                    <th>Rol diagnóstico</th>
                    <th>Tipo</th>
                   
                  </tr>
                </thead>
                <tbody>
                  ${enc.diagnosticos.map(d => `
                    <tr>
                      <td>${d.rank ?? '—'}</td>
                      <td>${Utils.escapeHtml(d.rol)}</td>
                      <td>
                        ${d.tipoDx
        ? `<span class="badge ${TIPO_DX_BADGE(d.tipoDxCodigo)}">
                              ${Utils.escapeHtml(d.tipoDx)}
                            </span>`
        : '—'}
                      </td>
                     
                    </tr>`).join('')}
                </tbody>
              </table>
            </div>` : ''}

          <!-- Hospitalizacion -->
          ${enc.hospitalizacion ? `
            <hr>
            <p class="mb-2 fw-bold">Hospitalización:</p>
            <div class="row g-2">
              ${enc.hospitalizacion.viaIngreso ? `
                <div class="col-12 col-sm-6 col-md-4">
                  <div class="border rounded p-2 h-100">
                    <p class="text-muted small mb-1">Vía de Ingreso</p>
                    <p class="mb-0 fw-bold small">
                      ${Utils.escapeHtml(enc.hospitalizacion.viaIngreso)}
                     
                    </p>
                  </div>
                </div>` : ''}

              ${enc.hospitalizacion.condicionEgreso ? `
                <div class="col-12 col-sm-6 col-md-4">
                  <div class="border rounded p-2 h-100">
                    <p class="text-muted small mb-1">Condición y Destino de Egreso</p>
                    <p class="mb-0 fw-bold small">
                      ${Utils.escapeHtml(enc.hospitalizacion.condicionEgreso)}
                    
                    </p>
                  </div>
                </div>` : ''}

              ${enc.hospitalizacion.estadoFallecimiento ? `
                <div class="col-12 col-sm-6 col-md-4">
                  <div class="border rounded p-2 h-100">
                    <p class="text-muted small mb-1">Estado al Egreso</p>
                    <p class="mb-0 fw-bold small">
                      ${Utils.escapeHtml(enc.hospitalizacion.estadoFallecimiento)}
                    </p>
                  </div>
                </div>` : ''}

              ${enc.hospitalizacion.destino ? `
                <div class="col-12 col-sm-6 col-md-4">
                  <div class="border rounded p-2 h-100">
                    <p class="text-muted small mb-1">Institución de Destino</p>
                    <p class="mb-0 fw-bold small">
                      <code>${Utils.escapeHtml(enc.hospitalizacion.destino)}</code>
                    </p>
                  </div>
                </div>` : ''}
            </div>` : ''}

        </div>
      </div>
    `;
  },

  renderPatient(data) {
    return `
      <div class="card mb-3 shadow-sm">
        <div class="card-header bg-success text-white">
          <h4 class="mb-0">Datos del Paciente</h4>
        </div>
        <div class="card-body">
          <div class="row g-3">
            <div class="col-12 col-md-6">
              <p><strong>Activo:</strong> ${data.activo ? 'Sí' : 'No'}</p>
              <p><strong>Fallecido:</strong> ${data.fallecido ? 'Sí' : 'No'}</p>
              <p><strong>Nombre:</strong> ${data.nombres || 'N/A'}</p>
              <p><strong>Documento:</strong> ${data.documento || 'N/A'}</p>
              <p><strong>Tipo de documento:</strong> ${data.tipoDocumento
        ? `${data.tipoDocumento.code}${data.tipoDocumento.display ? ' – ' + data.tipoDocumento.display : ''}`
        : 'N/A'
      }</p>
              <p><strong>Fecha de Nacimiento:</strong> ${data.fechaNacimiento || 'N/A'}</p>
              <p><strong>Hora de Nacimiento:</strong> ${data.horaNacimiento || 'N/A'}</p>
              <p><strong>Edad:</strong> ${data.edad !== '' ? data.edad + ' años' : 'N/A'}</p>
             
            </div>
            <div class="col-12 col-md-6">
              <p><strong>País de Residencia:</strong> ${data.paisResidencia || 'N/A'}</p>
              <p><strong>Municipio de Residencia:</strong> ${data.municipioResidencia || 'N/A'}</p>
              <p><strong>Sexo Biológico (extensión):</strong> ${data.sexoBiologicoExt || 'N/A'}</p>
              <p><strong>Identidad de Género:</strong> ${data.identidadGenero || 'N/A'}</p>
              <p><strong>Nacionalidad:</strong> ${data.nacionalidad || 'N/A'}</p>
              <p><strong>Zona de Residencia:</strong> ${data.zonaResidencia || 'N/A'}</p>
              <p><strong>DIVIPOLA Municipio:</strong> ${data.divipolaMunicipio || 'N/A'}</p>
              <p><strong>Código país:</strong> ${data.codigoPais || 'N/A'}</p>
              <p><strong>Etnia:</strong> ${data.etnia || 'N/A'}</p>
              <p><strong>Tipo Discapacidad:</strong> ${data.discapacidad || 'N/A'}</p>
            </div>
          </div>
        </div>
      </div>
    `;
  },

  renderOrganizations(organizations) {
    const totalOrgs = organizations.length;
    let html = '';

    organizations.forEach((org, idx) => {
      const rol = totalOrgs <= 1 ? 'Organizacion prestadora de salud' : OrganizationService.getRol(org);
      const headerClass = rol === 'EPS' ? 'bg-info' : 'bg-secondary';

      const nombreOrg = Utils.toText(org?.name);
      const nit = OrganizationService.getNIT(org);
      const codigoPrestador = OrganizationService.getCodigoPrestador(org);
      const tipoOrg = Utils.toText(org?.type?.[0]?.coding?.[0]?.display);

      html += `
        <div class="card mb-3 shadow-sm">
          <div class="card-header ${headerClass} text-white d-flex justify-content-between align-items-center">
            <h4 class="mb-0">${rol}${totalOrgs > 1 ? ` #${idx + 1}` : ''}</h4>
          </div>
          <div class="card-body">
            <div class="row g-3">
              <div class="col-12 col-md-6">
                ${Utils.field('Nombre', nombreOrg)}
                ${Utils.field('NIT', nit)}
                ${Utils.field('Código de habilitación (REPS)', codigoPrestador)}
                ${Utils.field('Tipo de organización', tipoOrg)}
              </div>
            </div>
          </div>
        </div>
      `;
    });

    return html;
  },

  renderPractitioners(practitioners) {
    if (!practitioners || practitioners.length === 0) return '';

    let html = '';

    practitioners.forEach((pract, idx) => {
      const data = DataMappers.extractPractitionerData(pract);
      if (!data) return;

      html += `
        <div class="card mb-3 shadow-sm">
          <div class="card-header bg-primary text-white">
            <h4 class="mb-0">
              <i class="bx bx-user-circle"></i> Profesional de la Salud${practitioners.length > 1 ? ` #${idx + 1}` : ''}
            </h4>
          </div>
          <div class="card-body">
            <div class="row g-3">
              <!-- Columna izquierda -->
              <div class="col-12 col-md-6">
                ${Utils.field('Nombre completo', data.nombreCompleto)}  
                ${Utils.field('Documento', data.documento)}
                ${data.tipoDocumento ? Utils.field('Tipo de documento',
        `${data.tipoDocumento.code}${data.tipoDocumento.display ? ' – ' + data.tipoDocumento.display : ''}`
      ) : ''}
                ${Utils.field('Estado', data.activo ? 'Activo' : 'Inactivo')}
              </div>
            </div>

            <!-- Cualificaciones profesionales -->
            ${data.qualification.length > 0 ? `
              <hr>
              <h5><i class="bx bx-medal"></i> Cualificaciones Profesionales</h5>
              <div class="table-responsive">
                <table class="table table-sm table-bordered">
                  <thead class="table-light">
                    <tr>
                      <th>#</th>
                      <th>Cualificación</th>
                      <th>Periodo</th>
                    </tr>
                  </thead>
                  <tbody>
                    ${data.qualification.map((q, i) => `
                      <tr>
                        <td>${i + 1}</td>
                        <td>${Utils.escapeHtml(q.display || 'N/A')}</td>
                        <td>
                          ${q.periodo.inicio || 'N/A'}
                          ${q.periodo.fin ? ' - ' + q.periodo.fin : ' - Actual'}
                        </td>
                      </tr>
                    `).join('')}
                  </tbody>
                </table>
              </div>
            ` : ''}
          </div>
        </div>
      `;
    });

    return html;
  },

  renderRelatedResources(resources) {
    let html = '';


    // Conditions (Diagnósticos) - NUEVA SECCIÓN MEJORADA
    if (resources.conditions && resources.conditions.length > 0) {
      html += `
        <div class="card mb-3 shadow-sm">
          <div class="card-header bg-warning text-dark">
            <h4 class="mb-0">
              <i class="bx bx-health"></i> Diagnósticos / Condiciones (${resources.conditions.length})
            </h4>
          </div>
          <div class="card-body">
      `;

      resources.conditions.forEach((condition, idx) => {
        const data = DataMappers.extractConditionData(condition);
        if (!data) return;

        // Determinar color del badge según estado clínico
        const getBadgeClass = (estado) => {
          const estadoLower = (estado || '').toLowerCase();
          if (estadoLower.includes('active') || estadoLower.includes('activo')) return 'bg-danger';
          if (estadoLower.includes('resolved') || estadoLower.includes('resuelto')) return 'bg-success';
          if (estadoLower.includes('inactive') || estadoLower.includes('inactivo')) return 'bg-secondary';
          return 'bg-info';
        };

        const badgeClass = getBadgeClass(data.estadoClinico.display);

        html += `
          ${idx > 0 ? '<hr>' : ''}
          <div class="condition-item ${idx > 0 ? 'mt-3' : ''}">
            <div class="d-flex justify-content-between align-items-start mb-2">
              <h5 class="mb-0">
                <span class="badge bg-secondary me-2">#${idx + 1}</span>
                ${Utils.escapeHtml(data.codigo.display || data.codigo.text || 'Sin descripción')}
              </h5>
              <span class="badge ${badgeClass}">${Utils.escapeHtml(data.estadoClinico.display || 'Sin estado')}</span>
            </div>

            <div class="row g-3">
              <!-- Columna izquierda -->
              <div class="col-12 col-md-6">
                <p class="mb-2"><strong>Código ICD-10:</strong> 
                <span style="color: black;">${(data.codigo.codigo || 'N/A')}</span>
                </p>
                
                ${data.categoria.display ? `
                  <p class="mb-2"><strong>Categoría:</strong> 
                    ${Utils.escapeHtml(data.categoria.display)}
                   </p>
                ` : ''}

                ${data.estadoVerificacion.display ? `
                <p class="mb-2">
                  <strong>Estado de Verificación:</strong>
                  ${Utils.escapeHtml(data.estadoVerificacion.display)}
                </p>
              ` : ''}
            </div>
            ${data.notas ? `
              <div class="mt-2 p-2 bg-light rounded">
                <p class="mb-0"><strong>Notas:</strong></p>
                <p class="mb-0 text-muted small">${Utils.escapeHtml(data.notas)}</p>
              </div>
            ` : ''}
          </div>
        `;
      });

      html += `
          </div>
        </div>
      `;
    }

    // Alergias e Intolerancias
    if (resources.allergyIntolerances?.length > 0) {
      html += `
        <div class="card mb-3 shadow-sm">
          <div class="card-header bg-danger text-white">
            <h4 class="mb-0">
              <i class="bx bx-error-alt"></i>
              Alergias e Intolerancias (${resources.allergyIntolerances.length})
            </h4>
          </div>
          <div class="card-body">
      `;

      resources.allergyIntolerances.forEach((allergy, idx) => {
        const a = DataMappers.extractAllergyData(allergy);
        if (!a) return;

        // ── Badges dinámicos ──────────────────────────────────────────────────

        // Estado clínico
        const estadoBadge = (() => {
          const c = (a.estadoClinico.display || '').toLowerCase();
          if (c.includes('active') || c.includes('activ')) return ['bg-danger', a.estadoClinico.display];
          if (c.includes('resolved') || c.includes('resuel')) return ['bg-success', a.estadoClinico.display];
          if (c.includes('inactive') || c.includes('inactiv')) return ['bg-secondary', a.estadoClinico.display];
          return a.estadoClinico.display ? ['bg-info', a.estadoClinico.display] : null;
        })();

        // Tipo FHIR (allergy | intolerance) — puede estar ausente
        const tipoBadge = (() => {
          const t = (a.tipo || '').toLowerCase();
          if (t === 'allergy') return ['bg-danger', '🚨 Alergia'];
          if (t === 'intolerance') return ['bg-warning text-dark', '⚠️ Intolerancia'];
          return null;
        })();

        // Criticidad — puede estar ausente
        const criticidadBadge = (() => {
          const c = (a.criticidad || '').toLowerCase();
          if (c === 'high') return ['bg-danger', '⚠️ Criticidad alta'];
          if (c === 'low') return ['bg-success', '🟢 Criticidad baja'];
          if (c.includes('unable')) return ['bg-secondary', '❓ Sin evaluar'];
          return a.criticidad ? ['bg-warning text-dark', a.criticidad] : null;
        })();

        // Verificación — puede ser null
        const verificacionBadge = a.estadoVerificacion ? (() => {
          const v = (a.estadoVerificacion.display || '').toLowerCase();
          const cls = v.includes('confirm') ? 'bg-success' : 'bg-warning text-dark';
          return [cls, a.estadoVerificacion.display];
        })() : null;

        // Color de severidad de reacciones
        const severidadColor = (s) => {
          const sl = (s || '').toLowerCase();
          if (sl === 'severe') return 'text-danger fw-bold';
          if (sl === 'moderate') return 'text-warning fw-bold';
          if (sl === 'mild') return 'text-success';
          return 'text-muted';
        };

        // Etiquetas de categoría FHIR
        const LABELS_CATEGORIA = {
          food: '🍽️ Alimento',
          medication: '💊 Medicamento',
          environment: '🌿 Ambiental',
          biologic: '🧬 Biológico'
        };

        // ── HTML del ítem ─────────────────────────────────────────────────────
        html += `
          ${idx > 0 ? '<hr class="my-3">' : ''}
          <div class="allergy-item ${idx > 0 ? 'mt-3' : ''}">

            <!-- Encabezado: nombre + badges de estado -->
            <div class="d-flex justify-content-between align-items-start flex-wrap gap-1 mb-3">
              <h5 class="mb-0">
                <span class="badge bg-secondary me-1">#${idx + 1}</span>
                ${Utils.escapeHtml(a.descripcion || a.tipoAlergia.display || 'Sin descripción')}
              </h5>
              <div class="d-flex flex-wrap gap-1">
                ${tipoBadge ? `<span class="badge ${tipoBadge[0]}">${tipoBadge[1]}</span>` : ''}
                ${estadoBadge ? `<span class="badge ${estadoBadge[0]}">${Utils.escapeHtml(estadoBadge[1])}</span>` : ''}
                ${criticidadBadge ? `<span class="badge ${criticidadBadge[0]}">${criticidadBadge[1]}</span>` : ''}
              </div>
            </div>

            <!-- Datos principales en dos columnas -->
            <div class="row g-3">

              <!-- Columna izquierda -->
              <div class="col-12 col-md-6">

                <!-- Tipo de alergia (campo code de MinSalud) -->
                ${a.tipoAlergia.display ? `
                  <p class="mb-2">
                    <strong>Tipo de Alergia:</strong>
                    ${Utils.escapeHtml(a.tipoAlergia.display)}
                   
                  </p>` : ''}

                <!-- Descripción libre (code.text) -->
                ${a.descripcion ? `
                  <p class="mb-2">
                    <strong>Descripción:</strong>
                    ${Utils.escapeHtml(a.descripcion)}
                  </p>` : ''}

                <!-- Estado de verificación -->
                ${verificacionBadge ? `
                  <p class="mb-2">
                    <strong>Verificación:</strong>
                    <span class="badge ${verificacionBadge[0]} ms-1">
                      ${Utils.escapeHtml(verificacionBadge[1])}
                    </span>
                  </p>` : ''}

                <!-- Categorías FHIR (food/medication/…) si vienen -->
                ${a.categorias.length > 0 ? `
                  <p class="mb-2">
                    <strong>Categoría:</strong>
                    ${a.categorias.map(c =>
          `<span class="badge bg-secondary ms-1">
                        ${LABELS_CATEGORIA[c] || Utils.escapeHtml(c)}
                      </span>`
        ).join('')}
                  </p>` : ''}

              </div>

              <!-- Columna derecha -->
              <div class="col-12 col-md-6">

              

                ${a.fechaRegistro ? `
                  <p class="mb-2">
                    <strong>Fecha de Registro:</strong>
                    ${Utils.escapeHtml(Utils.formatearFecha(a.fechaRegistro))}
                  </p>` : ''}


              </div>
            </div>

          </div>
        `;
      });

      html += `
          </div>
        </div>
      `;
    }

    // Administración de Medicamentos
    if (resources.medicationAdministrations?.length > 0) {
      html += `
        <div class="card mb-3 shadow-sm">
          <div class="card-header bg-info text-white">
            <h4 class="mb-0">
              <i class="bx bx-capsule"></i>
              Administración de Medicamentos (${resources.medicationAdministrations.length})
            </h4>
          </div>
          <div class="card-body">
      `;

      resources.medicationAdministrations.forEach((med, idx) => {
        const m = DataMappers.extractMedicationAdministrationData(med);
        if (!m) return;

        const statusBadge = (() => {
          const s = (m.status || '').toLowerCase();
          if (s === 'completed') return ['bg-success', '✅ Completado'];
          if (s === 'in-progress') return ['bg-primary', '🔄 En progreso'];
          if (s === 'stopped') return ['bg-danger', '🛑 Detenido'];
          if (s === 'on-hold') return ['bg-warning text-dark', '⏸️ En espera'];
          if (s === 'not-done') return ['bg-secondary', '❌ No realizado'];
          return m.status ? ['bg-secondary', m.status] : null;
        })();

        html += `
          ${idx > 0 ? '<hr class="my-3">' : ''}
          <div class="medadmin-item ${idx > 0 ? 'mt-3' : ''}">

            <!-- Encabezado: nombre del medicamento + estado -->
            <div class="d-flex justify-content-between align-items-start flex-wrap gap-1 mb-3">
              <h5 class="mb-0">
                <span class="badge bg-secondary me-1">#${idx + 1}</span>
                ${Utils.escapeHtml(m.medicamento.display || 'Sin nombre')}
              </h5>
              ${statusBadge
            ? `<span class="badge ${statusBadge[0]}">${statusBadge[1]}</span>`
            : ''}
            </div>

            <div class="row g-3">

              <!-- Columna izquierda: identificación del medicamento -->
              <div class="col-12 col-md-6">

                ${m.medicamento.codigo ? `
                  <p class="mb-2">
                    <strong>Código IUM:</strong>
                    <code>${Utils.escapeHtml(m.medicamento.codigo)}</code>
                  </p>` : ''}

                ${m.categoria.display ? `
                  <p class="mb-2">
                    <strong>Categoría:</strong>
                    ${Utils.escapeHtml(m.categoria.display)}
                    ${m.categoria.codigo
              ? `<code class="ms-1">${Utils.escapeHtml(m.categoria.codigo)}</code>`
              : ''}
                  </p>` : ''}

                ${m.fechaAdministracion ? `
                  <p class="mb-2">
                    <strong>Fecha de Administración:</strong>
                    ${Utils.escapeHtml(Utils.formatearFecha(m.fechaAdministracion))}
                  </p>` : ''}

             
              </div>

              <!-- Columna derecha: dosificación y cantidades -->
              <div class="col-12 col-md-6">

                ${m.dosage.dosis ? `
                  <p class="mb-2">
                    <strong>Dosis:</strong>
                    ${Utils.escapeHtml(String(m.dosage.dosis.valor))}
                    ${Utils.escapeHtml(m.dosage.dosis.unidad)}
                    
                  </p>` : ''}

                ${m.dosage.via ? `
                  <p class="mb-2">
                    <strong>Vía de Administración:</strong>
                    ${Utils.escapeHtml(m.dosage.via.display || m.dosage.via.codigo)}
                    ${m.dosage.via.codigo
              ? `<code class="ms-1">${Utils.escapeHtml(m.dosage.via.codigo)}</code>`
              : ''}
                  </p>` : ''}

                ${m.dosage.frecuencia ? `
                  <p class="mb-2">
                    <strong>Frecuencia:</strong>
                    Cada ${Utils.escapeHtml(String(m.dosage.frecuencia.valor))}
                    ${Utils.escapeHtml(m.dosage.frecuencia.unidad)}
                  </p>` : ''}

             


              </div>
            </div>

         

          </div>
        `;
      });

      html += `
          </div>
        </div>
      `;
    }

    // MedicationStatements (medicamentos referidos / crónicos)
    if (resources.medicationStatements?.length > 0) {
      html += `
        <div class="card mb-3 shadow-sm">
          <div class="card-header bg-info text-white">
            <h4 class="mb-0">
              <i class="bx bx-list-ul"></i>
              Medicamentos Referidos (${resources.medicationStatements.length})
            </h4>
          </div>
          <div class="card-body">
      `;

      resources.medicationStatements.forEach((med, idx) => {
        const medCoding = med?.medicationCodeableConcept?.coding?.[0];
        html += `
          ${idx > 0 ? '<hr class="my-2">' : ''}
          <div class="${idx > 0 ? 'mt-2' : ''}">
            <span class="badge bg-secondary me-1">#${idx + 1}</span>
            <strong>${Utils.escapeHtml(medCoding?.display || 'Sin nombre')}</strong>
            ${medCoding?.code ? `<code class="ms-1">${Utils.escapeHtml(medCoding.code)}</code>` : ''}
            ${med?.status ? `<span class="badge bg-secondary ms-2">${Utils.escapeHtml(med.status)}</span>` : ''}
          </div>
        `;
      });

      html += `
          </div>
        </div>
      `;
    }

    // Órdenes de Medicamento (MedicationRequest)
    if (resources.medicationRequests?.length > 0) {
      html += `
        <div class="card mb-3 shadow-sm">
          <div class="card-header bg-primary text-white">
            <h4 class="mb-0">
              <i class="bx bx-notepad"></i>
              Órdenes de Medicamento (${resources.medicationRequests.length})
            </h4>
          </div>
          <div class="card-body">
      `;

      resources.medicationRequests.forEach((req, idx) => {
        const r = DataMappers.extractMedicationRequestData(req);
        if (!r) return;

        // ── Badges ────────────────────────────────────────────────────────────
        const statusBadge = (() => {
          const s = (r.status || '').toLowerCase();
          if (s === 'active') return ['bg-success', '✅ Activa'];
          if (s === 'completed') return ['bg-secondary', '☑️ Completada'];
          if (s === 'cancelled') return ['bg-danger', '❌ Cancelada'];
          if (s === 'on-hold') return ['bg-warning text-dark', '⏸️ En espera'];
          if (s === 'stopped') return ['bg-danger', '🛑 Detenida'];
          if (s === 'draft') return ['bg-secondary', '📝 Borrador'];
          return r.status ? ['bg-secondary', r.status] : null;
        })();

        const intentBadge = (() => {
          const i = (r.intent || '').toLowerCase();
          if (i === 'order') return ['bg-primary', '📋 Orden'];
          if (i === 'plan') return ['bg-info', '🗓️ Plan'];
          if (i === 'proposal') return ['bg-secondary', '💡 Propuesta'];
          if (i === 'instance-order') return ['bg-primary', '📋 Orden instancia'];
          return r.intent ? ['bg-secondary', r.intent] : null;
        })();

        // Etiqueta de duración legible
        const UNIDADES_DURACION = { d: 'días', h: 'horas', wk: 'semanas', mo: 'meses', a: 'años', min: 'minutos' };
        const duracionLabel = r.dosage?.timing?.duracion != null
          ? `${r.dosage.timing.duracion} ${UNIDADES_DURACION[r.dosage.timing.unidadDuracion] || r.dosage.timing.unidadDuracion}`
          : '';

        html += `
          ${idx > 0 ? '<hr class="my-3">' : ''}
          <div class="medreq-item ${idx > 0 ? 'mt-3' : ''}">

            <!-- Encabezado: nombre medicamento + badges -->
            <div class="d-flex justify-content-between align-items-start flex-wrap gap-1 mb-3">
              <h5 class="mb-0">
                <span class="badge bg-secondary me-1">#${idx + 1}</span>
                ${Utils.escapeHtml(r.medicamento.display || 'Sin nombre')}
              </h5>
              <div class="d-flex flex-wrap gap-1">
                ${intentBadge ? `<span class="badge ${intentBadge[0]}">${intentBadge[1]}</span>` : ''}
                ${statusBadge ? `<span class="badge ${statusBadge[0]}">${statusBadge[1]}</span>` : ''}
                ${r.reportado ? `<span class="badge bg-warning text-dark">📣 Reportado</span>` : ''}
              </div>
            </div>

            <div class="row g-3">

              <!-- Columna izquierda: identificación -->
              <div class="col-12 col-md-6">

                ${r.medicamento.codigo ? `
                  <p class="mb-2">
                    <strong>Código (INN/IUM):</strong>
                    <code>${Utils.escapeHtml(r.medicamento.codigo)}</code>
                  </p>` : ''}

                ${r.categoria ? `
                  <p class="mb-2">
                    <strong>Categoría:</strong>
                    ${Utils.escapeHtml(r.categoria.display)}
                    ${r.categoria.codigo
              ? `<code class="ms-1">${Utils.escapeHtml(r.categoria.codigo)}</code>`
              : ''}
                  </p>` : ''}

                ${r.fechaOrden ? `
                  <p class="mb-2">
                    <strong>Fecha de Orden:</strong>
                    ${Utils.escapeHtml(Utils.formatearFecha(r.fechaOrden))}
                  </p>` : ''}

               

                ${r.motivos.length > 0 ? `
                  <p class="mb-2">
                    <strong>Motivo:</strong>
                    ${r.motivos.map(m =>
                `${Utils.escapeHtml(m.display || m.text || m.codigo)}`
                + (m.codigo ? ` <code>${Utils.escapeHtml(m.codigo)}</code>` : '')
              ).join(', ')}
                  </p>` : ''}

              </div>

              <!-- Columna derecha: dosificación -->
              <div class="col-12 col-md-6">

                ${r.dosage?.dosis ? `
                  <p class="mb-2">
                    <strong>Dosis:</strong>
                    ${Utils.escapeHtml(String(r.dosage.dosis.valor))}
                    ${Utils.escapeHtml(r.dosage.dosis.unidad)}
                    ${r.dosage.dosis.codigo
              ? `<code class="ms-1">${Utils.escapeHtml(r.dosage.dosis.codigo)}</code>`
              : ''}
                  </p>` : ''}

                ${r.dosage?.via ? `
                  <p class="mb-2">
                    <strong>Vía:</strong>
                    ${Utils.escapeHtml(r.dosage.via.display || r.dosage.via.codigo)}
                    ${r.dosage.via.codigo
              ? `<code class="ms-1">${Utils.escapeHtml(r.dosage.via.codigo)}</code>`
              : ''}
                  </p>` : ''}

                ${r.dosage?.frecuencia ? `
                  <p class="mb-2">
                    <strong>Frecuencia:</strong>
                    Cada ${Utils.escapeHtml(String(r.dosage.frecuencia.valor))}
                    ${Utils.escapeHtml(r.dosage.frecuencia.unidad)}
                  </p>` : ''}

                ${duracionLabel ? `
                  <p class="mb-2">
                    <strong>Duración del tratamiento:</strong>
                    ${Utils.escapeHtml(duracionLabel)}
                    ${r.dosage?.timing?.display
              ? `<span class="text-muted">(${Utils.escapeHtml(r.dosage.timing.display)})</span>`
              : ''}
                  </p>` : ''}

                ${r.dosage?.texto ? `
                  <p class="mb-2">
                    <strong>Instrucción:</strong>
                    <span class="text-muted">${Utils.escapeHtml(r.dosage.texto)}</span>
                  </p>` : ''}

              </div>
            </div>

            ${r.notas ? `
              <div class="mt-3 p-2 bg-light rounded border-start border-primary border-3">
                <p class="mb-1 fw-bold">Notas:</p>
                <p class="mb-0 text-muted small">${Utils.escapeHtml(r.notas)}</p>
              </div>
            ` : ''}

          </div>
        `;
      });

      html += `
          </div>
        </div>
      `;
    }

    // Antecedentes
    resources.antecedentes?.forEach((ant) => {
      html += `
        <div class="card mb-3 shadow-sm">
          <div class="card-header bg-warning text-white">
            <h4 class="mb-0">Antecedente Familiar</h4>
          </div>
          <div class="card-body">
            <p><strong>Estado:</strong> ${ant?.status || 'N/A'}</p>
            <p><strong>Relación Familiar:</strong> ${ant?.relationship?.coding?.[0]?.display || 'N/A'}</p>
            <p><strong>Condición:</strong> ${ant?.condition?.[0]?.code?.coding?.[0]?.display || 'N/A'}</p>
          </div>
        </div>
      `;
    });


    // Procedimientos
    if (resources.procedures?.length > 0) {
      html += `
        <div class="card mb-3 shadow-sm">
          <div class="card-header bg-success text-white">
            <h4 class="mb-0">
              <i class="bx bx-plus-medical"></i>
              Procedimientos (${resources.procedures.length})
            </h4>
          </div>
          <div class="card-body">
      `;

      resources.procedures.forEach((proc, idx) => {
        const p = DataMappers.extractProcedureData(proc);
        if (!p) return;

        const statusBadge = (() => {
          const s = (p.status || '').toLowerCase();
          if (s === 'completed') return ['bg-success', '✅ Completado'];
          if (s === 'in-progress') return ['bg-primary', '🔄 En progreso'];
          if (s === 'not-done') return ['bg-danger', '❌ No realizado'];
          if (s === 'stopped') return ['bg-danger', '🛑 Detenido'];
          if (s === 'preparation') return ['bg-warning text-dark', '⏳ Preparación'];
          return p.status ? ['bg-secondary', p.status] : null;
        })();

        html += `
          ${idx > 0 ? '<hr class="my-3">' : ''}
          <div class="procedure-item ${idx > 0 ? 'mt-3' : ''}">

            <!-- Encabezado: nombre + badges -->
            <div class="d-flex justify-content-between align-items-start flex-wrap gap-1 mb-3">
              <h5 class="mb-0">
                <span class="badge bg-secondary me-1">#${idx + 1}</span>
                ${Utils.escapeHtml(
          p.codigo?.display || p.codigoTexto || 'Sin nombre'
        )}
              </h5>
              <div class="d-flex flex-wrap gap-1">
                ${p.esOtraTecnologia
            ? `<span class="badge bg-warning text-dark">🧩 Otra Tecnología</span>`
            : `<span class="badge bg-info">🏥 Procedimiento</span>`}
                ${statusBadge
            ? `<span class="badge ${statusBadge[0]}">${statusBadge[1]}</span>`
            : ''}
              </div>
            </div>

            <div class="row g-3">

              <!-- Columna izquierda -->
              <div class="col-12 col-md-6">

                ${p.codigo?.codigo ? `
                  <p class="mb-2"><strong>Código CUPS:</strong>
                    <code>${Utils.escapeHtml(p.codigo.codigo)}</code>
                  </p>` : ''}

                ${p.codigoTexto && !p.codigo?.display ? `
                  <p class="mb-2"><strong>Descripción:</strong>
                    ${Utils.escapeHtml(p.codigoTexto)}
                  </p>` : ''}

                ${p.categoria ? `
                  <p class="mb-2"><strong>Categoría:</strong>
                    ${Utils.escapeHtml(p.categoria.display)}
                    ${p.categoria.codigo
              ? `<code class="ms-1">${Utils.escapeHtml(p.categoria.codigo)}</code>`
              : ''}
                  </p>` : ''}

                ${p.fechaSolicitud ? `
                  <p class="mb-2"><strong>Fecha de Solicitud:</strong>
                    ${Utils.escapeHtml(Utils.formatearFecha(p.fechaSolicitud))}
                  </p>` : ''}

                ${p.fechaRealizacion ? `
                  <p class="mb-2"><strong>Fecha de Realización:</strong>
                    ${Utils.escapeHtml(Utils.formatearFecha(p.fechaRealizacion))}
                  </p>` : ''}

                ${p.motivos.length > 0 ? `
                  <p class="mb-2"><strong>Finalidad:</strong>
                    ${p.motivos.map(m =>
                `${Utils.escapeHtml(m.display || m.text || m.codigo)}`
                + (m.codigo ? ` <code>${Utils.escapeHtml(m.codigo)}</code>` : '')
              ).join(', ')}
                  </p>` : ''}

              </div>

            </div>

          </div>
        `;
      });

      html += `
          </div>
        </div>
      `;
    }

    // Observaciones (Incapacidad, Ocupación, Resultado de Procedimiento)
    if (resources.observations?.length > 0) {
      html += `
        <div class="card mb-3 shadow-sm">
          <div class="card-header bg-info text-white">
            <h4 class="mb-0">
              <i class="bx bx-test-tube"></i>
              Observaciones (${resources.observations.length})
            </h4>
          </div>
          <div class="card-body">
      `;

      resources.observations.forEach((obs, idx) => {
        const o = DataMappers.extractObservationData(obs);
        if (!o) return;

        const statusBadge = (() => {
          const s = (o.status || '').toLowerCase();
          if (s === 'final') return ['bg-success', '✅ Final'];
          if (s === 'preliminary') return ['bg-warning text-dark', '⏳ Preliminar'];
          if (s === 'registered') return ['bg-secondary', '📝 Registrado'];
          if (s === 'amended') return ['bg-info', '✏️ Enmendado'];
          if (s === 'cancelled') return ['bg-danger', '❌ Cancelado'];
          return o.status ? ['bg-secondary', o.status] : null;
        })();

        const tipoBadge = (() => {
          if (o.tipoObs === 'incapacidad') return ['bg-warning text-dark', '🩹 Incapacidad'];
          if (o.tipoObs === 'ocupacion') return ['bg-primary', '💼 Ocupación'];
          if (o.tipoObs === 'resultado-procedimiento') return ['bg-success', '🔬 Resultado'];
          return ['bg-secondary', 'Observación'];
        })();

        html += `
          ${idx > 0 ? '<hr class="my-3">' : ''}
          <div class="observation-item ${idx > 0 ? 'mt-3' : ''}">

            <!-- Encabezado: nombre + badges -->
            <div class="d-flex justify-content-between align-items-start flex-wrap gap-1 mb-3">
              <h5 class="mb-0">
                <span class="badge bg-secondary me-1">#${idx + 1}</span>
                ${Utils.escapeHtml(o.codigo?.display || o.codigoTexto || 'Sin nombre')}
              </h5>
              <div class="d-flex flex-wrap gap-1">
                <span class="badge ${tipoBadge[0]}">${tipoBadge[1]}</span>
                ${statusBadge
            ? `<span class="badge ${statusBadge[0]}">${statusBadge[1]}</span>`
            : ''}
              </div>
            </div>

            <div class="row g-3">

              <!-- Columna izquierda: identificación -->
              <div class="col-12 col-md-6">

                ${o.codigo?.codigo ? `
                  <p class="mb-2"><strong>Código:</strong>
                    <code>${Utils.escapeHtml(o.codigo.codigo)}</code>
                  </p>` : ''}

                ${o.codigoTexto && o.codigoTexto !== o.codigo?.display ? `
                  <p class="mb-2"><strong>Descripción:</strong>
                    ${Utils.escapeHtml(o.codigoTexto)}
                  </p>` : ''}

                ${o.fechaEfectiva ? `
                  <p class="mb-2"><strong>Fecha Efectiva:</strong>
                    ${Utils.escapeHtml(Utils.formatearFecha(o.fechaEfectiva))}
                  </p>` : ''}

                

              </div>

              <!-- Columna derecha: valores -->
              <div class="col-12 col-md-6">

                

                ${o.dispositivo ? `
                  <p class="mb-2"><strong>Dispositivo/Equipo:</strong>
                    <code>${Utils.escapeHtml(o.dispositivo)}</code>
                  </p>` : ''}

                ${o.tipoObs === 'ocupacion' && o.valor?.display ? `
                  <p class="mb-2"><strong>Ocupación (CIUO):</strong>
                    ${Utils.escapeHtml(o.valor.display)}
                    ${o.valor.codigo
              ? `<code class="ms-1">${Utils.escapeHtml(o.valor.codigo)}</code>`
              : ''}
                  </p>` : ''}

              </div>
            </div>

            <!-- Componentes (incapacidad, resultado procedimiento) -->
            ${o.componentes.length > 0 ? `
              <div class="mt-3">
                <p class="mb-2 fw-bold">Componentes:</p>
                <div class="table-responsive">
                  <table class="table table-sm table-bordered align-middle mb-0">
                    <thead class="table-light">
                      <tr>
                        <th>Campo</th>
                        <th>Valor</th>
                      </tr>
                    </thead>
                    <tbody>
                      ${o.componentes.map(c => `
                        <tr>
                          <td>
                            ${c.display
                  ? `<strong>${Utils.escapeHtml(c.display)}</strong>`
                  : `<strong>${Utils.escapeHtml(c.texto)}</strong>`}
                            ${c.codigo ? `<br><code class="text-muted small">${Utils.escapeHtml(c.codigo)}</code>` : ''}
                          </td>
                          <td>
                            <span class="badge bg-primary">
                              ${Utils.escapeHtml(c.valor)}
                            </span>
                          </td>
                        </tr>
                      `).join('')}
                    </tbody>
                  </table>
                </div>
              </div>
            ` : ''}

        

          </div>
        `;
      });

      html += `
          </div>
        </div>
      `;
    }

    // Factores de Riesgo (RiskAssessment)
    if (resources.riskAssessments?.length > 0) {
      html += `
        <div class="card mb-3 shadow-sm">
          <div class="card-header bg-warning text-dark">
            <h4 class="mb-0">
              <i class="bx bx-shield-alt-2"></i>
              Factores de Riesgo (${resources.riskAssessments.length})
            </h4>
          </div>
          <div class="card-body">
      `;

      resources.riskAssessments.forEach((risk, idx) => {
        const r = DataMappers.extractRiskAssessmentData(risk);
        if (!r) return;

        const statusBadge = (() => {
          const s = (r.status || '').toLowerCase();
          if (s === 'final') return ['bg-success', '✅ Final'];
          if (s === 'registered') return ['bg-primary', '📝 Registrado'];
          if (s === 'preliminary') return ['bg-warning text-dark', '⏳ Preliminar'];
          if (s === 'amended') return ['bg-info', '✏️ Enmendado'];
          if (s === 'corrected') return ['bg-info', '🔧 Corregido'];
          if (s === 'cancelled') return ['bg-danger', '❌ Cancelado'];
          return r.status ? ['bg-secondary', r.status] : null;
        })();

        html += `
          ${idx > 0 ? '<hr class="my-3">' : ''}
          <div class="risk-item ${idx > 0 ? 'mt-3' : ''}">

            <!-- Encabezado: nombre + badges -->
            <div class="d-flex justify-content-between align-items-start flex-wrap gap-1 mb-3">
              <h5 class="mb-0">
                <span class="badge bg-secondary me-1">#${idx + 1}</span>
                ${Utils.escapeHtml(r.codigo?.display || r.codigoTexto || 'Sin nombre')}
              </h5>
              <div class="d-flex flex-wrap gap-1">
                ${statusBadge
            ? `<span class="badge ${statusBadge[0]}">${statusBadge[1]}</span>`
            : ''}
              </div>
            </div>

            <div class="row g-3">

              <!-- Columna izquierda -->
              <div class="col-12 col-md-6">

                ${r.codigo?.codigo ? `
                  <p class="mb-2"><strong>Código (FactorRiesgo):</strong>
                    <code>${Utils.escapeHtml(r.codigo.codigo)}</code>
                  </p>` : ''}

                ${r.codigoTexto && r.codigoTexto !== r.codigo?.display ? `
                  <p class="mb-2"><strong>Descripción:</strong>
                    ${Utils.escapeHtml(r.codigoTexto)}
                  </p>` : ''}

              </div>

            
            </div>

          </div>
        `;
      });

      html += `
          </div>
        </div>
      `;
    }


    // Solicitudes de Servicio (ServiceRequest)
    if (resources.serviceRequests?.length > 0) {
      html += `
        <div class="card mb-3 shadow-sm">
          <div class="card-header bg-primary text-white">
            <h4 class="mb-0">
              <i class="bx bx-file-blank"></i>
              Solicitudes de Servicio (${resources.serviceRequests.length})
            </h4>
          </div>
          <div class="card-body">
      `;

      resources.serviceRequests.forEach((svc, idx) => {
        const s = DataMappers.extractServiceRequestData(svc);
        if (!s) return;

        const statusBadge = (() => {
          const st = (s.status || '').toLowerCase();
          if (st === 'active') return ['bg-primary', '✅ Activa'];
          if (st === 'completed') return ['bg-success', '☑️ Completada'];
          if (st === 'on-hold') return ['bg-warning text-dark', '⏸️ En espera'];
          if (st === 'cancelled') return ['bg-danger', '❌ Cancelada'];
          if (st === 'draft') return ['bg-secondary', '📝 Borrador'];
          if (st === 'revoked') return ['bg-danger', '🚫 Revocada'];
          return s.status ? ['bg-secondary', s.status] : null;
        })();

        const intentBadge = (() => {
          const i = (s.intent || '').toLowerCase();
          if (i === 'order') return ['bg-primary', '📋 Orden'];
          if (i === 'plan') return ['bg-info', '🗓️ Plan'];
          if (i === 'proposal') return ['bg-secondary', '💡 Propuesta'];
          if (i === 'directive') return ['bg-warning text-dark', '⚠️ Directiva'];
          return s.intent ? ['bg-secondary', s.intent] : null;
        })();

        const prioridadBadge = (() => {
          const p = (s.prioridad || '').toLowerCase();
          if (p === 'urgent') return ['bg-danger', '🔴 Urgente'];
          if (p === 'asap') return ['bg-warning text-dark', '🟡 Lo antes posible'];
          if (p === 'routine') return ['bg-secondary', '🟢 Rutina'];
          if (p === 'stat') return ['bg-danger', '⚡ STAT'];
          return null;
        })();

        html += `
          ${idx > 0 ? '<hr class="my-3">' : ''}
          <div class="service-request-item ${idx > 0 ? 'mt-3' : ''}">

            <!-- Encabezado: nombre + badges -->
            <div class="d-flex justify-content-between align-items-start flex-wrap gap-1 mb-3">
              <h5 class="mb-0">
                <span class="badge bg-secondary me-1">#${idx + 1}</span>
                ${Utils.escapeHtml(s.codigo?.display || s.codigoTexto || 'Sin nombre')}
              </h5>
              <div class="d-flex flex-wrap gap-1">
                ${intentBadge ? `<span class="badge ${intentBadge[0]}">${intentBadge[1]}</span>` : ''}
                ${statusBadge ? `<span class="badge ${statusBadge[0]}">${statusBadge[1]}</span>` : ''}
                ${prioridadBadge ? `<span class="badge ${prioridadBadge[0]}">${prioridadBadge[1]}</span>` : ''}
              </div>
            </div>

            <div class="row g-3">

              <!-- Columna izquierda -->
              <div class="col-12 col-md-6">

                ${s.codigo?.codigo ? `
                  <p class="mb-2"><strong>Código CUPS:</strong>
                    <code>${Utils.escapeHtml(s.codigo.codigo)}</code>
                  </p>` : ''}

                ${s.codigoTexto && s.codigoTexto !== s.codigo?.display ? `
                  <p class="mb-2"><strong>Descripción:</strong>
                    ${Utils.escapeHtml(s.codigoTexto)}
                  </p>` : ''}

                ${s.categoria ? `
                  <p class="mb-2"><strong>Categoría:</strong>
                    ${Utils.escapeHtml(s.categoria.display)}
                    ${s.categoria.codigo
              ? `<code class="ms-1">${Utils.escapeHtml(s.categoria.codigo)}</code>`
              : ''}
                  </p>` : ''}

                ${s.fechaSolicitud ? `
                  <p class="mb-2"><strong>Fecha de Solicitud:</strong>
                    ${Utils.escapeHtml(Utils.formatearFecha(s.fechaSolicitud))}
                  </p>` : ''}

            

              </div>

              <!-- Columna derecha -->
              <div class="col-12 col-md-6">

               

                ${s.motivos.length > 0 ? `
                  <p class="mb-2"><strong>Finalidad:</strong>
                    ${s.motivos.map(m =>
                `${Utils.escapeHtml(m.display || m.text || m.codigo)}`
                + (m.codigo ? ` <code>${Utils.escapeHtml(m.codigo)}</code>` : '')
              ).join(', ')}
                  </p>` : ''}

               

                ${s.bodySite.length > 0 ? `
                  <p class="mb-2"><strong>Parte del Cuerpo:</strong>
                    ${s.bodySite.map(bs =>
                `<span class="badge bg-secondary me-1">${Utils.escapeHtml(bs)}</span>`
              ).join('')}
                  </p>` : ''}

              </div>
            </div>

          


          </div>
        `;
      });

      html += `
          </div>
        </div>
      `;
    }
    // ── Documentos Clínicos (DocumentReference) ──────────────────────────────
    if (resources.documentReferences?.length > 0) {
      html += `
            <div class="card mb-3 shadow-sm">
              <div class="card-header bg-secondary text-white">
                <h4 class="mb-0"><i class="bx bx-file"></i> Documentos Clínicos (${resources.documentReferences.length})</h4>
              </div>
              <div class="card-body">
          `;

      resources.documentReferences.forEach((doc, idx) => {
        const d = DataMappers.extractDocumentReferenceData(doc);
        if (!d) return;

        const statusBadge = (() => {
          const s = (d.status || '').toLowerCase();
          if (s === 'current') return ['bg-success', '✅ Actual'];
          if (s === 'superseded') return ['bg-warning text-dark', '⚠️ Reemplazado'];
          if (s === 'entered-in-error') return ['bg-danger', '❌ Error'];
          return d.status ? ['bg-secondary', d.status] : null;
        })();

        html += `
              ${idx > 0 ? '<hr class="my-3">' : ''}
              <div class="document-item ${idx > 0 ? 'mt-3' : ''}">
                <div class="d-flex justify-content-between align-items-start flex-wrap gap-1 mb-3">
                  <h5 class="mb-0">
                    <span class="badge bg-secondary me-1">#${idx + 1}</span>
                    <i class="bx bx-file-blank me-1"></i>
                    ${Utils.escapeHtml(d.tipo?.display || 'Documento')}
                  </h5>
                  <div class="d-flex flex-wrap gap-1">
                    ${statusBadge ? `<span class="badge ${statusBadge[0]}">${statusBadge[1]}</span>` : ''}
                    ${d.confidencialidad.length > 0 ? `<span class="badge bg-danger">🔒 ${Utils.escapeHtml(d.confidencialidad[0])}</span>` : ''}
                  </div>
                </div>
                <div class="row g-3">
                  <div class="col-12 col-md-6">
                    ${d.categoria ? `<p class="mb-2"><strong>Categoría:</strong> ${Utils.escapeHtml(d.categoria.display)}</p>` : ''}
                    ${d.fecha ? `<p class="mb-2"><strong>Fecha:</strong> ${Utils.escapeHtml(Utils.formatearFecha(d.fecha))}</p>` : ''}
                  </div>
                  <div class="col-12 col-md-6">
                    ${d.periodoContexto ? `<p class="mb-2"><strong>Período:</strong> ${d.periodoContexto.inicio ? Utils.escapeHtml(Utils.formatearFecha(d.periodoContexto.inicio)) : '?'} → ${d.periodoContexto.fin ? Utils.escapeHtml(Utils.formatearFecha(d.periodoContexto.fin)) : '?'}</p>` : ''}
                  </div>
                </div>
                ${d.descripcion ? `<div class="mt-2"><p class="mb-1"><strong>Descripción:</strong></p><p class="text-muted small mb-0">${Utils.escapeHtml(d.descripcion)}</p></div>` : ''}
    
                <!-- ── Adjuntos Mejorado ── -->
                ${d.contenidos.length > 0 ? `
                  <div class="mt-3">
                    <p class="mb-2 fw-bold">Adjuntos (${d.contenidos.length}):</p>
                    <div class="list-group">
                      ${d.contenidos.map((c, cidx) => {
          const ruta = Utils.getText(c.url);
          const contentType = Utils.getText(c.contentType).toLowerCase();
          const titulo = c.titulo || `Archivo ${cidx + 1}`;
          const esPDF =
            contentType.includes('pdf') ||
            ruta.toLowerCase().includes('.pdf') ||
            ruta.includes('/DocumentReference/') ||
            ruta.includes('/descargar') ||
            c.tieneData === true;

          let botonAccion = '';

          if (esPDF) {
            if (ruta.includes('/DocumentReference/') || ruta.includes('/descargar')) {
              botonAccion = `
                              <button class="btn btn-sm btn-danger"
                                      onclick="PDFViewerService.mostrar('${ruta.replace(/'/g, "\\'")}')"
                                      title="Ver PDF en visor">
                                <i class="bx bxs-file-pdf"></i> Ver PDF
                              </button>`;
            } else if (c.tieneData) {
              botonAccion = `<button class="btn btn-sm btn-secondary" disabled title="Contenido embebido (base64)">Base64</button>`;
            } else if (ruta) {
              botonAccion = `<a href="${Utils.escapeHtml(ruta)}" class="btn btn-sm btn-primary" target="_blank" rel="noopener">
                                             <i class="bx bx-link-external"></i> Abrir
                                           </a>`;
            }
          } else if (ruta) {
            botonAccion = `<a href="${Utils.escapeHtml(ruta)}" class="btn btn-sm btn-primary" target="_blank" rel="noopener">
                                           <i class="bx bx-link-external"></i> Abrir
                                         </a>`;
          }

          return `
                          <div class="list-group-item">
                            <div class="d-flex justify-content-between align-items-start">
                              <div>
                                <h6 class="mb-1">
                                  <i class="bx ${esPDF ? 'bxs-file-pdf text-danger' : 'bx-file'} me-1"></i>
                                  ${Utils.escapeHtml(titulo)}
                                </h6>
                                <p class="mb-1 small text-muted">
                                  ${contentType ? `Tipo: <code>${Utils.escapeHtml(c.contentType)}</code>` : ''}
                                  ${c.formato ? ` • Formato: ${Utils.escapeHtml(c.formato)}` : ''}
                                  ${c.tamanio ? ` • Tamaño: ${(c.tamanio / 1024).toFixed(1)} KB` : ''}
                                </p>
                              </div>
                              ${botonAccion}
                            </div>
                          </div>
                        `;
        }).join('')}
                    </div>
                  </div>
                ` : ''}
              </div>
            `;
      });

      html += `</div></div>`;
    }


    const tieneExtras = Object.values(resources).some(arr => Array.isArray(arr) && arr.length > 0);

    if (!tieneExtras) {
      html += `
        <div class="alert alert-info" role="alert">
          <h4 class="alert-heading">ℹ️ Información Adicional</h4>
          <p>No se encontraron recursos adicionales para este paciente en este RDA.</p>
        </div>
      `;
    }

    return html;
  },


  renderClarificationNotes(notes) {
    if (!notes || notes.length === 0) return '';

    let html = `
      <div class="card mb-3 shadow-sm">
        <div class="card-header text-white" style="background-color: #6f42c1;">
          <h4 class="mb-0">
            <i class="bx bx-edit-alt"></i>
            Notas Aclaratorias (${notes.length})
          </h4>
        </div>
        <div class="card-body">
    `;

    notes.forEach((obs, idx) => {
      const o = DataMappers.extractObservationData(obs);
      if (!o) return;

      // El texto de la nota viene en valueString → o.valor
      const textoNota = typeof o.valor === 'string' ? o.valor : '';

      // Obtener el performer (quien emitió la nota)
      const performerRef = o.performers?.[0] || '';
      const performerId = performerRef.includes('/') ? performerRef.split('/')[1] : '';

      html += `
        ${idx > 0 ? '<hr class="my-3">' : ''}
        <div class="clarification-note-item ${idx > 0 ? 'mt-3' : ''}">
  
          <div class="d-flex justify-content-between align-items-start flex-wrap gap-1 mb-2">
            <h5 class="mb-0">
              <span class="badge bg-secondary me-1">#${idx + 1}</span>
              <i class="bx bx-notepad me-1" style="color:#6f42c1;"></i>
              ${Utils.escapeHtml(o.codigoTexto || o.codigo?.display || 'Nota aclaratoria')}
            </h5>
            ${o.fechaEfectiva ? `
              <span class="badge bg-light text-dark border">
                <i class="bx bx-calendar me-1"></i>
                ${Utils.escapeHtml(Utils.formatearFecha(o.fechaEfectiva))}
              </span>` : ''}
          </div>
  
          ${textoNota ? `
            <div class="p-3 rounded" style="background-color:#f3eeff; border-left: 4px solid #6f42c1;">
              <p class="mb-0" style="white-space: pre-wrap; color: #3d2070;">
                ${Utils.escapeHtml(textoNota)}
              </p>
            </div>` :
          `<p class="text-muted small fst-italic">Sin contenido de nota.</p>`
        }
  
          ${performerId ? `
            <p class="mt-2 mb-0 text-muted small">
              <i class="bx bx-user me-1"></i>
              <strong>Emitida por:</strong> <code>${Utils.escapeHtml(performerId)}</code>
            </p>` : ''}
  
        </div>
      `;
    });

    html += `
        </div>
      </div>
    `;

    return html;
  },
};
// ============================================================================
// 12b. SERVICIO VISOR PDF
// ============================================================================

const PDFViewerService = {
  _objectUrl: null,
  _pdfDoc: null,
  _currentPage: 1,
  _totalPages: 0,
  _scale: 1.2,
  _rendering: false,

  ensureViewer() {
    let overlay = document.getElementById('pdfViewerOverlay');
    if (overlay) return overlay;

    overlay = document.createElement('div');
    overlay.id = 'pdfViewerOverlay';
    overlay.style.cssText = `
      display: none;
      position: fixed;
      inset: 0;
      background: rgba(0,0,0,0.85);
      z-index: 9999;
      flex-direction: column;
      align-items: center;
      justify-content: center;
    `;

    overlay.innerHTML = `
      <div style="
        background: #2d2d2d;
        width: 90vw;
        height: 90vh;
        border-radius: 8px;
        display: flex;
        flex-direction: column;
        overflow: hidden;
        box-shadow: 0 10px 40px rgba(0,0,0,0.6);
      ">
        <!-- Barra superior -->
        <div style="
          display: flex;
          justify-content: space-between;
          align-items: center;
          padding: 10px 16px;
          background: #343a40;
          color: #fff;
          flex-shrink: 0;
        ">
          <span id="pdfViewerTitle" style="font-size:14px; font-weight:600;">
            <i class="bx bxs-file-pdf" style="color:#e74c3c;"></i> Documento PDF
          </span>
          <!-- Controles de página -->
          <div style="display:flex; align-items:center; gap:8px;">
            <button id="pdfPrevPage" class="btn btn-sm btn-outline-light" title="Página anterior">
              <i class="bx bx-chevron-left"></i>
            </button>
            <span style="color:#fff; font-size:13px;">
              Página <span id="pdfCurrentPage">1</span> de <span id="pdfTotalPages">-</span>
            </span>
            <button id="pdfNextPage" class="btn btn-sm btn-outline-light" title="Página siguiente">
              <i class="bx bx-chevron-right"></i>
            </button>
            <span style="width:1px; background:#666; height:20px; display:inline-block; margin:0 4px;"></span>
            <button id="pdfZoomOut" class="btn btn-sm btn-outline-light" title="Alejar">
              <i class="bx bx-zoom-out"></i>
            </button>
            <button id="pdfZoomIn" class="btn btn-sm btn-outline-light" title="Acercar">
              <i class="bx bx-zoom-in"></i>
            </button>
            <span style="width:1px; background:#666; height:20px; display:inline-block; margin:0 4px;"></span>
            <button onclick="PDFViewerService.cerrar()" class="btn btn-sm btn-outline-light" title="Cerrar">
              <i class="bx bx-x"></i> Cerrar
            </button>
          </div>
        </div>

        <!-- Loader -->
        <div id="pdfViewerLoader" style="
          display: flex;
          flex: 1;
          align-items: center;
          justify-content: center;
          font-size: 18px;
          color: #aaa;
          gap: 10px;
        ">
          <i class="bx bx-loader-alt bx-spin" style="font-size:28px;"></i>
          Cargando documento...
        </div>

        <!-- Canvas container -->
        <div id="pdfCanvasContainer" style="
          display: none;
          flex: 1;
          overflow-y: auto;
          overflow-x: auto;
          background: #525659;
          display: none;
          align-items: flex-start;
          justify-content: center;
          padding: 20px;
        ">
          <canvas id="pdfCanvas" style="
            box-shadow: 0 4px 20px rgba(0,0,0,0.5);
            background: white;
            display: block;
          "></canvas>
        </div>

        <!-- Error -->
        <div id="pdfViewerError" style="
          display: none;
          flex: 1;
          align-items: center;
          justify-content: center;
          flex-direction: column;
          gap: 12px;
          color: #dc3545;
          font-size: 16px;
          padding: 20px;
          text-align: center;
        ">
          <i class="bx bx-error-circle" style="font-size:48px;"></i>
          <span id="pdfViewerErrorMsg">No se pudo cargar el documento.</span>
          <button onclick="PDFViewerService.cerrar()" class="btn btn-sm btn-secondary mt-2">Cerrar</button>
        </div>
      </div>
    `;

    overlay.addEventListener('click', (e) => {
      if (e.target === overlay) this.cerrar();
    });

    document.body.appendChild(overlay);

    // Eventos botones
    document.getElementById('pdfPrevPage').addEventListener('click', () => this.cambiarPagina(-1));
    document.getElementById('pdfNextPage').addEventListener('click', () => this.cambiarPagina(1));
    document.getElementById('pdfZoomIn').addEventListener('click', () => this.cambiarZoom(0.2));
    document.getElementById('pdfZoomOut').addEventListener('click', () => this.cambiarZoom(-0.2));

    return overlay;
  },

  async cargarPDFJS() {
    if (window.pdfjsLib) return;
    await new Promise((resolve, reject) => {
      const script = document.createElement('script');
      script.src = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js';
      script.onload = resolve;
      script.onerror = reject;
      document.head.appendChild(script);
    });
    window.pdfjsLib.GlobalWorkerOptions.workerSrc =
      'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js';
  },

  async mostrar(ruta) {
    const overlay = this.ensureViewer();
    const loader = document.getElementById('pdfViewerLoader');
    const container = document.getElementById('pdfCanvasContainer');
    const errorDiv = document.getElementById('pdfViewerError');
    const errorMsg = document.getElementById('pdfViewerErrorMsg');
    const titleEl = document.getElementById('pdfViewerTitle');

    overlay.style.display = 'flex';
    loader.style.display = 'flex';
    container.style.display = 'none';
    errorDiv.style.display = 'none';

    const segmentos = ruta.split('/').filter(Boolean);
    const idxDocRef = segmentos.indexOf('DocumentReference');
    const nombreDoc = idxDocRef !== -1
      ? `RDA – ${segmentos[idxDocRef + 1]?.slice(0, 8) ?? ''}…`
      : 'Documento PDF';
    titleEl.innerHTML = `<i class="bx bxs-file-pdf" style="color:#e74c3c;"></i> ${nombreDoc}`;

    if (this._objectUrl) {
      URL.revokeObjectURL(this._objectUrl);
      this._objectUrl = null;
    }

    try {
      await this.cargarPDFJS();

      const objectUrl = await APIService.obtenerPDF(ruta);
      this._objectUrl = objectUrl;

      const loadingTask = window.pdfjsLib.getDocument(objectUrl);
      this._pdfDoc = await loadingTask.promise;
      this._totalPages = this._pdfDoc.numPages;
      this._currentPage = 1;

      document.getElementById('pdfTotalPages').textContent = this._totalPages;

      loader.style.display = 'none';
      container.style.display = 'flex';

      await this.renderPagina(this._currentPage);

    } catch (e) {
      console.error('Error cargando PDF:', e);
      loader.style.display = 'none';
      errorMsg.textContent = e?.message || 'No se pudo cargar el documento.';
      errorDiv.style.display = 'flex';
    }
  },

  async renderPagina(num) {
    if (this._rendering) return;
    this._rendering = true;

    const page = await this._pdfDoc.getPage(num);
    const canvas = document.getElementById('pdfCanvas');
    const ctx = canvas.getContext('2d');

    const viewport = page.getViewport({ scale: this._scale });
    canvas.width = viewport.width;
    canvas.height = viewport.height;

    await page.render({ canvasContext: ctx, viewport }).promise;

    document.getElementById('pdfCurrentPage').textContent = num;
    document.getElementById('pdfPrevPage').disabled = num <= 1;
    document.getElementById('pdfNextPage').disabled = num >= this._totalPages;

    this._rendering = false;
  },

  async cambiarPagina(delta) {
    const nueva = this._currentPage + delta;
    if (nueva < 1 || nueva > this._totalPages) return;
    this._currentPage = nueva;
    await this.renderPagina(nueva);
  },

  async cambiarZoom(delta) {
    this._scale = Math.min(3, Math.max(0.5, this._scale + delta));
    await this.renderPagina(this._currentPage);
  },

  cerrar() {
    const overlay = document.getElementById('pdfViewerOverlay');
    if (overlay) overlay.style.display = 'none';

    this._pdfDoc = null;
    this._currentPage = 1;
    this._totalPages = 0;
    this._rendering = false;

    if (this._objectUrl) {
      URL.revokeObjectURL(this._objectUrl);
      this._objectUrl = null;
    }
  },

  _objectUrl: null,
  _pdfDoc: null,
  _currentPage: 1,
  _totalPages: 0,
  _scale: 1.2,
  _rendering: false
};

// Atajo de teclado ESC para cerrar el visor
document.addEventListener('keydown', (e) => {
  if (e.key === 'Escape') PDFViewerService?.cerrar();
});


// ============================================================================
// 13. CONTROLADORES PRINCIPALES
// ============================================================================

const AppController = {
  async manejarConsulta() {
    try {
      const elements = DOM.elements;

      if (!elements.inputTipoDocumento) {
        return alert('Error: No se encontró el selector de tipo de documento');
      }
      if (!elements.inputDocumento) {
        return alert('Error: No se encontró el campo de número de documento');
      }

      const tipoDocumento = elements.inputTipoDocumento.value;
      const numeroDocumento = elements.inputDocumento.value;

      if (!Utils.getText(numeroDocumento).trim()) {
        alert('Por favor ingrese un número de documento');
        elements.inputDocumento.focus();
        return;
      }
      if (!Utils.getText(tipoDocumento).trim()) {
        alert('Por favor seleccione un tipo de documento');
        elements.inputTipoDocumento.focus();
        return;
      }

      // Limpiar datos previos
      InmunizacionService.limpiar();

      // Iniciar consultas en paralelo
      const consultaPromises = [
        APIService.consultarComposition(tipoDocumento, numeroDocumento.trim()),
        InmunizacionService.consultar()
      ];

      // UI feedback
      const boton = elements.botonConsulta;
      const textoOriginal = boton ? boton.innerHTML : null;

      if (boton) {
        boton.disabled = true;
        boton.innerHTML = '<i class="bx bx-loader-alt bx-spin"></i> Consultando...';
      }

      try {
        const [resultado] = await Promise.all(consultaPromises);

        // Guardar datos globalmente
        AppState.datosGlobalesFHIR = resultado;

        // Procesar y mostrar
        await this.procesarResultados(resultado);

        // Resetear filtro
        const filtroSelect = elements.filtroTipoEncuentro;
        if (filtroSelect) filtroSelect.value = 'todos';

        // Mostrar contenedor del filtro
        const filtroContainer = DOM.qs('.accordion-body.bg-light.border-bottom');
        if (filtroContainer) filtroContainer.style.display = 'block';
      } finally {
        if (boton) {
          boton.disabled = false;
          boton.innerHTML = textoOriginal;
        }
      }
    } catch (error) {
      console.error('❌ Error en manejarConsulta:', error);
      alert(`Error: ${error.message}`);
    }
  },

  async procesarResultados(resultado) {
    try {
   

      const paciente = resultado?.referencedResources?.patients?.[0] || null;
      UIRenderers.renderPatient(paciente);

      this.actualizarBadges(resultado?.summary);

      const todosLosDocumentos = Utils.safeArray(resultado?.entry);
      const documentosHistorial = todosLosDocumentos.filter((e) => !e?.resource?.encounter);
      const documentosEncuentros = todosLosDocumentos.filter((e) => !!e?.resource?.encounter);

      console.log(`📊 Historial: ${documentosHistorial.length}, Encuentros: ${documentosEncuentros.length}`);

      UIRenderers.renderDocumentos(documentosHistorial);

      AppState.datosEncuentrosGlobales = documentosEncuentros;
      UIRenderers.renderEncuentros(documentosEncuentros);

      DOM.setBadgeText('badgeTotalEncuentros', `Total: ${documentosEncuentros.length}`);

      UIRenderers.renderPaginacion(resultado);
    } catch (error) {
      console.error('Error procesando resultados:', error);
      throw error;
    }
  },

  actualizarBadges(summary) {
    if (summary) {
      DOM.setBadgeText('badgePaciente', `Paciente: ${summary.compositionsPaciente || 0}`);
      DOM.setBadgeText('badgeEncuentros', `Encuentros: ${summary.compositionsEncuentros || 0}`);
    }
  },

  aplicarFiltroTipoEncuentro(tipoFiltro) {
    if (!AppState.datosEncuentrosGlobales || AppState.datosEncuentrosGlobales.length === 0) {
      console.warn('⚠️ No hay datos de encuentros cargados para filtrar');
      return;
    }

    const tf = Utils.getText(tipoFiltro).toLowerCase();
    let filtrados = [];

    if (tf === 'todos' || tf === '') {
      filtrados = AppState.datosEncuentrosGlobales;
    } else {
      const mapa = {
        consulta: ['consulta'],
        'hospitalización': ['hospitalización', 'hospitalizacion', 'hospital'],
        urgencias: ['urgencias', 'urgencia', 'emergencia'],
        procedimiento: ['procedimiento', 'procedimientos']
      };
      const claves = mapa[tf] || [tf];

      filtrados = AppState.datosEncuentrosGlobales.filter((entry) => {
        const titulo = Utils.getText(entry?.resource?.title).toLowerCase();
        return claves.some((p) => titulo.includes(p));
      });
    }

    UIRenderers.renderEncuentros(filtrados);
    DOM.setBadgeText('badgeTotalEncuentros', `Total: ${filtrados.length}`);
  }
};

// ============================================================================
// 14. INICIALIZACIÓN
// ============================================================================

document.addEventListener('DOMContentLoaded', () => {
  console.log('📱 Página cargada completamente');

  const elements = DOM.elements;

  // Botón de consulta
  if (elements.botonConsulta) {
    elements.botonConsulta.addEventListener('click', (e) => {
      e.preventDefault();
      e.stopPropagation();
      AppController.manejarConsulta();
    });
  } else {
    console.error('❌ No se encontró el botón consultaInfoIhce');
  }

  // Filtro de encuentros
  if (elements.filtroTipoEncuentro) {
    elements.filtroTipoEncuentro.addEventListener('change', function () {
      console.log('🔄 Filtro de encuentros cambiado a:', this.value);
      AppController.aplicarFiltroTipoEncuentro(this.value);
    });
  } else {
    console.warn('⚠️ No se encontró el selector filtroTipoEncuentro');
  }

  // Exponer para debug
  window.AppDebug = {
    controller: AppController,
    state: AppState,
    api: APIService,
    utils: Utils
  };
});