(function (global) {
  const app = (global.EpicrisisApp = global.EpicrisisApp || {});
  const RECOMMENDATION_LABELS = {
    incapacidad: "Incapacidad",
    tratamiento_cuidado: "Tratamientos y cuidados",
    restriccion: "Restricciones",
    seguimiento_rehabilitacion: "Seguimiento y rehabilitación",
    signo_alarma: "Signos de alarma",
    otra: "Otras indicaciones",
  };

  function obtenerBotonGenerarPdf() {
    return document.getElementById("generar_pdf_btn");
  }

  function actualizarBotonGenerarPdf(options = {}) {
    const btn = obtenerBotonGenerarPdf();
    if (!btn) return;
    if (!btn.dataset.originalText) {
      btn.dataset.originalText = String(btn.textContent || "Generar PDF").trim() || "Generar PDF";
    }
    if (typeof options.disabled === "boolean") {
      btn.disabled = options.disabled;
      btn.setAttribute("aria-busy", options.disabled ? "true" : "false");
    }
    if (typeof options.text === "string" && options.text.trim()) {
      btn.textContent = options.text;
    } else if (options.resetText) {
      btn.textContent = btn.dataset.originalText;
    }
  }

  function mostrarAlertaPdf(mensaje) {
    const texto = String(mensaje || "").trim();
    if (texto) {
      AppToast.error(app.state.toastProcessIds.pdf || AppToast.show(texto, { type: "error" }), texto, {
        actionLabel: "Reintentar",
        onAction: app.toast.actions.reintentarPdf,
      });
    }
  }

  function obtenerMensajeError(error, fallback) {
    if (error instanceof Error && String(error.message || "").trim()) {
      return error.message;
    }
    return fallback;
  }

  function limpiarTemporizadorImpresion(key) {
    const state = app.state.pdfPrintState;
    if (state[key]) {
      globalThis.clearTimeout(state[key]);
      state[key] = null;
    }
  }

  function limpiarTemporizadoresImpresion() {
    limpiarTemporizadorImpresion("cleanupTimerId");
    limpiarTemporizadorImpresion("focusFallbackTimerId");
  }

function desmontarIframeImpresion() {
  const state = app.state.pdfPrintState;
  if (typeof state.releaseListeners === "function") {
    state.releaseListeners();
  }
  state.releaseListeners = null;
  if (state.activeFrame) {
    state.activeFrame.remove();
  }
  state.activeFrame = null;
  state.printRequested = false;
  app.state.syncGlobals();
  }

  function finalizarFlujoPdf(reason) {
    const state = app.state.pdfPrintState;
    limpiarTemporizadoresImpresion();
    desmontarIframeImpresion();
    state.isGenerating = false;
    actualizarBotonGenerarPdf({ disabled: false, resetText: true });
    if (app.state.toastProcessIds.pdf) {
      if (reason === "error" || reason === "print-error") {
        AppToast.error(app.state.toastProcessIds.pdf, "Falló la generación/impresión del PDF.", {
          actionLabel: "Reintentar",
          onAction: app.toast.actions.reintentarPdf,
        });
      } else {
        AppToast.success(app.state.toastProcessIds.pdf, "Flujo de impresión finalizado.", {
          description: "Puedes volver a imprimir o revisar el historial.",
          actionLabel: "Imprimir de nuevo",
          onAction: app.toast.actions.reintentarPdf,
        });
      }
    }
    app.state.syncGlobals();
  }

  function crearIframeImpresion() {
    const frame = document.createElement("iframe");
    frame.id = "epicrisis_pdf_print_frame";
    frame.className = "epicrisis-print-frame";
    frame.setAttribute("aria-hidden", "true");
    document.body.appendChild(frame);
    return frame;
  }

  function esperarIframeListoParaImprimir(frame, timeoutMs = 8000) {
    return new Promise((resolve, reject) => {
      let loadDetectado = false;

      const cleanup = () => {
        frame.removeEventListener("load", onLoad);
        globalThis.clearInterval(intervalId);
        globalThis.clearTimeout(timeoutId);
      };

      const listo = () => {
        const frameDoc = frame.contentDocument;
        if (frameDoc?.readyState !== "complete") return false;

        const frameWin = frame.contentWindow;
        if (!frameWin) return false;
        if (frameWin.__epicrisisPrintReady === true) return true;

        return Boolean(loadDetectado && frameDoc.body && frameDoc.body.innerText.trim().length > 0);
      };

      const onLoad = () => {
        loadDetectado = true;
        if (listo()) {
          cleanup();
          resolve();
        }
      };

      const intervalId = globalThis.setInterval(() => {
        if (listo()) {
          cleanup();
          resolve();
        }
      }, 100);

      const timeoutId = globalThis.setTimeout(() => {
        cleanup();
        reject(new Error("Timeout esperando a que el iframe del PDF quedara listo"));
      }, timeoutMs);

      frame.addEventListener("load", onLoad);
      if (listo()) {
        cleanup();
        resolve();
      }
    });
  }

  function registrarListenersImpresion(frame) {
    const state = app.state.pdfPrintState;
    const frameWindow = frame.contentWindow;
    if (!frameWindow) {
      throw new Error("No se pudo obtener la ventana del iframe de impresión");
    }

    const programarLimpiezaPorFoco = () => {
      if (!state.printRequested || state.activeFrame !== frame) return;
      limpiarTemporizadorImpresion("focusFallbackTimerId");
      state.focusFallbackTimerId = globalThis.setTimeout(() => {
        if (state.printRequested && state.activeFrame === frame) {
          finalizarFlujoPdf("focus-fallback");
        }
      }, 1200);
    };

    const onAfterPrint = () => {
      if (state.activeFrame !== frame) return;
      finalizarFlujoPdf("afterprint");
    };

    const onFrameFocus = () => {
      programarLimpiezaPorFoco();
    };

    const onWindowFocus = () => {
      programarLimpiezaPorFoco();
    };

    frameWindow.addEventListener("afterprint", onAfterPrint);
    frameWindow.addEventListener("focus", onFrameFocus);
    window.addEventListener("focus", onWindowFocus);

    state.releaseListeners = () => {
      frameWindow.removeEventListener("afterprint", onAfterPrint);
      frameWindow.removeEventListener("focus", onFrameFocus);
      window.removeEventListener("focus", onWindowFocus);
    };
  }

  function lanzarImpresionEnIframe(frame) {
    const state = app.state.pdfPrintState;
    if (state.activeFrame !== frame) {
      throw new Error("El iframe de impresión ya no esta activo");
    }
    if (state.printRequested) return;

    state.printRequested = true;
    actualizarBotonGenerarPdf({ disabled: true, text: "Abriendo impresión..." });
    registrarListenersImpresion(frame);

    limpiarTemporizadorImpresion("cleanupTimerId");
    state.cleanupTimerId = globalThis.setTimeout(() => {
      if (state.printRequested && state.activeFrame === frame) {
        finalizarFlujoPdf("timeout-fallback");
      }
    }, 20000);

    try {
      frame.contentWindow.focus();
      frame.contentWindow.print();
    } catch (error) {
      finalizarFlujoPdf("print-error");
      throw error;
    }
  }

  async function guardarPdfEpicrisisCache(payload) {
    const response = await fetch("/epicrisis/pdf_cache", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify(payload),
    });

    if (!response.ok) {
      let detalle = `HTTP ${response.status}`;
      const contentType = response.headers.get("content-type") || "";
      if (contentType.includes("application/json")) {
        const dataError = await response.json();
        if (dataError?.detail) detalle = dataError.detail;
      }
      throw new Error(detalle);
    }

    const data = await response.json();
    app.state.latestPdfCacheId = data?.cache_id || null;
    app.state.syncGlobals();
    return data;
  }

  function formatearCodigoDescripcion(item, codigoKeys, descripcionKeys) {
    if (!item || typeof item !== "object") return "";
    const codigo = codigoKeys.map((key) => String(item?.[key] || "").trim()).find(Boolean) || "";
    const descripcion = descripcionKeys.map((key) => String(item?.[key] || "").trim()).find(Boolean) || "";
    if (codigo && descripcion) return `${codigo} - ${descripcion}`;
    return codigo || descripcion;
  }

  function normalizePdfText(value) {
    const normalized = String(app.utils.stripHtml(value || "") || "")
      .replace(/\s+/g, " ")
      .trim();
    return /^(?:no especificado|sin informaci[oó]n)$/i.test(normalized) ? "" : normalized;
  }

  function hasMeaningfulText(value) {
    return Boolean(normalizePdfText(value));
  }

  function buildPdfRecommendationRows(recommendations) {
    const source = Array.isArray(recommendations) ? recommendations : [];
    const items = Object.keys(RECOMMENDATION_LABELS).flatMap((categoria) =>
      source
        .filter((item) => item && typeof item === "object" && item.categoria === categoria)
        .map((item) => ({
          categoria: RECOMMENDATION_LABELS[categoria],
          indicacion: normalizePdfText(item.indicacion),
          duracion: normalizePdfText(item.duracion),
          fecha: normalizePdfText(item.fecha),
        }))
        .filter((item) => item.indicacion || item.duracion || item.fecha)
    );
    return {
      items,
      rowsHtml: items.map((item) => `
        <tr>
          <td>${app.utils.escapeHtml(item.categoria)}</td>
          <td>${app.utils.escapeHtml(item.indicacion)}</td>
          <td>${app.utils.escapeHtml(item.duracion)}</td>
          <td>${app.utils.escapeHtml(item.fecha)}</td>
        </tr>
      `).join(""),
    };
  }

  function isPlaceholderFacturaProcedureField(value) {
    const normalized = normalizePdfText(value)
      .normalize("NFKD")
      .replace(/[\u0300-\u036f]/g, "")
      .toLowerCase();
    return ["", "[desc]", "codigo", "codigo cups", "descripcion", "no especificado", "procedimiento"]
      .includes(normalized);
  }

  function isMeaningfulFacturaProcedure(item) {
    const codigo = normalizePdfText(item?.codigo_soat || item?.codigo_cups);
    const descripcion = normalizePdfText(item?.descripcion || item?.concepto);
    if (isPlaceholderFacturaProcedureField(descripcion)) {
      return false;
    }
    if (codigo && isPlaceholderFacturaProcedureField(codigo)) {
      return false;
    }
    return Boolean(codigo || descripcion);
  }

  function renderPdfTableContent(headers, rows) {
    return `
      <table>
        <thead><tr>${headers.map((header) => `<th>${app.utils.escapeHtml(header)}</th>`).join("")}</tr></thead>
        <tbody>${rows}</tbody>
      </table>
    `;
  }

  function renderPdfSection(numero, titulo, contentHtml) {
    return `
      <section class="pdf-section">
        <h2>${numero}. ${app.utils.escapeHtml(titulo)}</h2>
        ${contentHtml}
      </section>
    `;
  }

  function buildPatientInfoRowsHtml({ metadatos, nombrePaciente }) {
    const rows = [
      ["Nombre del paciente", metadatos.nombre_paciente || nombrePaciente],
      ["Identificación", metadatos.datos_identificacion_paciente],
      ["Sexo", metadatos.sexo],
      ["Edad", metadatos.edad],
      ["Fecha de nacimiento", metadatos.fecha_nacimiento],
      ["Prestador de servicio", metadatos.prestador_servicio],
      ["No. de Caso", metadatos.caso],
      ["Fecha de ingreso", metadatos.fecha_ingreso],
      ["Motivo de consulta", metadatos.motivo_consulta],
      ["Teléfono", metadatos.telefono || metadatos.celular],
      ["Correo", metadatos.correo || metadatos.email],
      ["Dirección", metadatos.direccion],
    ]
      .filter(([, value]) => hasMeaningfulText(value))
      .map(([label, value]) => `
        <tr>
          <td><strong>${app.utils.escapeHtml(label)}</strong></td>
          <td>${app.utils.escapeHtml(String(value || "").trim())}</td>
        </tr>
      `);

    return rows.join("");
  }

  function buildPdfEpicrisisHtml({
    nombrePaciente,
    fechaGeneracion,
    metadatos,
    seccionesContenido,
  }) {
    const filasPaciente = buildPatientInfoRowsHtml({ metadatos, nombrePaciente });
    const seccionesNumeradas = seccionesContenido.map((seccion, index) =>
      renderPdfSection(index + 2, seccion.title, seccion.contentHtml)
    ).join("");
    const numeroSeccionFirma = seccionesContenido.length + 2;

    return `
      <!doctype html>
      <html lang="es">
      <head>
        <meta charset="utf-8" />
        <title>Epicrisis - ${app.utils.escapeHtml(nombrePaciente)}</title>
        <style>
          @page { size: A4; margin: 16mm 14mm 18mm 14mm; }
          html, body { width: 100%; }
          body { font-family: Arial, sans-serif; color: #1f2937; margin: 0; padding: 0; }
          .pdf-header { margin-bottom: 10px; page-break-after: avoid; break-after: avoid-page; }
          h1 { margin: 0 0 8px 0; color: #1d4ed8; }
          h2 { margin: 0 0 10px 0; color: #1e3a8a; border-bottom: 2px solid #dbeafe; padding-bottom: 6px; page-break-after: avoid; break-after: avoid-page; }
          p { line-height: 1.45; margin: 8px 0; }
          .meta { background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 8px; padding: 12px; }
          .pdf-section { margin-top: 16px; }
          table { width: 100%; border-collapse: collapse; margin-top: 8px; page-break-inside: auto; break-inside: auto; }
          th, td { border: 1px solid #d1d5db; padding: 8px; font-size: 12px; text-align: left; vertical-align: top; }
          th { background: #eff6ff; }
          thead { display: table-header-group; }
          tr { page-break-inside: avoid; break-inside: avoid-page; }
          .text-block { white-space: pre-wrap; background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 8px; padding: 12px; }
          .firma { margin-top: 24px; padding-top: 16px; page-break-inside: avoid; break-inside: avoid-page; }
          .footer { margin-top: 22px; font-size: 11px; color: #6b7280; }
        </style>
      </head>
      <body>
        <div class="pdf-header">
          <h1>Epicrisis</h1>
          <p><strong>Paciente:</strong> ${app.utils.escapeHtml(nombrePaciente)}</p>
          <p><strong>Fecha de generación:</strong> ${app.utils.escapeHtml(fechaGeneracion)}</p>
        </div>

        <section class="pdf-section">
          <h2>1. Información del Paciente</h2>
          <table>
            <tbody>${filasPaciente}</tbody>
          </table>
        </section>

        ${seccionesNumeradas}

        <section class="pdf-section">
          <h2>${numeroSeccionFirma}. Firma Médico Tratante</h2>
          <div class="firma">
            <p>________________________________________</p>
            <p><strong>Firma médico tratante</strong></p>
            <p>Nombre: ________________________________________</p>
            <p>Registro médico: _________________________________</p>
            <p>Fecha: __________________________________________</p>
          </div>
        </section>

        <div class="footer">Documento generado desde Epicrisis IA.</div>
        <script>
          (function () {
            if (globalThis.__epicrisisPrintSetupDone) return;
            globalThis.__epicrisisPrintSetupDone = true;
            globalThis.__epicrisisPrintReady = false;
            function markReady() {
              if (globalThis.__epicrisisPrintReady) return;
              globalThis.__epicrisisPrintReady = true;
            }
            function waitForFontsThenMark() {
              if (document.fonts && document.fonts.ready && typeof document.fonts.ready.then === "function") {
                document.fonts.ready.then(markReady).catch(markReady);
                return;
              }
              markReady();
            }
            if (document.readyState === "complete") {
              waitForFontsThenMark();
            } else {
              globalThis.addEventListener("load", waitForFontsThenMark, { once: true });
            }
          })();
        <\/script>
      </body>
      </html>
    `;
  }

  async function generarPdfEpicrisis() {
    const state = app.state.pdfPrintState;
    if (state.isGenerating) {
      AppToast.show("Ya hay un proceso de PDF en ejecución.", {
        type: "info",
        duration: 2500,
      });
      return;
    }

    state.isGenerating = true;
    state.printRequested = false;
    limpiarTemporizadoresImpresion();
    desmontarIframeImpresion();
    actualizarBotonGenerarPdf({ disabled: true, text: "Generando PDF..." });
    app.state.toastProcessIds.pdf = AppToast.start("Iniciando generación de PDF...", {
      description: "Construyendo contenido de epicrisis.",
      actionLabel: "Ver historial",
      onAction: app.toast.actions.verHistorial,
    });

    try {
      const data = app.state.data;
      const metadatos = data.metadatos || {};
      const nombrePaciente = normalizePdfText(data.nombrePaciente);
      const diagnosticosSeleccionados = app.selection.obtenerElementosSeleccionados("diagnosticos");
      const procedimientosSeleccionados = app.selection.obtenerElementosSeleccionados("procedimientos");
      const medicamentosSeleccionados = app.selection.obtenerElementosSeleccionados("medicamentos");
      const ayudasDiagnosticasSeleccionadas = app.selection.obtenerElementosSeleccionados("ayudas_diagnosticas");
      const imagenesDiagnosticasSeleccionadas = ayudasDiagnosticasSeleccionadas
        .filter((item) => item.tipo === "imagen")
        .map((item) => `${item.nombre}${item.concepto ? ` - ${item.concepto}` : ""}`);
      const mapaCie10Canonico = app.diagnosticos.construirMapaCie10Canonico();
      const hallazgosExtra = app.selection.obtenerTextoAmpliacion("hallazgos_extra");
      const descripcionProcedimientoExtra = app.selection.obtenerTextoAmpliacion("descripcion_procedimiento_extra");

      const resumen = normalizePdfText(data.resumen);
      const recomendaciones = Array.isArray(data.recomendacionesMedicas)
        ? data.recomendacionesMedicas
        : [];
      const antecedentes = Array.isArray(data.antecedentesHC) ? data.antecedentesHC : [];
      const hallazgos = app.selection.combinarTextoConAmpliacion(data.hallazgos, hallazgosExtra);
      const descripcionQx = app.selection.combinarTextoConAmpliacion(
        data.descripcionProcedimiento,
        descripcionProcedimientoExtra
      );
      const fechaGeneracion =
        globalThis.AppDateTime?.formatDateTime(new Date().toISOString()) || new Date().toISOString();
      const procedimientosFactura = Array.isArray(data.procedimientosFactura) ? data.procedimientosFactura : [];
      const soatResultados = Array.isArray(data.soatResultados) ? data.soatResultados : [];
      const qxAgentResults = Array.isArray(data.qxAgentResults) ? data.qxAgentResults : [];
      const hasQuirurgicoDocument = Boolean(data.hasQuirurgicoDocument);
      const diagnosticosPdf = diagnosticosSeleccionados
        .map((texto) => {
          const diag = app.diagnosticos.parseDiagnosticoParaPdf(texto, mapaCie10Canonico);
          const codigo = normalizePdfText(diag.codigo);
          const descripcion = normalizePdfText(diag.descripcion || texto);
          if (!codigo && !descripcion) return null;
          return { codigo, descripcion };
        })
        .filter(Boolean);
      const filasDiagnosticos = diagnosticosPdf.map((diag) => `
        <tr>
          <td>${app.utils.escapeHtml(diag.codigo)}</td>
          <td>${app.utils.escapeHtml(diag.descripcion)}</td>
        </tr>
      `).join("");

      const antecedentesPdf = antecedentes
        .map((item) => normalizePdfText(item))
        .filter(Boolean);
      const filasAntecedentes = antecedentesPdf.map((item) => `
        <tr>
          <td>${app.utils.escapeHtml(item)}</td>
        </tr>
      `).join("");

      const {
        items: recomendacionesPdf,
        rowsHtml: filasRecomendaciones,
      } = buildPdfRecommendationRows(recomendaciones);

      const procedimientosPdf = procedimientosSeleccionados
        .map((item) => {
          const value = normalizePdfText(item?.descripcion || item);
          const match = new RegExp(/^\s*(\d{6})\s*(?:[-:]|\s)\s*(.+)$/).exec(value);
          const codigo = normalizePdfText(item?.codigo_cups || (match ? match[1] : ""));
          const descripcion = match ? normalizePdfText(match[2]) : value;
          if (!codigo && !descripcion) return null;
          return {
            codigo,
            descripcion,
            clasificacion: item?.clasificacion || "sin_clasificar",
          };
        })
        .filter(Boolean);
      const renderProcedureRows = (classification) => procedimientosPdf
        .filter((item) => item.clasificacion === classification)
        .map((procedimiento) => `
        <tr>
          <td>${app.utils.escapeHtml(procedimiento.codigo)}</td>
          <td>${app.utils.escapeHtml(procedimiento.descripcion)}</td>
        </tr>
      `).join("");

      const medicamentosPdf = medicamentosSeleccionados
        .map((item) => {
          const med = app.medicamentos.parseMedicamentoParaPdf(item);
          const medicamentoMostrar = normalizePdfText(
            [med.medicamento, med.presentacion].filter(Boolean).join(" · ")
          );
          const codigoReferencia = normalizePdfText(med.codigo_referencia);
          const posologia = normalizePdfText(med.posologia);
          const dosis = normalizePdfText(med.dosis);
          const cantidad = normalizePdfText(med.cantidad);
          if (
            !codigoReferencia
            && !medicamentoMostrar
            && !posologia
            && !dosis
            && !cantidad
          ) return null;
          return {
            codigoReferencia,
            medicamentoMostrar,
            posologia,
            dosis,
            cantidad,
          };
        })
        .filter(Boolean);
      const filasMedicamentos = medicamentosPdf.map((med) => `
        <tr>
          <td>${app.utils.escapeHtml(med.codigoReferencia)}</td>
          <td>${app.utils.escapeHtml(med.medicamentoMostrar)}</td>
          <td>${app.utils.escapeHtml(med.posologia)}</td>
          <td>${app.utils.escapeHtml(med.dosis)}</td>
          <td>${app.utils.escapeHtml(med.cantidad)}</td>
        </tr>
      `).join("");

      const ayudasPdf = ayudasDiagnosticasSeleccionadas
        .map((ayuda) => {
          const tipo = normalizePdfText(app.ayudasDiagnosticas.etiquetaTipoAyuda(ayuda.tipo));
          const nombre = normalizePdfText(app.ayudasDiagnosticas.obtenerNombreExportable(ayuda));
          const interpretacion = normalizePdfText(app.ayudasDiagnosticas.obtenerInterpretacionExportable(ayuda));
          if (!tipo && !nombre && !interpretacion) return null;
          return { tipo, nombre, interpretacion };
        })
        .filter(Boolean);
      const filasAyudasDiagnosticas = ayudasPdf.map((ayuda) => `
        <tr>
          <td>${app.utils.escapeHtml(ayuda.tipo)}</td>
          <td>${app.utils.escapeHtml(ayuda.nombre)}</td>
          <td>${app.utils.escapeHtml(ayuda.interpretacion)}</td>
        </tr>
      `).join("");

      const hallazgosNormalizados = normalizePdfText(hallazgos);
      const descripcionQxNormalizada = normalizePdfText(descripcionQx);

      const seccionesContenido = [];
      const appendSection = ({ title, hasContent, renderContent }) => {
        if (!hasContent) return;
        seccionesContenido.push({
          title,
          contentHtml: renderContent(),
        });
      };

      appendSection({
        title: "Resumen Clínico",
        hasContent: hasMeaningfulText(resumen),
        renderContent: () => `<div class="text-block">${app.utils.escapeHtml(resumen)}</div>`,
      });

      appendSection({
        title: "Recomendaciones médicas",
        hasContent: recomendacionesPdf.length > 0,
        renderContent: () => renderPdfTableContent(
          ["Categoría", "Indicación", "Duración", "Fecha"],
          filasRecomendaciones,
        ),
      });

      appendSection({
        title: "Diagnósticos",
        hasContent: diagnosticosPdf.length > 0,
        renderContent: () => renderPdfTableContent(
          ["Código CIE-10", "Descripción CIE-10"],
          filasDiagnosticos,
        ),
      });

      appendSection({
        title: "Antecedentes",
        hasContent: antecedentesPdf.length > 0,
        renderContent: () => renderPdfTableContent(["Antecedente"], filasAntecedentes),
      });

      appendSection({
        title: "Procedimientos",
        hasContent: procedimientosPdf.length > 0 || hasMeaningfulText(hallazgosNormalizados) || hasMeaningfulText(descripcionQxNormalizada),
        renderContent: () => [
          ["quirurgico", "Quirúrgicos"],
          ["no_quirurgico", "No quirúrgicos"],
          ["sin_clasificar", "Sin clasificar"],
        ].map(([key, label]) => {
          const rows = renderProcedureRows(key);
          if (!rows && key !== "quirurgico") return "";
          const surgicalDetail = key === "quirurgico" ? `
            ${hasMeaningfulText(hallazgosNormalizados) ? `<p><strong>Hallazgos:</strong></p><div class="text-block">${app.utils.escapeHtml(hallazgosNormalizados)}</div>` : ""}
            ${hasMeaningfulText(descripcionQxNormalizada) ? `<p><strong>Descripción:</strong></p><div class="text-block">${app.utils.escapeHtml(descripcionQxNormalizada)}</div>` : ""}
          ` : "";
          if (!rows && !surgicalDetail.trim()) return "";
          return `<h3>${label}</h3>${rows ? renderPdfTableContent(["Código CUPS", "Procedimiento"], rows) : ""}${surgicalDetail}`;
        }).join(""),
      });

      appendSection({
        title: "Ayudas Diagnósticas",
        hasContent: ayudasPdf.length > 0,
        renderContent: () => renderPdfTableContent(
          ["Tipo", "Ayuda", "Interpretación"],
          filasAyudasDiagnosticas,
        ),
      });

      appendSection({
        title: "Medicamentos",
        hasContent: medicamentosPdf.length > 0,
        renderContent: () => renderPdfTableContent(
          ["CUM/IUM", "Medicamento", "Posología", "Dosis", "Cantidad"],
          filasMedicamentos,
        ),
      });

      const html = buildPdfEpicrisisHtml({
        nombrePaciente,
        fechaGeneracion,
        metadatos,
        seccionesContenido,
      });

      AppToast.progress(app.state.toastProcessIds.pdf, "Guardando PDF en caché...", {
        description: "Preparando versión para impresión.",
      });
      const cacheResult = await guardarPdfEpicrisisCache({
        nombre_paciente: nombrePaciente,
        case_key: app.selection.obtenerEpicrisisCaseKeyDesdeUrl(),
        epicrisis_base_id: app.selection.obtenerEpicrisisBaseIdDesdeUrl(),
        historia_id: data.historiaId || null,
        quirurgico_id: data.quirurgicoId || null,
        factura_id: data.facturaId || null,
        resumen_clinico: resumen,
        recomendaciones_medicas: recomendaciones,
        antecedentes: antecedentesPdf,
        hallazgos_quirurgicos: hallazgos,
        descripcion_procedimiento: descripcionQx,
        diagnosticos: diagnosticosSeleccionados,
        procedimientos: procedimientosSeleccionados,
        ayudas_diagnosticas: ayudasDiagnosticasSeleccionadas,
        imagenes_diagnosticas: imagenesDiagnosticasSeleccionadas,
        medicamentos: medicamentosSeleccionados,
        notas_medicas: {
          hallazgos_extra: hallazgosExtra,
          descripcion_procedimiento_extra: descripcionProcedimientoExtra,
        },
        html_pdf: html,
      });

      if (!cacheResult?.cache_id) {
        throw new Error("No se pudo guardar el PDF de epicrisis en cache");
      }

      const frame = crearIframeImpresion();
      state.activeFrame = frame;
      app.state.syncGlobals();

      frame.srcdoc = html;

      AppToast.progress(app.state.toastProcessIds.pdf, "Abriendo diálogo de impresión...", {
        description: "Confirma o cancela desde la ventana del navegador.",
        actionLabel: "Imprimir de nuevo",
        onAction: app.toast.actions.reintentarPdf,
      });
      await esperarIframeListoParaImprimir(frame);
      lanzarImpresionEnIframe(frame);
    } catch (error) {
      finalizarFlujoPdf("error");
      mostrarAlertaPdf(
        obtenerMensajeError(error, "No se pudo generar o imprimir el PDF de la epicrisis."),
      );
    }
  }

  app.pdf = {
    buildPdfRecommendationRows,
    buildPdfEpicrisisHtml,
    generarPdfEpicrisis,
    isMeaningfulFacturaProcedure,
  };

  global.generarPdfEpicrisis = generarPdfEpicrisis;
})(globalThis);
