(function (global) {
  const app = (global.EpicrisisApp = global.EpicrisisApp || {});

  const MEDICAMENTO_GENERIC_CODES = new Set(["2801"]);
  const MEDICAMENTO_PLACEHOLDER_PATTERN = /\[(?:dato|desc|valor|med|dosis|cant|unit|total|cups|tipo|dias|tarifa|nombre|observaci[oó]n)\]/gi;
  const MEDICAMENTO_ITEM_KEYWORDS = {
    insumo_material: ["sutura", "curacion", "curación", "gasa", "vendaje", "ferula", "férula", "yeso", "algodon", "algodón", "material", "aguja"],
    equipo_dispositivo: ["cateter", "catéter", "abocat", "buretrol", "equipo", "venoclisis", "jeringa", "sonda", "canula", "cánula", "dispositivo"],
    solucion: ["solucion", "solución", "cloruro de sodio", "solucion salina", "solución salina", "lactato de ringer", "dextrosa"],
  };

  function sanitizarPlaceholderMedicamento(value) {
    const texto = String(value || "").trim();
    if (!texto) return "";
    const limpio = texto.replace(MEDICAMENTO_PLACEHOLDER_PATTERN, "").trim().replace(/^[-:/\s]+|[-:/\s]+$/g, "");
    if (!limpio || ["no disponible", "n/a", "none"].includes(limpio.toLowerCase())) return "";
    return limpio;
  }

  function clasificarTipoItemMedicamento(nombre, fuente) {
    const nombreNorm = sanitizarPlaceholderMedicamento(nombre).toLowerCase();
    if (!nombreNorm) return { tipo_item: "desconocido", tipo_item_origen: "sin_nombre" };
    for (const [tipo, keywords] of Object.entries(MEDICAMENTO_ITEM_KEYWORDS)) {
      if (keywords.some((keyword) => nombreNorm.includes(keyword))) {
        return { tipo_item: tipo, tipo_item_origen: "heuristica_keyword" };
      }
    }
    return {
      tipo_item: "medicamento",
      tipo_item_origen: fuente === "factura" ? "fallback_factura" : "fallback_clinico",
    };
  }

  function separarNombreYDosisEnTexto(texto) {
    const limpio = sanitizarPlaceholderMedicamento(texto);
    if (!limpio) return { medicamento: "", dosis: "" };
    const match = new RegExp(/\d/).exec(limpio);
    if (!match) return { medicamento: limpio, dosis: "" };
    const idx = match.index || 0;
    const medicamento = limpio.slice(0, idx).trim().replace(/^[-:/\s]+|[-:/\s]+$/g, "");
    const dosis = limpio.slice(idx).trim().replace(/^[-:/\s]+|[-:/\s]+$/g, "");
    if (!medicamento) return { medicamento: limpio, dosis: "" };
    return { medicamento, dosis };
  }

  function resolverEstadoCodigoMedicamento(codigo, codigoTipo, fuente) {
    const codigoNorm = String(codigo || "").trim().toUpperCase();
    if (!codigoNorm) return "no_identificado";
    if (MEDICAMENTO_GENERIC_CODES.has(codigoNorm)) return "pendiente_validacion";
    if (String(fuente || "").trim() === "factura" && codigoTipo !== "medicamento") {
      return "pendiente_validacion";
    }
    return "identificado";
  }

  function normalizarMedicamentoCanonico(item, fuentePredeterminada = "historia_clinica") {
    const fuente = String(fuentePredeterminada || "desconocida").trim() || "desconocida";
    let codigo = "";
    let medicamento = "";
    let dosis = "";
    let cantidad = "";
    let codigoTipo = "desconocido";
    let codigoOrigen = "desconocido";
    let textoOriginal = "";
    let tipoItem = "desconocido";
    let tipoItemOrigen = "desconocido";
    let key = "";
    let presentacion = "";
    let posologia = "";
    let estados = [];
    let codigoFacturacion = "";
    let codigoReferencia = "";
    let discrepanciaCantidad = false;
    let asociacionFactura = "no_evaluable";
    let cantidadClinica = null;
    let cantidadFacturada = null;
    let unidadCantidad = "";
    let pertinencia = null;

    if (item && typeof item === "object" && !Array.isArray(item)) {
      codigo = String(
        item?.codigo_medicamento ||
        item?.cod_medicamento ||
        item?.codigo ||
        ""
      ).trim();
      medicamento = sanitizarPlaceholderMedicamento(item?.medicamento || item?.nombre || item?.detalle);
      dosis = sanitizarPlaceholderMedicamento(item?.dosis);
      cantidad = sanitizarPlaceholderMedicamento(item?.cantidad || item?.cant);
      textoOriginal = sanitizarPlaceholderMedicamento(item?.texto_original);
      codigoTipo = String(item?.codigo_tipo || "").trim() || "desconocido";
      codigoOrigen = String(item?.codigo_origen || "").trim() || "desconocido";
      tipoItem = String(item?.tipo_item || "").trim() || "desconocido";
      tipoItemOrigen = String(item?.tipo_item_origen || "").trim() || "desconocido";
      key = String(item?.key || "").trim();
      presentacion = sanitizarPlaceholderMedicamento(item?.presentacion);
      posologia = sanitizarPlaceholderMedicamento(
        item?.posologia || [item?.via, item?.frecuencia, item?.duracion].filter(Boolean).join(" ")
      );
      estados = (Array.isArray(item?.estados) ? item.estados : [item?.estado_caso || item?.tipo_uso])
        .map((status) => String(status || "").trim().toLowerCase())
        .filter(Boolean);
      codigoFacturacion = String(item?.codigo_facturacion || "").trim();
      codigoReferencia = String(item?.codigo_referencia || "").trim();
      discrepanciaCantidad = Boolean(item?.discrepancia_cantidad);
      asociacionFactura = String(item?.asociacion_factura || "no_evaluable").trim();
      cantidadClinica = item?.cantidad_clinica ?? null;
      cantidadFacturada = item?.cantidad_facturada ?? null;
      unidadCantidad = String(item?.unidad_cantidad || "").trim();
      pertinencia = item?.pertinencia && typeof item.pertinencia === "object"
        ? {
            estado: String(item.pertinencia.estado || "").trim().toLowerCase(),
            cadena: item.pertinencia.cadena && typeof item.pertinencia.cadena === "object"
              ? { ...item.pertinencia.cadena }
              : {},
            comparacion_cantidad: item.pertinencia.comparacion_cantidad
              && typeof item.pertinencia.comparacion_cantidad === "object"
              ? { ...item.pertinencia.comparacion_cantidad }
              : {},
            motivos: Array.isArray(item.pertinencia.motivos) ? item.pertinencia.motivos.map(String) : [],
            fuentes: Array.isArray(item.pertinencia.fuentes) ? item.pertinencia.fuentes.map(String) : [],
            evidencias: Array.isArray(item.pertinencia.evidencias) ? item.pertinencia.evidencias.map(String) : [],
            justificacion_clinica: String(item.pertinencia.justificacion_clinica || "").trim(),
          }
        : null;

      if (!codigoTipo || codigoTipo === "desconocido") {
        if (String(item?.codigo_medicamento || item?.cod_medicamento || "").trim()) {
          codigoTipo = "medicamento";
          codigoOrigen = "codigo_medicamento";
        } else if (String(item?.codigo || "").trim()) {
          codigoTipo = fuente === "factura" ? "facturacion" : "medicamento";
          codigoOrigen = "codigo";
        }
      }
    } else {
      textoOriginal = sanitizarPlaceholderMedicamento(app.utils.stripHtml(item).replace(/\s+/g, " ").trim());
      const manualParts = textoOriginal.split("|").map((part) => part.trim());
      if (manualParts.length >= 5) {
        [medicamento, posologia, dosis, cantidad] = manualParts;
        estados = manualParts.slice(4).join("|").split(/[·,]/).map((status) => status.trim().toLowerCase()).filter(Boolean);
      }
      const partes = textoOriginal.split(/\s*-\s*/).map((p) => p.trim()).filter(Boolean);
      const esCodigo = (value) => /^[A-Z0-9]{2,12}$/i.test(String(value || "").trim());
      const tieneCodigo = Boolean(partes[0] && esCodigo(partes[0]));

      if (manualParts.length >= 5) {
        // El formato explícito evita inferencias por posición.
      } else if (partes.length >= 4 && tieneCodigo) {
        codigo = partes[0];
        cantidad = partes[1] || "";
        medicamento = partes[2] || "";
        dosis = partes.slice(3).join(" - ");
      } else if (partes.length >= 3 && tieneCodigo) {
        codigo = partes[0];
        medicamento = partes[1] || "";
        dosis = partes[2] || "";
        cantidad = partes[3] || "";
      } else if (partes.length >= 3) {
        cantidad = partes[0] || "";
        medicamento = partes[1] || partes[0] || "";
        dosis = partes.slice(2).join(" - ");
      } else if (partes.length === 2) {
        if (esCodigo(partes[0])) {
          codigo = partes[0];
          medicamento = partes[1] || "";
        } else {
          medicamento = partes[0] || "";
          dosis = partes[1] || "";
        }
      } else if (partes.length === 1) {
        const split = separarNombreYDosisEnTexto(partes[0]);
        medicamento = split.medicamento;
        dosis = split.dosis;
      }

      if (codigo) {
        codigoTipo = ["historia_clinica", "manual", "cache_pdf"].includes(fuente)
          ? "medicamento"
          : "desconocido";
        codigoOrigen = "texto";
      }
    }

    const codigoEstado = String(item?.codigo_estado || "").trim()
      || resolverEstadoCodigoMedicamento(codigo, codigoTipo, fuente);
    if (!tipoItem || tipoItem === "desconocido") {
      const clasificacion = clasificarTipoItemMedicamento(medicamento || textoOriginal, fuente);
      tipoItem = clasificacion.tipo_item;
      tipoItemOrigen = clasificacion.tipo_item_origen;
    }
    const codigoNormalizado = codigoEstado === "identificado" ? codigo : (codigoEstado === "pendiente_validacion" ? codigo : "");

    return {
      codigo: codigoNormalizado,
      codigo_estado: codigoEstado,
      codigo_tipo: codigoTipo || "desconocido",
      codigo_origen: codigoOrigen || "desconocido",
      medicamento: medicamento || "",
      dosis: dosis || "",
      cantidad: cantidad || "",
      fuente: String(item?.fuente || fuente || "desconocida").trim() || "desconocida",
      texto_original: textoOriginal || medicamento || "",
      tipo_item: tipoItem,
      tipo_item_origen: tipoItemOrigen,
      key,
      nombre: medicamento || "",
      presentacion,
      posologia,
      estados: [...new Set(estados)],
      codigo_facturacion: codigoFacturacion,
      codigo_referencia: codigoReferencia,
      discrepancia_cantidad: discrepanciaCantidad,
      asociacion_factura: asociacionFactura,
      cantidad_clinica: cantidadClinica,
      cantidad_facturada: cantidadFacturada,
      unidad_cantidad: unidadCantidad,
      pertinencia,
    };
  }

  function obtenerCodigoMedicamentoVisible(item) {
    const med = normalizarMedicamentoCanonico(item);
    return med.codigo_estado === "identificado" && med.codigo
      ? med.codigo
      : "Código no identificado";
  }

  function obtenerEtiquetaPrincipalMedicamento(item) {
    const med = normalizarMedicamentoCanonico(item);
    if (med.tipo_item === "insumo_material") {
      return `INSUMO / MATERIAL${med.medicamento ? ` - ${med.medicamento}` : ""}`;
    }
    if (med.tipo_item === "equipo_dispositivo") {
      return `EQUIPO / DISPOSITIVO${med.medicamento ? ` - ${med.medicamento}` : ""}`;
    }
    return `${obtenerCodigoMedicamentoVisible(med)}${med.medicamento ? ` - ${med.medicamento}` : ""}`;
  }

  function formatearMedicamentoPresentacion(item) {
    const med = normalizarMedicamentoCanonico(item);
    const estados = med.estados.map((status) => status.replaceAll("_", " ")).join(" · ");
    return [med.medicamento, med.posologia, med.dosis, med.cantidad, estados].filter((value) => value !== "" && value != null).join(" - ");
  }

  function parseMedicamentoParaPdf(item) {
    return normalizarMedicamentoCanonico(item);
  }

  function normalizarMedicamentos(lista, fuentePredeterminada = "historia_clinica") {
    return (lista || [])
      .map((item) => normalizarMedicamentoCanonico(item, fuentePredeterminada))
      .filter((m) => m.medicamento || m.dosis || m.cantidad);
  }

  function deduplicarMedicamentos(items) {
    const seen = new Set();
    const out = [];
    (items || []).forEach((raw) => {
      const med = normalizarMedicamentoCanonico(raw);
      const key = [
        med.key || "",
        med.medicamento || "",
        med.presentacion || "",
        med.posologia || "",
        med.dosis || "",
        med.cantidad || "",
      ].join("|").toLowerCase();
      if (!key.replace(/\|/g, "").trim() || seen.has(key)) return;
      seen.add(key);
      out.push(med);
    });
    return out;
  }

  app.medicamentos = {
    sanitizarPlaceholderMedicamento,
    clasificarTipoItemMedicamento,
    separarNombreYDosisEnTexto,
    resolverEstadoCodigoMedicamento,
    normalizarMedicamentoCanonico,
    obtenerCodigoMedicamentoVisible,
    obtenerEtiquetaPrincipalMedicamento,
    formatearMedicamentoPresentacion,
    parseMedicamentoParaPdf,
    normalizarMedicamentos,
    deduplicarMedicamentos,
  };
})(globalThis);
