# test-integration.R
# Pruebas de integracion que verifican flujos de trabajo completos

# ============================================================
# FLUJOS DE TRABAJO TIPICOS DE USUARIO
# ============================================================

test_that("flujo: buscar termino -> obtener codigos -> validar", {
  skip_on_cran()

  # 1. Usuario busca termino medico
  resultados_busqueda <- cie_search("diabetes mellitus", threshold = 0.70, max_results = 10)
  expect_gt(nrow(resultados_busqueda), 0)

  # 2. Obtiene codigos de los resultados
  codigos_encontrados <- resultados_busqueda$codigo

  # 3. Valida que los codigos son correctos
  validacion <- cie_validate_vector(codigos_encontrados)
  expect_true(all(validacion), info = "Todos los codigos de busqueda deben ser validos")

  # 4. Obtiene detalles de cada codigo
  detalles <- cie_lookup(codigos_encontrados)
  expect_equal(nrow(detalles), length(unique(codigos_encontrados)))
})

test_that("flujo: buscar categoria -> expandir -> calcular comorbilidad", {
  skip_on_cran()
  skip_if_not_installed("comorbidity")

  # 1. Buscar categoria general de diabetes
  resultado_e11 <- cie_lookup("E11")
  expect_equal(nrow(resultado_e11), 1)

  # 2. Expandir para obtener todos los subcigodos
  hijos_e11 <- cie_expand("E11")
  expect_gt(length(hijos_e11), 5)

  # 3. Crear datos de paciente simulados
  set.seed(123)
  datos_pacientes <- data.frame(
    id_paciente = rep(1:5, each = 2),
    codigo_cie = sample(hijos_e11, 10, replace = TRUE)
  )

  # 4. Calcular comorbilidades
  resultado_comorbid <- cie_comorbid(
    datos_pacientes,
    id = "id_paciente",
    code = "codigo_cie",
    map = "charlson"
  )

  expect_s3_class(resultado_comorbid, "tbl_df")
  expect_true("score_charlson" %in% names(resultado_comorbid))
})

test_that("flujo: normalizar codigos -> buscar -> mapear comorbilidad", {
  skip_on_cran()

  # 1. Codigos en formato mixto (como vendrian de datos reales)
  codigos_raw <- c("E110", "I509", "C509", "e11.0", " Z00 ")

  # 2. Normalizar codigos
  codigos_norm <- cie_normalizar(codigos_raw, buscar_db = FALSE)
  expect_equal(length(codigos_norm), length(codigos_raw))

  # 3. Buscar detalles
  suppressMessages({
    detalles <- cie_lookup(codigos_norm)
  })
  expect_gt(nrow(detalles), 0)

  # 4. Mapear a categorias de comorbilidad
  mapa <- cie_map_comorbid(codigos_norm)
  expect_equal(nrow(mapa), length(codigos_norm))
  expect_true("Diabetes" %in% mapa$categoria)
})

