#' @title SEQIC Indicator 10 – Trauma Team Activation Appropriateness
#'
#' @description
#'
#' `r lifecycle::badge("experimental")`
#'
#' Calculates three trauma system quality indicators related to trauma team
#' activations where the patient was kept at the facility:
#' \itemize{
#'   \item 10a: Proportion of patients meeting triage criteria (based on Injury
#'   Severity Score or Need For Trauma Intervention) who received low-level
#'   or no activation (undertriage).
#'   \item 10b: Proportion of patients not meeting triage criteria who received
#'   highest-level trauma activation (overtriage).
#'   \item 10c: Proportion of major trauma patients receiving a full activation
#'   (undertriage via Peng & Xiang, 2019).
#'
#'   (10a, 10b, 10c can be based on Injury Severity Score or Need For Trauma
#'   Intervention based on user choice)
#' }
#'
#' Users may stratify results by one or more grouping variables and optionally
#' compute confidence intervals.
#'
#' @inheritParams seqic_indicator_1
#' @inheritParams seqic_indicator_6
#' @inheritParams seqic_indicator_9
#'
#' @param trauma_team_activation_level Column indicating the trauma team
#'   activation level (e.g., `"Level 1"`, `"Level 2"`, `"Level 3"`,
#'   `"Consultation"`). Must be character or factor.
#' @param iss Optional numeric column representing the Injury Severity Score.
#' @param nfti Optional column indicating Need For Trauma Intervention
#'   classification of positive or negative. Should be character, factor, or
#'   logical.
#' @param groups Optional character vector of column names used for grouping
#'   results.
#' @param calculate_ci Optional; if not `NULL`, must be `"wilson"` or
#'   `"clopper-pearson"` to compute confidence intervals.
#'
#' @inheritDotParams nemsqar::nemsqa_binomial_confint conf.level correct
#'
#' @details
#' This function:
#' \itemize{
#'   \item Restricts analysis to Level I–IV trauma centers.
#'   \item Removes duplicate incidents using `unique_incident_id`.
#'   \item Classifies each record as meeting or not meeting triage criteria
#'   based on ISS or NFTI logic.
#'   \item Optionally computes 95% confidence intervals for each indicator.
#' }
#'
#' Users must ensure appropriate column names are passed and data is
#' pre-processed to include the necessary fields without missing critical
#' identifiers or timestamps.
#'
#' @returns A list of two tibbles with counts and proportions for SEQIC
#'   Indicators 10a, 10b, and 10c, along with model diagnostics for the Cribari
#'   or NFTI ouputs.  The proportions in 10a, 10b, and 10c will optionally
#'   include 95% confidence intervals.
#'
#' @examples
#' # Packages
#' library(dplyr)
#' library(traumar)
#'
#' # Simulated data for SEQIC Indicator 10
#' test_data <- tibble::tibble(
#'   id = as.character(1:12),
#'   trauma_level = c("I", "II", "III", "IV", "II", "I", "IV", "III", "II", "I",
#'   "III", "IV"),
#'   activation = c("Level 1", "Level 2", "None", "Consultation", "Level 1",
#'   "Level 1", "None", "Level 3", "Level 1", "Consultation", "None", "Level
#'   2"),
#'   acute_transfer = rep("No", 12),
#'   iss = c(25, 10, 16, 8, 30, 45, 12, 9, 28, 6, 17, 14),
#'   nfti = c(TRUE, FALSE, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, TRUE, FALSE,
#'   TRUE, TRUE),
#'   region = rep(c("East", "West"), each = 6)
#' )
#'
#' # Run the function, this will succeed
#' traumar::seqic_indicator_10(
#'   data = test_data,
#'   level = trauma_level,
#'   included_levels = c("I", "II", "III", "IV"),
#'   unique_incident_id = id,
#'   transfer_out_indicator = acute_transfer,
#'   trauma_team_activation_level = activation,
#'   iss = iss,
#'   nfti = NULL,
#'   groups = "region",
#'   calculate_ci = "wilson"
#' )
#'
#' # Run the function, this will fail
#' # Only one of `iss` or `nfti` arguments can be passed, not both
#' try(
#'   traumar::seqic_indicator_10(
#'   data = test_data,
#'   level = trauma_level,
#'   included_levels = c("I", "II", "III", "IV"),
#'   unique_incident_id = id,
#'   transfer_out_indicator = acute_transfer,
#'   trauma_team_activation_level = activation,
#'   iss = iss,
#'   nfti = nfti,
#'   groups = "region",
#'   calculate_ci = "wilson"
#' ))
#'
#' @references
#'
#' Beam G, Gorman K, Nannapaneni S, Zipf J, Simunich T, et al. (2022) Need for
#' Trauma Intervention and Improving Under-Triaging in Geriatric Trauma
#' Patients: undertriaged or Misclassified. Int J Crit Care Emerg Med 8:136.
#' doi.org/10.23937/2474-3674/1510136
#'
#' Peng J, Xiang H. Trauma undertriage and overtriage rates: are we using the
#' wrong formulas? Am J Emerg Med. 2016 Nov;34(11):2191-2192. doi:
#' 10.1016/j.ajem.2016.08.061. Epub 2016 Aug 31. PMID: 27615156; PMCID:
#' PMC6469681.
#'
#' Roden-Foreman JW, Rapier NR, Yelverton L, Foreman ML. Asking a Better
#' Question: Development and Evaluation of the Need For Trauma Intervention
#' (NFTI) Metric as a Novel Indicator of Major Trauma. J Trauma Nurs. 2017
#' May/Jun;24(3):150-157. doi: 10.1097/JTN.0000000000000283. PMID: 28486318.
#'
#' @author Nicolas Foss, Ed.D., MS
#'
#' @export
#'
seqic_indicator_10 <- function(
  data,
  level,
  included_levels = c("I", "II", "III", "IV"),
  unique_incident_id,
  transfer_out_indicator,
  trauma_team_activation_level,
  iss,
  nfti,
  groups = NULL,
  calculate_ci = NULL,
  ...
) {
  ###___________________________________________________________________________
  ### Data validation ----
  ###___________________________________________________________________________

  # Validate if `data` is a data frame or tibble. ----
  validate_data_structure(
    input = data,
    structure_type = c("data.frame", "tbl", "tbl_df"),
    type = "error"
  )

  # make the `level` column accessible for validation ----
  level_check <- validate_data_pull(
    input = data,
    type = "error",
    col = {{ level }},
    var_name = "level"
  )

  # validate `level` ----
  validate_character_factor(
    input = level_check,
    type = "error",
    var_name = "level"
  )

  # make the `unique_incident_id` column accessible for validation ----
  unique_incident_id_check <- validate_data_pull(
    input = data,
    type = "error",
    col = {{ unique_incident_id }},
    var_name = "unique_incident_id"
  )

  # Validate `unique_incident_id` ----
  validate_class(
    input = unique_incident_id_check,
    class_type = c("numeric", "integer", "character", "factor"),
    logic = "or",
    type = "error",
    var_name = "unique_incident_id"
  )

  # Ensure that `transfer_out_indicator` can be validated  ----
  transfer_out_indicator_check <- validate_data_pull(
    input = data,
    col = {{ transfer_out_indicator }},
    type = "error",
    var_name = "transfer_out_indicator"
  )

  # Validate `transfer_out_indicator` ----
  validate_class(
    input = transfer_out_indicator_check,
    class_type = c("logical", "character", "factor"),
    logic = "or",
    type = "error",
    var_name = "transfer_out_indicator"
  )

  # Ensure that `trauma_team_activation_level` can be validated ----
  trauma_team_activation_level_check <- validate_data_pull(
    input = data,
    col = {{ trauma_team_activation_level }},
    type = "error",
    var_name = "trauma_team_activation_level"
  )

  # Validate that `trauma_team_activation_level` ----
  validate_character_factor(
    input = trauma_team_activation_level_check,
    type = "error",
    var_name = "trauma_team_activation_level"
  )

  # Validate that `iss` is numeric. ----
  if (!rlang::quo_is_null(rlang::enquo(iss))) {
    # Ensure that `iss` can be validated ----
    iss_check <- validate_data_pull(
      input = data,
      col = {{ iss }},
      type = "error",
      var_name = "iss"
    )

    # Validation of `iss` ----
    validate_numeric(
      input = iss_check,
      min = 0,
      max = 75,
      type = "error",
      var_name = "iss"
    )
  }

  # Validate that `nfti` is character, factor, or logical.
  if (!rlang::quo_is_null(rlang::enquo(nfti))) {
    # Ensure that `nfti` can be validated
    nfti_check <- validate_data_pull(
      input = data,
      col = {{ nfti }},
      type = "error",
      var_name = "nfti"
    )

    # Validate `nfti`
    validate_class(
      input = nfti_check,
      class_type = c("character", "factor", "logical"),
      logic = "or",
      type = "error",
      var_name = "nfti"
    )
  }

  # Check if all elements in groups are strings (i.e., character vectors)
  validate_character_factor(input = groups, type = "error", null_ok = TRUE)

  # Check if all groups exist in the `data`
  validate_names(
    input = data,
    check_names = groups,
    type = "error",
    var_name = "groups",
    null_ok = TRUE
  )

  # Validate the `calculate_ci` argument ----
  calculate_ci <- validate_choice(
    input = calculate_ci,
    choices = c("wilson", "clopper-pearson"),
    several.ok = FALSE,
    type = "error",
    null_ok = TRUE,
    var_name = "calculate_ci"
  )

  # Validate the `included_levels` argument ----
  validate_class(
    input = included_levels,
    class_type = c("numeric", "character", "factor", "integer"),
    type = "error",
    logic = "or"
  )

  ###___________________________________________________________________________
  ### Data preparation ----
  ###___________________________________________________________________________

  # Preprocess the input dataset: ----
  # - Remove duplicate incidents to avoid double-counting (based on unique
  # incident ID)
  # - Restrict analysis to Level I-IV trauma centers
  # - Exclude transfers out (e.g., for whom definitive triage data may be
  # incomplete)

  data_prep <- data |>
    dplyr::distinct({{ unique_incident_id }}, .keep_all = TRUE) |>
    dplyr::filter(
      {{ level }} %in% included_levels,
      {{ transfer_out_indicator }} %in% c("No", FALSE)
    ) |>
    dplyr::mutate(
      # Define cases with highest-level activation (Level 1)
      full_activation = grepl(
        pattern = "level 1",
        x = {{ trauma_team_activation_level }},
        ignore.case = TRUE
      ),

      # Define low activation: ----
      # - Activation not recorded; assume no activation called
      # - Any level other than "Level 1"
      limited_no_activation = is.na({{ trauma_team_activation_level }}) |
        !grepl(
          pattern = "level 1",
          x = {{ trauma_team_activation_level }},
          ignore.case = TRUE
        )
    )

  # Dynamically classify patients using either ISS or NFTI logic ----
  if (
    !rlang::quo_is_null(rlang::enquo(iss)) &&
      rlang::quo_is_null(rlang::enquo(nfti))
  ) {
    data_prep <- data_prep |>
      dplyr::mutate(
        # Patients who should have had activation based on Cribari ISS > 15
        major_trauma = {{ iss }} > 15,
        # Patients who clearly did not require activation (ISS < 9)
        minor_trauma = {{ iss }} < 9,
        # Over triage = full activation and minor trauma
        overtriage = full_activation & minor_trauma,
        # Under triage = limited-to-no activation and major trauma
        undertriage = limited_no_activation & major_trauma
      )
  } else if (
    rlang::quo_is_null(rlang::enquo(iss)) &&
      !rlang::quo_is_null(rlang::enquo(nfti))
  ) {
    data_prep <- data_prep |>
      dplyr::mutate(
        # Patients flagged by NFTI as needing activation
        major_trauma = {{ nfti }} %in% c("Positive", TRUE, "Yes"),
        # Patients flagged by NFTI as NOT needing activation
        minor_trauma = {{ nfti }} %in% c("Negative", FALSE, "No"),
        # Over triage = full activation and minor trauma
        overtriage = full_activation & minor_trauma,
        # Under triage = limited-to-no activation and major trauma
        undertriage = limited_no_activation & major_trauma
      )
  } else {
    # Fail clearly if both or neither triage criteria are supplied
    cli::cli_abort(
      "Please supply exactly one of {.var iss} or {.var nfti}."
    )
  }

  # Get an identifier of how the triage classification was performed ----
  # Determine triage logic source as a scalar
  triage_logic_source <- if (
    !rlang::quo_is_null(rlang::enquo(nfti)) &&
      rlang::quo_is_null(rlang::enquo(iss))
  ) {
    "nfti"
  } else if (
    rlang::quo_is_null(rlang::enquo(nfti)) &&
      !rlang::quo_is_null(rlang::enquo(iss))
  ) {
    "cribari"
  } else if (
    !rlang::quo_is_null(rlang::enquo(nfti)) &&
      !rlang::quo_is_null(rlang::enquo(iss))
  ) {
    cli::cli_abort(
      "Please supply exactly one of {.var iss} or {.var nfti}."
    )
  }

  ###___________________________________________________________________________
  ### Calculations ----
  ###___________________________________________________________________________

  # Initiate the list for output ----
  seqic_10 <- list()

  # Measure 10a: undertriage ----
  # Patients who met triage criteria (positive) but received low activation
  # Denominator: all limited-to-no trauma team activation cases
  # Numerator: major_trauma AND limited_no_activation
  seqic_10a <- data_prep |>
    dplyr::summarize(
      numerator_10a = sum(
        undertriage,
        na.rm = TRUE
      ),

      # Patients who had a limited or no trauma team activation
      denominator_10a = sum(limited_no_activation, na.rm = TRUE),
      seqic_10a = dplyr::if_else(
        denominator_10a > 0,
        numerator_10a / denominator_10a,
        NA_real_ # Return NA if no denominator
      ),
      .by = {{ groups }}
    )

  # Measure 10b: overtriage ----
  # Patients who did NOT meet triage criteria (negative) but received highest
  # activation
  # Denominator: all full trauma team activations
  # Numerator: minor_trauma AND full_activation
  seqic_10b <- data_prep |>
    dplyr::summarize(
      numerator_10b = sum(
        overtriage,
        na.rm = TRUE
      ),
      denominator_10b = sum(full_activation, na.rm = TRUE),
      seqic_10b = dplyr::if_else(
        denominator_10b > 0,
        numerator_10b / denominator_10b,
        NA_real_
      ),
      .by = {{ groups }}
    )

  # Measure 10c: undertriage ----
  # Patients who met triage criteria (positive) but received low activation
  # Denominator: all major trauma cases
  # Numerator: major_trauma AND limited_no_activation
  # This is Peng & Xiang's (2016) update to the Cribari method of calculating
  # under triage
  seqic_10c <- data_prep |>
    dplyr::summarize(
      numerator_10c = sum(
        undertriage,
        na.rm = TRUE
      ),

      # All major trauma patients as denominator
      denominator_10c = sum(major_trauma, na.rm = TRUE),
      seqic_10c = dplyr::if_else(
        denominator_10c > 0,
        numerator_10c / denominator_10c,
        NA_real_ # Return NA if no denominator
      ),
      .by = {{ groups }}
    )

  # Model Diagnostic Testing ----
  # Cribari 2x2 matrix to produce model diagnostic tests
  # Based on methods in Peng & Xiang (2016)

  # The following is from Table 1 in Peng & Xiang (2016)
  # The Cribari matrix: Injury severity and trauma team activation.
  #
  #                            | Minor Trauma | Major Trauma |   Total
  # ---------------------------|--------------|--------------|---------
  # Full Trauma Team Activation|       a      |      b       |   a + b
  # Limited/No Activation      |       c      |      d       |   c + d
  # ---------------------------|--------------|--------------|---------
  # Total                      |     a + c    |    b + d     |     N
  #
  # Common statistical terms used in diagnostic testing:
  #
  # Sensitivity              = b / (b + d)
  # Specificity              = c / (a + c)
  #
  # False Negative Rate (FNR) = d / (b + d)      # 1 - Sensitivity
  # False Positive Rate (FPR) = a / (a + c)      # 1 - Specificity
  #
  # Positive Predictive Value (PPV) = b / (a + b)
  # Negative Predictive Value (NPV) = c / (c + d)
  #
  # False Discovery Rate (FDR) = a / (a + b)     # 1 - PPV
  # False Omission Rate (FOR)  = d / (c + d)     # 1 - NPV

  diagnostics <- data_prep |>
    dplyr::summarize(
      # Calculate the key confusion matrix values
      full_minor = sum(full_activation & minor_trauma, na.rm = TRUE), # False Positive
      full_major = sum(full_activation & major_trauma, na.rm = TRUE), # True Positive
      limited_minor = sum(limited_no_activation & minor_trauma, na.rm = TRUE), # True Negative
      limited_major = sum(limited_no_activation & major_trauma, na.rm = TRUE), # False Negative
      .by = {{ groups }}
    ) |>
    dplyr::mutate(
      # Total number of classified records ----
      # N here is total records not missing classification information
      N = full_minor + full_major + limited_minor + limited_major,

      # Sensitivity = b / (b + d) ----
      sensitivity = dplyr::if_else(
        (full_major + limited_major) > 0,
        full_major / (full_major + limited_major),
        NA_real_
      ),

      # Specificity = c / (a + c) ----
      specificity = dplyr::if_else(
        (full_minor + limited_minor) > 0,
        limited_minor / (full_minor + limited_minor),
        NA_real_
      ),

      # Positive Predictive Value (PPV) = b / (a + b) <-
      positive_predictive_value = dplyr::if_else(
        (full_minor + full_major) > 0,
        full_major / (full_minor + full_major),
        NA_real_
      ),

      # Negative Predictive Value (NPV) = c / (c + d) ----
      negative_predictive_value = dplyr::if_else(
        (limited_minor + limited_major) > 0,
        limited_minor / (limited_minor + limited_major),
        NA_real_
      ),

      # False Negative Rate (FNR) = d / (b + d); 1 - Sensitivity ----
      false_negative_rate = dplyr::if_else(
        (full_major + limited_major) > 0,
        limited_major / (full_major + limited_major),
        NA_real_
      ),

      # False Positive Rate (FPR) = a / (a + c); 1 - Specificity ----
      false_positive_rate = dplyr::if_else(
        (full_minor + limited_minor) > 0,
        full_minor / (full_minor + limited_minor),
        NA_real_
      ),

      # False Discovery Rate (FDR) = a / (a + b); ----
      # 1 - Positive Predictive Value
      false_discovery_rate = dplyr::if_else(
        (full_minor + full_major) > 0,
        full_minor / (full_minor + full_major),
        NA_real_
      ),

      # False Omission Rate (FOR) = d / (c + d); ----
      # 1 - Negative Predictive Value
      false_omission_rate = dplyr::if_else(
        (limited_minor + limited_major) > 0,
        limited_major / (limited_minor + limited_major),
        NA_real_
      )
    )

  # Optionally compute confidence intervals ----
  if (!is.null(calculate_ci)) {
    # Apply 95% confidence interval function

    # 10a CIs ----
    seqic_10a <- seqic_10a |>
      dplyr::bind_cols(
        nemsqar::nemsqa_binomial_confint(
          data = seqic_10a,
          x = numerator_10a,
          n = denominator_10a,
          method = calculate_ci,
          ...
        ) |>
          dplyr::select(lower_ci, upper_ci) |>
          dplyr::rename(lower_ci_10a = lower_ci, upper_ci_10a = upper_ci)
      )

    # 10b CIs ----
    seqic_10b <- seqic_10b |>
      dplyr::bind_cols(
        nemsqar::nemsqa_binomial_confint(
          data = seqic_10b,
          x = numerator_10b,
          n = denominator_10b,
          method = calculate_ci,
          ...
        ) |>
          dplyr::select(lower_ci, upper_ci) |>
          dplyr::rename(lower_ci_10b = lower_ci, upper_ci_10b = upper_ci)
      )

    # 10c CIs ----
    seqic_10c <- seqic_10c |>
      dplyr::bind_cols(
        nemsqar::nemsqa_binomial_confint(
          data = seqic_10c,
          x = numerator_10c,
          n = denominator_10c,
          method = calculate_ci,
          ...
        ) |>
          dplyr::select(lower_ci, upper_ci) |>
          dplyr::rename(lower_ci_10c = lower_ci, upper_ci_10c = upper_ci)
      )
  }

  # Add label if ungrouped ----
  if (is.null(groups)) {
    seqic_10$seqic_10 <-
      tibble::tibble(
        data = "population/sample",
        triage_logic = triage_logic_source
      ) |>
      dplyr::bind_cols(seqic_10a, seqic_10b, seqic_10c)

    seqic_10$diagnostics <- tibble::tibble(
      data = "population/sample",
      triage_logic = triage_logic_source
    ) |>
      dplyr::bind_cols(diagnostics)
  } else {
    # Arrange by grouping variables ----
    seqic_10$seqic_10 <- tibble::tibble(
      triage_logic = triage_logic_source
    ) |>
      dplyr::bind_cols(seqic_10a) |>
      dplyr::full_join(
        seqic_10b,
        by = dplyr::join_by(!!!rlang::syms(groups))
      ) |>
      dplyr::full_join(
        seqic_10c,
        by = dplyr::join_by(!!!rlang::syms(groups))
      ) |>
      dplyr::arrange(!!!rlang::syms(groups))

    seqic_10$diagnostics <- tibble::tibble(
      triage_logic = triage_logic_source
    ) |>
      dplyr::bind_cols(diagnostics) |>
      dplyr::arrange(!!!rlang::syms(groups))
  }

  # Return measures in a tibble ----
  return(seqic_10)
}