test_that("flujo: SQL personalizado -> procesamiento -> validacion", {
  skip_on_cran()

  # 1. Query SQL personalizada para obtener codigos por capitulo
  codigos_cap4 <- cie10_sql("
    SELECT codigo, descripcion
    FROM cie10
    WHERE codigo LIKE 'E%'
    LIMIT 50
  ")
  expect_s3_class(codigos_cap4, "tbl_df")
  expect_gt(nrow(codigos_cap4), 0)

  # 2. Validar codigos obtenidos
  validacion <- cie_validate_vector(codigos_cap4$codigo)
  expect_true(all(validacion))

  # 3. Obtener detalles completos
  detalles <- cie_lookup(codigos_cap4$codigo, descripcion_completa = TRUE)
  expect_true("descripcion_completa" %in% names(detalles))
})

# ============================================================
# PRUEBAS DE CONSISTENCIA ENTRE FUNCIONES
# ============================================================

test_that("cie_lookup y cie10_sql retornan mismos datos", {
  skip_on_cran()

  # Obtener codigo via cie_lookup
  resultado_lookup <- cie_lookup("E11.0")

  # Obtener mismo codigo via SQL directo
  resultado_sql <- cie10_sql("SELECT * FROM cie10 WHERE codigo = 'E11.0'")

  # Deben ser equivalentes
  expect_equal(resultado_lookup$codigo, resultado_sql$codigo)
  expect_equal(resultado_lookup$descripcion, resultado_sql$descripcion)
})

test_that("cie_expand y cie_lookup expandir dan mismos resultados", {
  skip_on_cran()

  # Via cie_expand
  hijos_expand <- cie_expand("E11")

  # Via cie_lookup con expandir=TRUE
  hijos_lookup <- cie_lookup("E11", expandir = TRUE)$codigo

  # Deben ser iguales
  expect_setequal(hijos_expand, hijos_lookup)
})

test_that("cie_normalizar y cie_lookup son coherentes", {
  skip_on_cran()

  # Codigo sin punto
  codigo_raw <- "E110"

  # Normalizar
  codigo_norm <- cie_normalizar(codigo_raw, buscar_db = FALSE)
  expect_equal(codigo_norm, "E11.0")

  # Buscar con codigo raw y normalizado
  resultado_raw <- cie_lookup(codigo_raw)
  resultado_norm <- cie_lookup(codigo_norm)

  # Deben dar mismo resultado
  expect_equal(resultado_raw$codigo, resultado_norm$codigo)
})

test_that("cie_validate_vector y cie_lookup son coherentes", {
  skip_on_cran()

  codigos <- c("E11.0", "INVALIDO", "Z00")

  # Validar
  validacion <- cie_validate_vector(codigos)
  expect_equal(validacion, c(TRUE, FALSE, TRUE))

  # Buscar (solo los validos deben retornar)
  suppressMessages({
    resultado <- cie_lookup(codigos)
  })

  # Solo codigos validos en resultado
  expect_true(all(resultado$codigo %in% codigos[validacion]))
})

# ============================================================
# PRUEBAS DE ESCENARIOS REALES
# ============================================================

test_that("escenario: analisis de egreso hospitalario", {
  skip_on_cran()
  skip_if_not_installed("comorbidity")

  # Simular datos de egresos hospitalarios
  set.seed(42)
  egresos <- data.frame(
    id_egreso = 1:20,
    codigo_principal = sample(c("E11.0", "I50.9", "C50.9", "J18.9", "K70.3"), 20, replace = TRUE),
    codigo_secundario = sample(c("E11.9", "I10", "Z86.7", "N18.9", "F32.9"), 20, replace = TRUE)
  )

  # 1. Validar codigos principales
  val_principal <- cie_validate_vector(egresos$codigo_principal)
  expect_true(all(val_principal))

  # 2. Validar codigos secundarios
  val_secundario <- cie_validate_vector(egresos$codigo_secundario)
  expect_true(all(val_secundario))

  # 3. Preparar datos para comorbilidad (formato largo)
  datos_largo <- data.frame(
    id = rep(egresos$id_egreso, 2),
    codigo = c(egresos$codigo_principal, egresos$codigo_secundario)
  )

  # 4. Calcular comorbilidades
  comorbilidades <- cie_comorbid(datos_largo, id = "id", code = "codigo", map = "charlson")
  expect_s3_class(comorbilidades, "tbl_df")
  expect_equal(nrow(comorbilidades), 20)
})

test_that("escenario: busqueda de codigos para estudio", {
  skip_on_cran()

  # Investigador busca codigos relacionados con su estudio
  # 1. Buscar por termino
  resultados_diabetes <- cie_search("diabetes mellitus tipo 2", threshold = 0.65)

  # 2. Buscar categoria general
  categoria_e11 <- cie_lookup("E11", expandir = TRUE)

  # 3. Combinar resultados
  codigos_estudio <- unique(c(resultados_diabetes$codigo, categoria_e11$codigo))

  # 4. Validar todos
  validacion <- cie_validate_vector(codigos_estudio)
  expect_true(all(validacion))

  # 5. Obtener descripciones completas
  descripciones <- cie_lookup(codigos_estudio, descripcion_completa = TRUE)
  expect_true("descripcion_completa" %in% names(descripciones))
})

test_that("escenario: limpieza de datos con codigos sucios", {
  skip_on_cran()

  # Datos con codigos en diferentes formatos (escenario real)
  codigos_sucios <- c(
    "E110",     # Sin punto - valido
    "e11.0",    # Minusculas - valido
    " E11.0 ",  # Espacios - valido (se normaliza con trim)
    "E11",      # Categoria - valido
    "DIABETES", # Texto (invalido)
    NA,         # NA - invalido
    "",         # Vacio - invalido
    "E11.0"     # Correcto - valido
  )

  # 1. Validar formato (normaliza internamente: trim, punto, sufijo X)
  formato_ok <- cie_validate_vector(codigos_sucios)
  expect_equal(formato_ok, c(TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, TRUE))

  # 2. Filtrar solo validos
  codigos_validos <- codigos_sucios[formato_ok]

  # 3. Normalizar
  codigos_norm <- cie_normalizar(codigos_validos, buscar_db = FALSE)

  # 4. Buscar en base
  suppressMessages({
    resultado <- cie_lookup(codigos_norm)
  })
  expect_gt(nrow(resultado), 0)
})

# ============================================================
# PRUEBAS DE RENDIMIENTO BASICAS
# ============================================================

test_that("busquedas multiples son razonablemente rapidas", {
  skip_on_cran()

  # Medir tiempo de 100 busquedas simples
  tiempo_inicio <- Sys.time()

  for (i in 1:100) {
    resultado <- cie_lookup("E11.0")
  }

  tiempo_fin <- Sys.time()
  duracion <- as.numeric(difftime(tiempo_fin, tiempo_inicio, units = "secs"))

  # Debe completar en menos de 30 segundos (muy generoso)
  expect_lt(duracion, 30)
})

test_that("validacion de vector grande es rapida", {
  skip_on_cran()

  # Vector de 10000 codigos
  codigos <- rep(c("E11.0", "Z00", "INVALIDO"), 3333)
  codigos <- c(codigos, "E11.0")  # Para llegar a 10000

  tiempo_inicio <- Sys.time()
  resultado <- cie_validate_vector(codigos)
  tiempo_fin <- Sys.time()

  duracion <- as.numeric(difftime(tiempo_fin, tiempo_inicio, units = "secs"))

  # Debe completar en menos de 5 segundos
  expect_lt(duracion, 5)
  expect_length(resultado, 10000)
})

# ============================================================
# PRUEBAS DE INTEROPERABILIDAD CON dplyr
# ============================================================

test_that("resultados funcionan con dplyr::filter", {
  skip_on_cran()

  resultado <- cie_search("diabetes", threshold = 0.70, max_results = 20)

  # Filtrar con dplyr
  filtrado <- resultado %>%
    dplyr::filter(score > 0.80)

  expect_s3_class(filtrado, "tbl_df")
})

test_that("resultados funcionan con dplyr::mutate", {
  skip_on_cran()

  resultado <- cie_lookup(c("E11.0", "E11.1", "E11.2"))

  # Mutar con dplyr
  mutado <- resultado %>%
    dplyr::mutate(codigo_corto = substr(codigo, 1, 3))

  expect_s3_class(mutado, "tbl_df")
  expect_true("codigo_corto" %in% names(mutado))
})

test_that("resultados funcionan con dplyr::group_by y summarise", {
  skip_on_cran()

  resultado <- cie10_sql("SELECT codigo, capitulo FROM cie10 WHERE codigo LIKE 'E1%' LIMIT 100")

  # Agrupar y resumir
  resumen <- resultado %>%
    dplyr::group_by(capitulo) %>%
    dplyr::summarise(n = dplyr::n(), .groups = "drop")

  expect_s3_class(resumen, "tbl_df")
  expect_true("n" %in% names(resumen))
})

# ============================================================
# PRUEBAS DE API CIE-11 (SOLO SI HAY CREDENCIALES)
# ============================================================

test_that("cie11_search falla gracefully sin credenciales", {
  skip_on_cran()
  skip_if_not_installed("httr2")

  # Limpiar credenciales temporalmente
  old_key <- Sys.getenv("ICD_API_KEY")
  Sys.unsetenv("ICD_API_KEY")
  on.exit(if (nchar(old_key) > 0) Sys.setenv(ICD_API_KEY = old_key))

  # Sin credenciales, debe dar error informativo
  expect_error(
    cie11_search("diabetes"),
    regexp = "API key|requerida|OMS",
    ignore.case = TRUE
  )
})
