# Generated by OmopViewer 0.1.0
# Be careful editing this file

server <- function(input, output, session) {

  # Shared variables
  inputs_initialized <- reactiveVal(FALSE)
  shared_cdm_names   <- reactiveVal(NULL)
  shared_cohort_names  <- reactiveVal(NULL)

  # fill selectise variables ----
  shiny::observe({
    for (k in seq_along(choices)) {
      if(!grepl("cdm_name|cohort_name", names(choices)[k])){

        if(any(c("compare_large_scale_characteristics_cohort_1",
                 "compare_large_scale_characteristics_cohort_2") %in%
               names(choices)[k])){
          shinyWidgets::updateRadioGroupButtons(session,
                                                inputId = names(choices)[k],
                                                choices = choices[[k]],
                                                selected = selected[[k]],
                                                status = "custom-light")
        } else {
          shiny::updateSelectizeInput(
            session,
            inputId = names(choices)[k],
            choices = choices[[k]],
            selected = selected[[k]],
            server = TRUE
          )

          shinyWidgets::updatePickerInput(session,
                                          inputId = names(choices)[k],
                                          choices = choices[[k]],
                                          selected = selected[[k]])
        }
      }
    }
    inputs_initialized(TRUE)
  })

  # sortable ui elements -----
  # have these in server to avoid race condition (if in UI)
  output$achilles_sortable <- renderUI({
    sortable::bucket_list(
      header = NULL,
      sortable::add_rank_list(
        text = "none",
        labels = c("codelist_name"),
        input_id = "achilles_code_use_none"
      ),
      sortable::add_rank_list(
        text = "header",
        labels = c("cdm_name", "estimate_name"),
        input_id = "achilles_code_use_header"
      ),
      sortable::add_rank_list(
        text = "groupColumn",
        labels = character(),
        input_id = "achilles_code_use_groupColumn"
      ),
      sortable::add_rank_list(
        text = "hide",
        labels = character(),
        input_id = "achilles_code_use_hide"
      )
    )
  })
  outputOptions(output, "achilles_sortable", suspendWhenHidden = FALSE)

  output$orphan_sortable <- renderUI({
    sortable::bucket_list(
      header = NULL,
      sortable::add_rank_list(
        text = "none",
        labels = c("variable_name", "cohort_name", "variable_level"),
        input_id = "orphan_codes_gt_none"
      ),
      sortable::add_rank_list(
        text = "header",
        labels = c("cdm_name", "estimate_name"),
        input_id = "orphan_codes_gt_header"
      ),
      sortable::add_rank_list(
        text = "groupColumn",
        labels = character(),
        input_id = "orphan_codes_gt_groupColumn"
      ),
      sortable::add_rank_list(
        text = "hide",
        labels = character(),
        input_id = "orphan_codes_gt_hide"
      )
    )
  })
  outputOptions(output, "orphan_sortable", suspendWhenHidden = FALSE)

  output$cohort_code_use_sortable <- renderUI({
    sortable::bucket_list(
      header = NULL,
      sortable::add_rank_list(
        text = "none",
        labels = c("cohort_name", "codelist_name", "source_concept_name", "source_concept_id", "variable_name", "variable_level"),
        input_id = "cohort_code_use_gt_none"
      ),
      sortable::add_rank_list(
        text = "header",
        labels = c("cdm_name", "estimate_name"),
        input_id = "cohort_code_use_gt_header"
      ),
      sortable::add_rank_list(
        text = "groupColumn",
        labels =  character(),
        input_id = "cohort_code_use_gt_groupColumn"
      ),
      sortable::add_rank_list(
        text = "hide",
        labels = c("diagnostic", "phenotyper_version",
                   "domain_id", "timing"),
        input_id = "cohort_code_use_gt_hide"
      )
    )
  })
  outputOptions(output, "cohort_code_use_sortable", suspendWhenHidden = FALSE)

  output$measurement_value_as_concept_sortable <- renderUI({
    sortable::bucket_list(
      header = "Table formatting",
      sortable::add_rank_list(
        text = "none",
        labels = c("variable_level",  "estimate_name"),
        input_id = "measurement_value_as_concept_gt_none"
      ),
      sortable::add_rank_list(
        text = "header",
        labels = c("cdm_name"),
        input_id = "measurement_value_as_concepts_gt_header"
      ),
      sortable::add_rank_list(
        text = "groupColumn",
        labels =  c("cohort_name", "codelist_name"),
        input_id = "measurement_value_as_concept_gt_groupColumn"
      ),
      sortable::add_rank_list(
        text = "hide",
        labels =  c("variable_name"),
        input_id = "measurement_value_as_concept_gt_hide"
      )
    )
  })
  outputOptions(output, "measurement_value_as_concept_sortable", suspendWhenHidden = FALSE)

  output$measurement_value_as_number_sortable <- renderUI({
    sortable::bucket_list(
      header = "Table formatting",
      sortable::add_rank_list(
        text = "none",
        labels = c("estimate_name"),
        input_id = "measurement_value_as_number_gt_none"
      ),
      sortable::add_rank_list(
        text = "header",
        labels = c("cdm_name"),
        input_id = "measurement_value_as_number_gt_header"
      ),
      sortable::add_rank_list(
        text = "groupColumn",
        labels =  c("cohort_name", "codelist_name"),
        input_id = "measurement_value_as_number_gt_groupColumn"
      ),
      sortable::add_rank_list(
        text = "hide",
        labels =  c("variable_name", "variable_level"),
        input_id = "measurement_value_as_number_gt_hide"
      )
    )
  })
  outputOptions(output, "measurement_value_as_number_sortable", suspendWhenHidden = FALSE)

  output$measurement_summary_sortable <- renderUI({
    sortable::bucket_list(
      header = "Table formatting",
      sortable::add_rank_list(
        text = "none",
        labels = c("variable_name", "estimate_name"),
        input_id = "measurement_summary_gt_none"
      ),
      sortable::add_rank_list(
        text = "header",
        labels = c("cdm_name"),
        input_id = "measurement_summary_gt_header"
      ),
      sortable::add_rank_list(
        text = "groupColumn",
        labels =  c("cohort_name", "codelist_name"),
        input_id = "measurement_summary_gt_groupColumn"
      ),
      sortable::add_rank_list(
        text = "hide",
        labels =  c("variable_level"),
        input_id = "measurement_summary_gt_hide"
      )
    )
  })
  outputOptions(output, "measurement_summary_sortable", suspendWhenHidden = FALSE)

  output$drug_diagnostics_sortable <- renderUI({
    sortable::bucket_list(
      header = "Table formatting",
      sortable::add_rank_list(
        text = "none",
        labels = c("ingredient_name","variable_name", "estimate_name"),
        input_id = "drug_diagnostics_gt_none"
      ),
      sortable::add_rank_list(
        text = "header",
        labels = c("cdm_name"),
        input_id = "drug_diagnostics_gt_header"
      ),
      sortable::add_rank_list(
        text = "groupColumn",
        labels =  c("cohort_name", "codelist_name"),
        input_id = "drug_diagnostics_gt_groupColumn"
      ),
      sortable::add_rank_list(
        text = "hide",
        labels =  c("route", "drug_type",
                    "variable_level",
                    "ingredient_concept_id"),
        input_id = "drug_diagnostics_gt_hide"
      )
    )
  })
  outputOptions(output, "drug_diagnostics_sortable", suspendWhenHidden = FALSE)


  output$summarise_characteristics_sortable <- renderUI({
    sortable::bucket_list(
      header = "Table formatting",
      sortable::add_rank_list(
        text = "none",
        labels = c("variable_name", "variable_level", "estimate_name"),
        input_id = "summarise_characteristics_gt_none"
      ),
      sortable::add_rank_list(
        text = "header",
        labels = c("cdm_name", "cohort_name"),
        input_id = "summarise_characteristics_gt_header"
      ),
      sortable::add_rank_list(
        text = "groupColumn",
        labels = NULL,
        input_id = "summarise_characteristics_gt_groupColumn"
      ),
      sortable::add_rank_list(
        text = "hide",
        labels = c("diagnostic", "phenotyper_version", "matchedSample"),
        input_id = "summarise_characteristics_gt_hide"
      )
    )
  })
  outputOptions(output, "summarise_characteristics_sortable", suspendWhenHidden = FALSE)

  output$summarise_cohort_overlap_sortable <- renderUI({
    sortable::bucket_list(
      header = "Table formatting",
      sortable::add_rank_list(
        text = "none",
        labels = c("cohort_name_reference", "cohort_name_comparator", "estimate_name"),
        input_id = "summarise_cohort_overlap_gt_none"
      ),
      sortable::add_rank_list(
        text = "header",
        labels = "variable_name",
        input_id = "summarise_cohort_overlap_gt_header"
      ),
      sortable::add_rank_list(
        text = "groupColumn",
        labels = "cdm_name",
        input_id = "summarise_cohort_overlap_gt_groupColumn"
      ),
      sortable::add_rank_list(
        text = "hide",
        labels = c("variable_level", "diagnostic", "phenotyper_version", "matchedSample"),
        input_id = "summarise_cohort_overlap_gt_hide"
      )
    )
  })
  outputOptions(output, "summarise_cohort_overlap_sortable", suspendWhenHidden = FALSE)

  output$summarise_cohort_timing_sortable <- renderUI({
    sortable::bucket_list(
      header = "Table formatting",
      sortable::add_rank_list(
        text = "none",
        labels = c("cohort_name_reference", "cohort_name_comparator", "estimate_name"),
        input_id = "summarise_cohort_timing_gt_none"
      ),
      sortable::add_rank_list(
        text = "header",
        labels = "variable_name",
        input_id = "summarise_cohort_timing_gt_header"
      ),
      sortable::add_rank_list(
        text = "groupColumn",
        labels = "cdm_name",
        input_id = "summarise_cohort_timing_gt_groupColumn"
      ),
      sortable::add_rank_list(
        text = "hide",
        labels = "variable_level",
        input_id = "summarise_cohort_timing_gt_hide"
      )
    )
  })
  outputOptions(output, "summarise_cohort_timing_sortable", suspendWhenHidden = FALSE)

  output$summarise_cohort_survival_sortable<- renderUI({
    sortable::bucket_list(
      header = "Table formatting",
      sortable::add_rank_list(
        text = "none",
        labels = c("cdm_name", "target_cohort"),
        input_id = "survival_table_none"
      ),
      sortable::add_rank_list(
        text = "header",
        labels = "estimate_name",
        input_id = "survival_table_header"
      ),
      sortable::add_rank_list(
        text = "groupColumn",
        labels = character(),
        input_id = "survival_table_groupColumn"
      ),
      sortable::add_rank_list(
        text = "hide",
        labels = character(),
        input_id = "survival_table_hide"
      )
    )
  })
  outputOptions(output, "summarise_cohort_survival_sortable", suspendWhenHidden = FALSE)


  # Define shared cdm_names values ----
  cdm_values <- names(choices)[grepl("cdm_name", names(choices)) & names(choices) != "shared_cdm_names"]
  last_active_cdm_input <- reactiveVal(NULL)
  for(inputValue in cdm_values){
    local({
      inputValue_local <- inputValue
      shiny::observeEvent(input[[inputValue_local]], {
        val <- input[[inputValue_local]]
        if (is.null(val) || length(val) == 0 || all(val == "")) {
          val <- character(0)
        }

        if (!identical(val, shared_cdm_names())) {
          last_active_cdm_input(inputValue_local)
          shared_cdm_names(val)
        }
      }, ignoreNULL = FALSE)
    })
  }

  shiny::observeEvent(shared_cdm_names(), {
    new_shared_val <- shared_cdm_names()
    source_input <- isolate(last_active_cdm_input())

    for (inputId in cdm_values) {
      if (!is.null(source_input) && inputId == source_input) {
        next
      }

      current_val <- isolate(input[[inputId]])
      if (is.null(current_val)) current_val <- character(0)

      if (!identical(new_shared_val, current_val)) {
        shinyWidgets::updatePickerInput(session, inputId, selected = new_shared_val)
      }
    }
  }, ignoreNULL = FALSE, ignoreInit = TRUE)

  # Define shared cohort_names values ----
  cohort_values <- names(choices)[grepl("cohort_name", names(choices)) & names(choices) != "shared_cohort_names"]
  last_active_cohort_input <- reactiveVal(NULL)
  for(inputValue in cohort_values){
    local({
      inputValue_local <- inputValue
      shiny::observeEvent(input[[inputValue_local]], {
        val <- input[[inputValue_local]]
        if (is.null(val) || length(val) == 0 || all(val == "")) {
          val <- character(0)
        }

        if (!identical(val, shared_cohort_names())) {
          last_active_cohort_input(inputValue_local) # Log the source!
          shared_cohort_names(val)
        }
      }, ignoreNULL = FALSE)
    })
  }

  shiny::observeEvent(shared_cohort_names(), {
    new_shared_val <- shared_cohort_names()
    source_input <- isolate(last_active_cohort_input())

    for (inputId in cohort_values) {
      if (!is.null(source_input) && inputId == source_input) {
        next
      }

      current_val <- isolate(input[[inputId]])
      if (is.null(current_val)) { current_val <- character(0) }

      if (!identical(new_shared_val, current_val)) {
        shinyWidgets::updatePickerInput(session, inputId, selected = new_shared_val)
      }
    }
  }, ignoreNULL = FALSE, ignoreInit = TRUE)

  # download raw data -----
  output$download_raw <- shiny::downloadHandler(
    filename = "results.csv",
    content = function(file) {

      # Initialize a progress bar
      shiny::withProgress(value = 0, {

        # Step 1: Importing data
        shiny::incProgress(.25, message = "Importing data", detail = "Preparing summarised result...")
        rawData <- omopgenerics::importSummarisedResult(file.path(getwd(), "data", "raw"))

        # Step 2: Exporting data
        shiny::incProgress(.75, message = "Exporting data", detail = "Preparing file for download...")
        omopgenerics::exportSummarisedResult(rawData, fileName = file)
      })
    }
  )

  # database description ----
  database_description <- eventReactive(input$updateDatabaseDescription, {
    req(shared_cdm_names())
    req(inputs_initialized())

    info <- database_descriptions |>
      dplyr::filter(.data$database %in% shared_cdm_names())

    return(info)
  })

  output$database_text <- renderUI({
    info <- database_description()

    info$author[which(info$author == "")] <- "Unknown author"
    info$date[which(info$date == "")]     <- "Unknown date"
    info$key_sources[which(info$key_sources == "")] <- "Unknown key sources"

    info <- info |>
      dplyr::mutate("metadata" = paste0("Author: ", author, " (Date: ", date, ")<br>Sources: ", key_sources))

    info$description <-  purrr::map(info$description,
                                    function(info) {paste0("<lbr>", info, "</br>")})

    lapply(1:nrow(info), function(i) {
      row <- info[i, ]

      has_meta <- !is.na(row$author) && !is.na(row$date) && !is.na(row$key_sources)

      tags$details(
        tags$summary(row$database),
        tags$div(
          class = "content-box",

          if (has_meta) {
            tagList(
              tags$div(
                style = "padding: 15px; border-left: 4px solid #750075; color = grey; background: #E9E9E9; font-style: italic;",
                shiny::HTML(row$metadata)
              ),
              tags$div(
                style = "padding: 15px; border-left: 4px solid #750075; background: #E9E9E9; font-weight: normal;",
                shiny::HTML(row$description[[1]])
              )
            )
          } else{
            tags$div(style = "padding: 15px; border-left: 4px solid #750075; background: #E9E9E9; font-weight: normal;",
                     "No database description for this database")
          }
        )
      )
    })
  })

  # clinical description ----
  clinical_description <- eventReactive(input$updateClinicalDescription, {
    req(shared_cohort_names())
    req(inputs_initialized())

    info <- clinical_descriptions |>
      dplyr::filter(.data$phenotype %in% shared_cohort_names()) |>
      dplyr::rename("description" = input$phenotypes_section)

    return(info)
  })

  output$clinical_text <- renderUI({
    info <- clinical_description()
    info$author[which(info$author == "")] <- "Unknown author"
    info$date[which(info$date == "")]     <- "Unknown date"
    info$key_sources[which(info$key_sources == "")] <- "Unknown key sources"

    info <- info |>
      dplyr::mutate("metadata" = paste0("Author: ", author, " (Date: ", date, ")<br>Sources: ", key_sources))

    info$description <-  purrr::map(info$description,
                                    function(info) {paste0("<lbr>", info, "</br>")})

    lapply(1:nrow(info), function(i) {
      row <- info[i, ]

      has_meta <- !is.na(row$author) && !is.na(row$date) && !is.na(row$key_sources)

      tags$details(
        tags$summary(row$phenotype),
        tags$div(
          class = "content-box",

          if (has_meta) {
            tagList(
              tags$div(
                style = "padding: 15px; border-left: 4px solid #750075; color = grey; background: #E9E9E9; font-style: italic;",
                shiny::HTML(row$metadata)
              ),
              tags$div(
                style = "padding: 15px; border-left: 4px solid #750075; background: #E9E9E9; font-weight: normal;",
                shiny::HTML(row$description[[1]])
              )
            )
          } else{
            tags$div(
              style = "padding: 15px; border-left: 4px solid #750075; background: #E9E9E9; font-weight: normal;",
              "No description for this cohort")

          }
        )
      )
    })
  })

  # summarise_omop_snapshot -----
  filterOmopSnapshot <- eventReactive(input$updateSnapshot, ({
    if (is.null(dataFiltered$summarise_omop_snapshot)) {
      validate("No snapshot in results")
    }

    result <- dataFiltered$summarise_omop_snapshot |>
      dplyr::filter(cdm_name %in% shared_cdm_names())
    attr(result, "settings")  <- attr(result, "settings") |>
      dplyr::select(!c("diagnostic", "phenotyper_version"))

    validateFilteredResult(result)

    return(result)
  }))
  ## Table summarise_omop_snapshot ----
  createTableOmopSnapshot <- shiny::reactive({
    filterOmopSnapshot() |>
      OmopSketch::tableOmopSnapshot() |>
      tab_header(
        title = "Database metadata",
        subtitle = "Overview of data source"
      ) |>
      tab_options(
        heading.align = "left"
      )
  })
  output$summarise_omop_snapshot_gt <- gt::render_gt({
    createTableOmopSnapshot()
  })
  output$summarise_omop_snapshot_gt_download <- shiny::downloadHandler(
    filename = "summarise_omop_snapshot_gt.docx",
    content = function(file) {
      gt::gtsave(data = createTableOmopSnapshot(), filename = file)
    }
  )

  # summarise_person -----
  filterPerson <- eventReactive(input$updatePerson, ({
    if (is.null(dataFiltered$summarise_person)) {
      validate("No person summary in results")
    }

    result <- dataFiltered$summarise_person |>
      dplyr::filter(cdm_name %in% shared_cdm_names())
    attr(result, "settings")  <- attr(result, "settings") |>
      dplyr::select(!c("diagnostic", "phenotyper_version"))
    validateFilteredResult(result)

    return(result)
  }))

  ## Table summarise_person -----
  createTablePerson <- shiny::reactive({
    filterPerson() |>
      OmopSketch::tablePerson() |>
      tab_header(
        title = "Summary of person table",
        subtitle = "The person table contains core information on patients captured in the OMOP CDM dataset."
      ) |>
      tab_options(
        heading.align = "left"
      )
  })
  output$summarise_person_gt <- gt::render_gt({
    createTablePerson()
  })
  output$summarise_person_gt_download <- shiny::downloadHandler(
    filename = "summarise_person_gt.docx",
    content = function(file) {
      obj <- createTablePerson()
      gt::gtsave(data = obj, filename = file)
    }
  )


  ## Plot date of birth ----
  filterPersonDob <- eventReactive(input$updatePerson, ({
    if (is.null(dataFiltered$summarise_dob_density)) {
      validate("No date of birth summary in results")
    }

    result <- dataFiltered$summarise_dob_density |>
      dplyr::filter(cdm_name %in% shared_cdm_names())

    validateFilteredResult(result)

    return(result)
  }))

  output$dobPlot <- renderPlot({
    filterPersonDob() |>
      dplyr::filter(estimate_name == "density_y" |
                      (estimate_name == "density_x" &
                         as.Date(estimate_value) >= input$dob_date_range[[1]]) &
                      as.Date(estimate_value) <= input$dob_date_range[[2]]) |>
      visOmopResults::scatterPlot(
        x = "density_x",
        y = "density_y",
        colour = "cdm_name",
        line = TRUE,
        point = FALSE,
        ribbon = FALSE,
        ymin = NULL,
        ymax = NULL) +
      ggplot2::xlab("Date of Birth") +
      ggplot2::ylab("Density") +
      ggplot2::scale_y_continuous(labels = scales::label_number()) +
      visOmopResults::themeVisOmop() +
      ggplot2::theme(legend.title = element_blank(),
                     legend.position = "top")
  })

  # summarise_observation_period -----
  filterObservationPeriod <- eventReactive(input$updateObservationPeriod, ({
    if (is.null(dataFiltered$summarise_observation_period)) {
      validate("No observation period summary in results")
    }

    result <- dataFiltered$summarise_observation_period |>
      dplyr::filter(cdm_name %in% shared_cdm_names())
    attr(result, "settings")  <- attr(result, "settings") |>
      dplyr::select(!c("diagnostic", "phenotyper_version"))
    validateFilteredResult(result)

    return(result)
  }))

  ## Table summarise_observation_period -----
  createTableObservationPeriod <- shiny::reactive({
    filterObservationPeriod() |>
      OmopSketch::tableObservationPeriod() |>
      tab_header(
        title = "Summary of observation periods",
        subtitle = "Observation periods are used to define time under observation for individuals in the data source."
      ) |>
      tab_options(
        heading.align = "left"
      )
  })
  output$summarise_observation_period_gt <- gt::render_gt({
    createTableObservationPeriod()
  })
  output$summarise_observation_period_gt_download <- shiny::downloadHandler(
    filename = "summarise_observation_period_gt.docx",
    content = function(file) {
      obj <- createTableObservationPeriod()
      gt::gtsave(data = obj, filename = file)
    }
  )

  ## Plot obs start end ----
  filterObs <- eventReactive(input$updateObservationPeriod, ({
    if (is.null(dataFiltered$summarise_obs_density)) {
      validate("No date of observation period distributions in results")
    }

    result <- dataFiltered$summarise_obs_density |>
      dplyr::filter(cdm_name %in% shared_cdm_names())
    validateFilteredResult(result)

    return(result)
  }))

  output$obsPlot <- renderPlot({

    plot_data <- filterObs() |>
      dplyr::filter(estimate_name == "density_y" |
                      (estimate_name == "density_x" &
                         as.Date(estimate_value) >= input$obs_date_range[[1]]) &
                      as.Date(estimate_value) <= input$obs_date_range[[2]]) |>
      dplyr::mutate(variable_name =
                      dplyr::if_else(variable_name == "observation_period_start_date",
                                     "observation period start date",
                                     "observation period end date")) |>
      dplyr::mutate(variable_name = factor(variable_name,
                                           levels = c("observation period start date",
                                                      "observation period end date")))

    plot_data |>
      visOmopResults::scatterPlot(
        x = "density_x",
        y = "density_y",
        group = c("cdm_name", "variable_name"),
        facet = "variable_name",
        colour = "cdm_name",
        line = TRUE,
        point = FALSE,
        ribbon = FALSE,
        ymin = NULL,
        ymax = NULL) +
      ggplot2::xlab("Date") +
      ggplot2::ylab("Density") +
      ggplot2::facet_wrap(vars(variable_name),
                          ncol = 1, scales = "free_y") +
      ggplot2::scale_y_continuous(labels = scales::label_number()) +
      visOmopResults::themeVisOmop() +
      ggplot2::theme(legend.title = element_blank(),
                     legend.position = "top") +
      ggplot2::scale_y_continuous(labels = scales::label_number())

  })

  # summarise_clinical_records ----
  filterClinicalRecords <- eventReactive(input$updateClinicalRecords, ({
    if (is.null(dataFiltered$summarise_clinical_records)) {
      validate("No clinical records summary in results")
    }
    result <- dataFiltered$summarise_clinical_records |>
      dplyr::filter(cdm_name %in% shared_cdm_names(),
                    group_level %in% input$summarise_clinical_records_omop_table)
    attr(result, "settings")  <- attr(result, "settings") |>
      dplyr::select(!c("diagnostic", "phenotyper_version"))
    validateFilteredResult(result)

    return(result)
  }))

  ## Table summarise_clinical_records -----
  createClinicalRecordsTable <- shiny::reactive({
    filterClinicalRecords() |>
      OmopSketch::tableClinicalRecords() |>
      tab_header(
        title = "Summary of Clinical Records",
        subtitle = "Summary of the clinical tables that contain the codes from the cohort codelist."
      ) |>
      tab_options(
        heading.align = "left"
      )
  })
  output$summarise_clinical_records_gt <- gt::render_gt({
    createClinicalRecordsTable()
  })
  output$summarise_clinical_records_gt_download <- shiny::downloadHandler(
    filename = "summarise_clinical_records_gt.docx",
    content = function(file) {
      obj <- createClinicalRecordsTable()
      gt::gtsave(data = obj, filename = file)
    }
  )

  ## Plot clinical record trends -----
  filterClinicalRecordTrends <- eventReactive(input$updateClinicalRecords, ({
    if (is.null(dataFiltered$summarise_trend)) {
      validate("No clinical records summary in results")
    }

    result <- dataFiltered$summarise_trend |>
      dplyr::filter(cdm_name %in% shared_cdm_names(),
                    group_level %in% input$summarise_clinical_records_omop_table)
    validateFilteredResult(result)

    return(result)
  }))

  output$clinicalTrends <- renderPlot({

    plot_data <- filterClinicalRecordTrends() |>
      dplyr::filter(
        as.Date(stringr::str_split_i(additional_level, " to", 1)) >= input$records_date_range[[1]] &
          as.Date(stringr::str_split_i(additional_level, " to", 1)) <= input$records_date_range[[2]]
      )

    plot <- plot_data |>
      OmopSketch::plotTrend(style = "default",
                            colour = input$clinical_records_plot_colour,
                            facet = input$clinical_records_plot_facet) +
      visOmopResults::themeVisOmop() +
      ggplot2::theme(legend.title = element_blank(),
                     legend.position = "top") +
      ggplot2::scale_y_continuous(labels = scales::label_number()) +
      ggplot2::ggtitle("")

    if(!is.null(input$clinical_records_plot_facet) &&
       isTRUE(input$clinical_records_plot_facet_free)){
      plot <- plot +
        facet_wrap(facets = input$clinical_records_plot_facet,
                   scales = "free_y")
    }

    plot@data <- plot@data  |>
      dplyr::mutate(time_interval = stringr::str_split_i(time_interval, " to", 1)) |>
      dplyr::mutate(time_interval = as.Date(time_interval))

    plot +
      ggplot2::scale_x_date()
  })

  # achilles_code_use -----
  filterAchillesCodeUse <- eventReactive(input$updateAchillesCodeUse, ({

    req(shared_cdm_names())
    req(inputs_initialized())

    if (is.null(dataFiltered$achilles_code_use)) {
      validate("No achilles code use in results")
    }

    achillesFiltered <- dataFiltered$achilles_code_use  |>
      dplyr::filter(.data$cdm_name %in% shared_cdm_names(),
                    .data$group_level %in% input$achilles_code_use_codelist_name)

    if(isFALSE(input$achilles_person_count)){
      achillesFiltered <- achillesFiltered  |>
        filter(estimate_name != "person_count")
    }
    if(isFALSE(input$achilles_record_count)){
      achillesFiltered <- achillesFiltered  |>
        filter(estimate_name != "record_count")
    }

    validateFilteredResult(achillesFiltered)

    return(achillesFiltered)
  }))

  ## Table achilles_code_use ----
  createAchillesCodeUseGT <- shiny::reactive({
    tbl <- CodelistGenerator::tableAchillesCodeUse(filterAchillesCodeUse(),
                                                   header = input$achilles_code_use_header,
                                                   groupColumn = input$achilles_code_use_groupColumn,
                                                   hide = input$achilles_code_use_hide) |>
      tab_header(
        title = "Summary of achilles codes",
        subtitle = "Codes from codelist observed in achilles tables."
      ) |>
      tab_options(
        heading.align = "left"
      )

    return(tbl)
  })
  createAchillesCodeUseInteractive <- shiny::reactive({
    tbl <- CodelistGenerator::tableAchillesCodeUse(filterAchillesCodeUse(),
                                                   header = input$achilles_code_use_header,
                                                   groupColumn = input$achilles_code_use_groupColumn,
                                                   hide = input$achilles_code_use_hide,
                                                   type = "tibble")
    names(tbl) <- stringr::str_remove_all(names(tbl),
                                          "\\[header_name\\]Database name\\n\\[header_level\\]")
    names(tbl) <- stringr::str_remove_all(names(tbl),
                                          "Estimate name\n\\[header_level\\]")
    names(tbl) <- stringr::str_replace_all(names(tbl),
                                           "\n\\[header_name\\]",
                                           ": ")
    return(tbl)
  })

  output$achilles_code_use_tbl <- shiny::renderUI({

    if(isFALSE(input$achilles_interactive)){
      tbl <- createAchillesCodeUseGT()
      return(tbl)

    } else {

      tbl <- createAchillesCodeUseInteractive()

      # column ordering by codelist and first column with a count
      order <- list("Codelist name"  = "asc",
                    "count" = "desc")
      names(order)[2] <- names(tbl)[ncol(tbl)]

      # suppressed to NA
      tbl <- tbl |>
        purrr::map_df(~ ifelse(grepl("^<", .), NA, .)) |>
        dplyr::mutate(dplyr::across(c(ends_with("count")),
                                    ~ gsub(",", "", .))) |>
        dplyr::mutate(dplyr::across(c(ends_with("count")),
                                    ~ suppressWarnings(as.numeric(.))))

      tbl <- reactable::reactable(tbl,
                                  defaultSorted = order,
                                  groupBy = c("Codelist name"),
                                  columns = getColsForTbl(tbl),
                                  filterable = TRUE,
                                  searchable = TRUE,
                                  defaultPageSize = 25,
                                  highlight = TRUE,
                                  striped = TRUE,
                                  compact = TRUE,
                                  showSortable = TRUE) |>
        reactablefmtr_add_title("Summary of achilles codes",
                                font_size = 25,
                                font_weight = "normal") |>
        reactablefmtr_add_subtitle("Codes from codelist observed in achilles tables.",
                                   font_size = 15,
                                   font_weight = "normal")

      return(tbl)
    }
  })

  output$achilles_code_use_download <- shiny::downloadHandler(
    filename = function(){
      if(isFALSE(input$achilles_interactive)){
        "summarise_achilles_code_use_gt.docx"
      }else{
        "summarise_achilles_code_use_tbl.csv"
      }
    },
    content = function(file){
      if(isFALSE(input$achilles_interactive)){
        gt::gtsave(data = createAchillesCodeUseGT(), filename = file)
      }else{
        readr::write_csv(createAchillesCodeUseInteractive(), file = file)
      }
    }
  )


  # orphan_codes -----
  filterOrphanCodes <-  eventReactive(input$updateOrphanCodeUse, ({
    req(shared_cdm_names())
    req(inputs_initialized())
    if (is.null(dataFiltered$orphan_code_use)) {
      validate("No orphan codes in results")
    }

    result <- dataFiltered$orphan_code_use |>
      dplyr::filter(.data$cdm_name %in% shared_cdm_names(),
                    .data$group_level %in% input$orphan_code_use_codelist_name)

    if(isFALSE(input$orphan_person_count)){
      result <- result  |>
        filter(estimate_name != "person_count")
    }
    if(isFALSE(input$orphan_record_count)){
      result <- result  |>
        filter(estimate_name != "record_count")
    }

    validateFilteredResult(result)

    return(result)
  }))

  ## Table orphan_codes -----
  createOrphanCodesGT <- shiny::reactive({
    tbl <- CodelistGenerator::tableOrphanCodes(
      filterOrphanCodes(),
      header = input$orphan_codes_gt_header,
      groupColumn = input$orphan_codes_gt_groupColumn,
      hide = input$orphan_codes_gt_hide
    )
    tbl |>
      tab_header(
        title = "Summary of orphan codes",
        subtitle = "Orphan codes refer to concepts present in the database that are not in a codelist but are related to included codes."
      ) |>
      tab_options(
        heading.align = "left"
      )

    return(tbl)
  })
  createOrphanCodesInteractive <- shiny::reactive({
    tbl <- CodelistGenerator::tableOrphanCodes(
      filterOrphanCodes(),
      header = input$orphan_codes_gt_header,
      groupColumn = input$orphan_codes_gt_groupColumn,
      hide = input$orphan_codes_gt_hide,
      type = "tibble"
    )
    names(tbl) <-stringr::str_remove_all(names(tbl),
                                         "\\[header_name\\]Database name\\n\\[header_level\\]")
    names(tbl) <- stringr::str_remove_all(names(tbl),
                                          "Estimate name\n\\[header_level\\]")
    names(tbl) <- stringr::str_replace_all(names(tbl),
                                           "\n\\[header_name\\]",
                                           ": ")

    return(tbl)
  })
  output$orphan_codes_tbl <- shiny::renderUI({

    if(isFALSE(input$orphan_interactive)){
      tbl <- createOrphanCodesGT()
      return(tbl)
    } else {
      tbl <- createOrphanCodesInteractive()
      # column ordering by codelist and first column with a count
      order <- list("Codelist name"  = "asc",
                    "count" = "desc")
      names(order)[2] <- names(tbl)[11]

      # suppressed to NA
      tbl <- tbl |>
        purrr::map_df(~ ifelse(grepl("^<", .), NA, .)) |>
        dplyr::mutate(dplyr::across(c(ends_with("count")),
                                    ~ gsub(",", "", .))) |>
        dplyr::mutate(dplyr::across(c(ends_with("count")),
                                    ~ suppressWarnings(as.numeric(.))))

      tbl <- reactable(tbl,
                       groupBy = c("Codelist name"),
                       columns = getColsForTbl(tbl),
                       defaultSorted = order,
                       filterable = TRUE,
                       searchable = TRUE,
                       defaultPageSize = 25,
                       highlight = TRUE,
                       striped = TRUE,
                       compact = TRUE,
                       showSortable = TRUE) |>
        reactablefmtr_add_title("Summary of orphan codes",
                                font_size = 25,
                                font_weight = "normal") |>
        reactablefmtr_add_subtitle("Orphan codes refer to concepts present in the database that are not in a codelist but are related to included codes.",
                                   font_size = 15,
                                   font_weight = "normal")

      return(tbl)
    }
  })

  output$orphan_codes_download <- shiny::downloadHandler(
    filename = function(){
      if(isFALSE(input$orphan_interactive)){
        "summarise_orphan_codes_gt.docx"
      }else{
        "summarise_orphan_codes_tbl.csv"
      }
    },
    content = function(file){
      if(isFALSE(input$orphan_interactive)){
        gt::gtsave(data = createOrphanCodesGT(), filename = file)
      }else{
        readr::write_csv(createOrphanCodesInteractive(), file = file)
      }
    }
  )

  # cohort_code_use -----
  filterCohortCodeUse <- eventReactive(input$updateCohortCodeUse, ({
    req(shared_cdm_names())
    req(shared_cohort_names())
    req(inputs_initialized())
    if (is.null(dataFiltered$cohort_code_use)) {
      validate("No cohort code use in results")
    }

    result <- dataFiltered$cohort_code_use |>
      visOmopResults::splitGroup(keep = TRUE) |>
      visOmopResults::splitAdditional(keep = TRUE) |>
      dplyr::filter(.data$cdm_name %in% shared_cdm_names(),
                    .data$cohort_name %in% shared_cohort_names(),
                    .data$domain_id %in% input$cohort_code_use_domain_id) |>
      dplyr::select(-visOmopResults::groupColumns(dataFiltered$cohort_code_use)) |>
      dplyr::select(-visOmopResults::additionalColumns(dataFiltered$cohort_code_use))

    if(isFALSE(input$cohort_code_use_person_count)){
      result <- result  |>
        filter(estimate_name != "person_count")
    }
    if(isFALSE(input$cohort_code_use_record_count)){
      result <- result  |>
        filter(estimate_name != "record_count")
    }

    validateFilteredResult(result)

    return(result)
  }))
  ## Table cohort_code_use -----
  createCohortCodeUseGT <- shiny::reactive({
    tbl <- CodelistGenerator::tableCohortCodeUse(
      filterCohortCodeUse(),
      header = input$cohort_code_use_gt_header,
      groupColumn = input$cohort_code_use_gt_groupColumn,
      hide = input$cohort_code_use_gt_hide
    ) |>
      tab_header(
        title = "Summary of cohort code use",
        subtitle = "Codes from codelist observed on day of cohort entry. Note more than one code could be seen for a person on this day (both of which would have led to inclusion)."
      ) |>
      tab_options(
        heading.align = "left"
      )

    return(tbl)
  })
  createCohortCodeUseInteractive <- shiny::reactive({
    tbl <-  CodelistGenerator::tableCohortCodeUse(
      filterCohortCodeUse(),
      header = input$cohort_code_use_gt_header,
      groupColumn = input$cohort_code_use_gt_groupColumn,
      hide = input$cohort_code_use_gt_hide,
      type = "tibble"
    )
    names(tbl) <-stringr::str_remove_all(names(tbl),
                                         "\\[header_name\\]Database name\\n\\[header_level\\]")
    names(tbl) <- stringr::str_remove_all(names(tbl),
                                          "Estimate name\n\\[header_level\\]")
    names(tbl) <- stringr::str_replace_all(names(tbl),
                                           "\n\\[header_name\\]",
                                           ": ")
    return(tbl)
  })
  output$cohort_code_use_tbl <- shiny::renderUI({

    if(isFALSE(input$cohort_code_use_interactive)){
      tbl <- createCohortCodeUseGT()
      return(tbl)
    } else {
      tbl <- createCohortCodeUseInteractive() |>
        dplyr::mutate(dplyr::across(c(ends_with("count")),
                                    ~ gsub(",", "", .))) |>
        dplyr::mutate(dplyr::across(c(ends_with("count")),
                                    ~ suppressWarnings(as.numeric(.))))

      tbl <- tbl |>
        dplyr::mutate("Cohort name - Codelist name" =
                        paste0(.data[["Cohort name"]], " - ", .data[["Codelist name"]])) |>
        dplyr::select(-c("Cohort name", "Codelist name")) |>
        dplyr::relocate("Cohort name - Codelist name")


      # column ordering by codelist and first column with a count
      order <- list("Cohort name - Codelist name"  = "asc",
                    "count" = "desc")
      names(order)[2] <- names(tbl)[11]

      # suppressed to NA
      tbl <- tbl |>
        purrr::map_df(~ ifelse(grepl("^<", .), NA, .))

      tbl <- reactable(tbl,
                       groupBy = c("Cohort name - Codelist name"),
                       columns = getColsForTbl(tbl,
                                               sortNALast = FALSE,
                                               names = c("Standard concept ID", "Source concept ID")),
                       defaultSorted = order,
                       filterable = TRUE,
                       searchable = TRUE,
                       defaultPageSize = 25,
                       highlight = TRUE,
                       striped = TRUE,
                       compact = TRUE,
                       showSortable = TRUE) |>
        reactablefmtr_add_title("Summary of cohort code use",
                                font_size = 25,
                                font_weight = "normal") |>
        reactablefmtr_add_subtitle("Codes from codelist observed on day of cohort entry. Note more than one code could be seen for a person on this day (both of which would have led to inclusion).",
                                   font_size = 15,
                                   font_weight = "normal")

      return(tbl)
    }
  })

  output$cohort_code_use_download <- shiny::downloadHandler(
    filename = function(){
      if(isFALSE(input$cohort_code_use_interactive)){
        file <- "summarise_cohort_code_use_gt.docx"
      }else{
        file <- "summarise_cohort_code_use_tbl.csv"
      }
      return(file)
    },
    content = function(file){
      if(isFALSE(input$cohort_code_use_interactive)){
        gt::gtsave(data = createCohortCodeUseGT(), filename = file)
      }else{
        readr::write_csv(createCohortCodeUseInteractive(), file = file)
      }
    }
  )

  # summarise measurement diagnostics -----
  filterMeasurementSummary <- eventReactive(input$updateMeasurementCodeUse, ({
    req(shared_cdm_names())
    req(shared_cohort_names())
    req(inputs_initialized())
    if (is.null(dataFiltered$measurement_summary)) {
      validate("No measurement summary in results")
    }

    result <- dataFiltered$measurement_summary |>
      dplyr::filter(.data$cdm_name %in% shared_cdm_names()) |>
      visOmopResults::filterGroup(.data$cohort_name %in% shared_cohort_names())

    validateFilteredResult(result)

    return(result)
  }))
  ## Table measurement_summary -----
  createMeasurementSummaryGT <- shiny::reactive({
    tbl <- MeasurementDiagnostics::tableMeasurementSummary(
      filterMeasurementSummary(),
      header = input$measurement_summary_gt_header,
      groupColumn = input$measurement_summary_gt_groupColumn,
      hide = input$measurement_summary_gt_hide
    ) |>
      tab_header(
        title = "Summary of measurements",
        subtitle = "Only codes from measurements/observations are shown. Time between measurements and number of measurements per subject."
      ) |>
      tab_options(
        heading.align = "left"
      )

    return(tbl)
  })

  output$measurement_summary_tbl <- shiny::renderUI({
    createMeasurementSummaryGT()
  })

  output$measurement_summary_gt_download <- shiny::downloadHandler(
    filename = "summarise_measurement_summary_gt.docx",
    content = function(file){
      gt::gtsave(data = createMeasurementSummaryGT(), filename = file)
    }
  )
  ## Plot measurement_summary ----
  getPlotMeasurementSummary <- shiny::reactive({
    result <- filterMeasurementSummary()

    MeasurementDiagnostics::plotMeasurementSummary(
      result,
      y = input$measurement_summary_y,
      plotType = input$measurement_summary_plottype,
      timeScale = input$measurement_summary_time_scale,
      facet = input$measurement_summary_facet,
      colour = input$measurement_summary_colour)
  })

  output$plot_measurement_summary <- shiny::renderPlot({
    getPlotMeasurementSummary()
  })

  output$plot_measurement_summary_download <- shiny::downloadHandler(
    filename = "output_ggplot2_measurement_summary.png",
    content = function(file) {
      obj <- getPlotMeasurementSummary()
      ggplot2::ggsave(
        filename = file,
        plot = obj,
        width = as.numeric(input$plot_measurement_summary_download_width),
        height = as.numeric(input$plot_measurement_summary_download_height),
        units = input$plot_measurement_summary_download_units,
        dpi = as.numeric(input$plot_measurement_summary_download_dpi)
      )
    }
  )

  # summarise measurement value as concept
  filterMeasurementValueAsConcept <- eventReactive(input$updateMeasurementCodeUse, ({

    if (is.null(dataFiltered$measurement_value_as_concept)) {
      validate("No measurement value as concept in results")
    }

    result <- dataFiltered$measurement_value_as_concept |>
      dplyr::filter(.data$cdm_name %in% shared_cdm_names()) |>
      visOmopResults::filterGroup(.data$cohort_name %in% shared_cohort_names())

    validateFilteredResult(result)

    return(result)
  }))

  ## Table measurement_value_as_concept -----
  createMeasurementValueAsConceptGT <- shiny::reactive({
    tbl <- MeasurementDiagnostics::tableMeasurementValueAsConcept(
      filterMeasurementValueAsConcept(),
      header = input$measurement_value_as_concept_gt_header,
      groupColumn = input$measurement_value_as_concept_gt_groupColumn,
      hide = input$measurement_value_as_concept_gt_hide
    ) |>
      tab_header(
        title = "Summary of measurement values (concepts)",
        subtitle = "Only codes from measurements that are concepts are shown."
      ) |>
      tab_options(
        heading.align = "left"
      )

    return(tbl)
  })

  output$measurement_value_as_concept_tbl <- shiny::renderUI({
    createMeasurementValueAsConceptGT()
  })

  output$measurement_value_as_concept_gt_download <- shiny::downloadHandler(
    filename = "summarise_measurement_value_as_concept_gt.docx",
    content = function(file){
      gt::gtsave(data = createMeasurementValueAsConceptGT(), filename = file)
    }
  )
  ## Plot measurement_value_as_concept ----
  getPlotMeasurementValueAsConcept <- shiny::reactive({
    result <- filterMeasurementValueAsConcept()

    MeasurementDiagnostics::plotMeasurementValueAsConcept(
      result,
      x = input$measurement_value_as_concept_x,
      y = input$measurement_value_as_concept_y,
      facet = input$measurement_value_as_concept_facet,
      colour = input$measurement_value_as_concept_colour
    ) +
      facet_wrap(input$measurement_value_as_concept_facet, scales = "free_y")
  })

  output$plot_measurement_value_as_concept <- shiny::renderPlot({
    getPlotMeasurementValueAsConcept()
  })

  output$plot_measurement_value_as_concept_download <- shiny::downloadHandler(
    filename = "output_ggplot2_measurement_value_as_concept.png",
    content = function(file) {
      obj <- getPlotMeasurementValueAsConcept()
      ggplot2::ggsave(
        filename = file,
        plot = obj,
        width = as.numeric(input$plot_measurement_value_as_concept_download_width),
        height = as.numeric(input$plot_measurement_value_as_concept_download_height),
        units = input$plot_measurement_value_as_concept_download_units,
        dpi = as.numeric(input$plot_measurement_value_as_concept_download_dpi)
      )
    }
  )

  # summarise measurement value as number
  filterMeasurementValueAsNumber <- eventReactive(input$updateMeasurementCodeUse, ({

    if (is.null(dataFiltered$measurement_value_as_number)) {
      validate("No measurement value as number in results")
    }

    result <- dataFiltered$measurement_value_as_number |>
      dplyr::filter(.data$cdm_name %in% shared_cdm_names()) |>
      visOmopResults::filterGroup(.data$cohort_name %in% shared_cohort_names())

    validateFilteredResult(result)

    return(result)
  }))

  ## Table measurement_value_as_number -----
  createMeasurementValueAsNumberGT <- shiny::reactive({
    tbl <- MeasurementDiagnostics::tableMeasurementValueAsNumber(
      filterMeasurementValueAsNumber(),
      header = input$measurement_value_as_number_gt_header,
      groupColumn = input$measurement_value_as_number_gt_groupColumn,
      hide = input$measurement_value_as_number_gt_hide
    ) |>
      tab_header(
        title = "Summary of measurement values (numeric)",
        subtitle = "Only codes from measurements which results are numeric are shown."
      ) |>
      tab_options(
        heading.align = "left"
      )

    return(tbl)
  })

  output$measurement_value_as_number_tbl <- shiny::renderUI({
    createMeasurementValueAsNumberGT()
  })

  output$measurement_value_as_number_gt_download <- shiny::downloadHandler(
    filename = "summarise_measurement_value_as_number_gt.docx",
    content = function(file){
      gt::gtsave(data = createMeasurementValueAsNumberGT(), filename = file)
    }
  )
  ## Plot measurement_value_as_number ----
  getPlotMeasurementValueAsNumber <- shiny::reactive({
    result <- filterMeasurementValueAsNumber()

    MeasurementDiagnostics::plotMeasurementValueAsNumber(
      result,
      x = input$measurement_value_as_number_x,
      plotType = input$measurement_value_as_number_plottype,
      facet = input$measurement_value_as_number_facet,
      colour = input$measurement_value_as_number_colour
    )
  })

  output$plot_measurement_value_as_number <- shiny::renderPlot({
    getPlotMeasurementValueAsNumber()
  })

  output$plot_measurement_value_as_number_download <- shiny::downloadHandler(
    filename = "output_ggplot2_measurement_value_as_number.png",
    content = function(file) {
      obj <- getPlotMeasurementValueAsNumber()
      ggplot2::ggsave(
        filename = file,
        plot = obj,
        width = as.numeric(input$plot_measurement_value_as_number_download_width),
        height = as.numeric(input$plot_measurement_value_as_number_download_height),
        units = input$plot_measurement_value_as_number_download_units,
        dpi = as.numeric(input$plot_measurement_value_as_number_download_dpi)
      )
    }
  )


  # summarise drug diagnostics -----
  filterDrugDiagnostics<- eventReactive(input$updateDrugDiagnostics, ({
    req(shared_cdm_names())
    req(shared_cohort_names())
    req(inputs_initialized())
    if (is.null(dataFiltered$summarise_drug_use)) {
      validate("No drug diagnostics in results")
    }

    result <- dataFiltered$summarise_drug_use |>
      dplyr::filter(.data$cdm_name %in% shared_cdm_names()) |>
      visOmopResults::filterGroup(.data$cohort_name %in%
                                    shared_cohort_names()) |>
      omopgenerics::filterGroup(.data$codelist_name %in%
                                  input$summarise_drug_use_codelist_name) |>
      omopgenerics::filterGroup(.data$route %in%
                                  input$summarise_drug_use_route) |>
      omopgenerics::filterGroup(.data$drug_type %in%
                                  input$summarise_drug_use_drug_type)

    if(isFALSE(input$drug_use_overall)){
      result <- result |>
        dplyr::filter(str_detect(group_name, "concept_name"))
    }

    if(isFALSE(input$drug_use_by_concept)){
      result <- result |>
        dplyr::filter(str_detect(group_name, "concept_name", negate = TRUE))
    }

    validateFilteredResult(result)

    return(result)
  }))

  ## Table drug diagnostics -----
  createDrugDiagnosticsGT <- shiny::reactive({

    res <- filterDrugDiagnostics()
    tbl <- res |>
      dplyr::arrange(group_name, group_level) |>
      visOmopResults::visOmopTable(header = input$drug_diagnostics_gt_header,
                                   groupColumn = input$drug_diagnostics_gt_groupColumn,
                                   estimateName = c(N = "<count>",
                                                    `Median [Q01, Q05, Q25 to Q75, Q95, Q99]` = "<median> [<q01>, <q05>, <q25> to <q75>, <q95>, <q99>]",
                                                    Range = "<min> to <max>",
                                                    `Percentage missing` = "<percentage_missing> %"),
                                   hide = input$drug_diagnostics_gt_hide ) |>
      tab_header(
        title = "Drug exposure diagnostics"
      ) |>
      tab_options(
        heading.align = "left"
      )

    return(tbl)
  })

  createDrugDiagnosticsInteractive <- shiny::reactive({

    res <- filterDrugDiagnostics()
    tbl <- res |>
      dplyr::arrange(group_name, group_level) |>
      visOmopResults::visOmopTable(header = input$drug_diagnostics_gt_header,
                                   groupColumn = input$drug_diagnostics_gt_groupColumn,
                                   estimateName = c(N = "<count>",
                                                    `Median [Q01, Q05, Q25 to Q75, Q95, Q99]` = "<median> [<q01>, <q05>, <q25> to <q75>, <q95>, <q99>]",
                                                    Range = "<min> to <max>",
                                                    `Percentage missing` = "<percentage_missing> %"),
                                   hide = input$drug_diagnostics_gt_hide,
                                   type = "tibble")
    names(tbl) <-stringr::str_remove_all(names(tbl),
                                         "\\[header_name\\]CDM name\\n\\[header_level\\]")
    names(tbl) <- stringr::str_remove_all(names(tbl),
                                          "Estimate name\n\\[header_level\\]")
    names(tbl) <- stringr::str_replace_all(names(tbl),
                                           "\n\\[header_name\\]",
                                           ": ")

    return(tbl)
  })

  output$drug_diagnostics_tbl <- shiny::renderUI({
    if(isFALSE(input$drug_diagnostics_interactive)){
      tbl <- createDrugDiagnosticsGT()
      return(tbl)
    } else {
      tbl <- createDrugDiagnosticsInteractive()

      tbl <- tbl |>
        dplyr::mutate("Cohort name - Codelist name" =
                        paste0(.data[["Cohort name"]], " - ", .data[["Codelist name"]])) |>
        dplyr::select(-c("Cohort name", "Codelist name")) |>
        dplyr::relocate("Cohort name - Codelist name")


      # column ordering by codelist and first column with a count
      order <- list("Cohort name - Codelist name"  = "asc")

      tbl <- reactable(tbl,
                       groupBy = c("Cohort name - Codelist name"),
                       columns = getColsForTbl(tbl,
                                               sortNALast = FALSE,
                                               names = c("Standard concept ID", "Source concept ID")),
                       defaultSorted = order,
                       filterable = TRUE,
                       searchable = TRUE,
                       defaultPageSize = 25,
                       highlight = TRUE,
                       striped = TRUE,
                       compact = TRUE,
                       showSortable = TRUE) |>
        reactablefmtr_add_title("Drug diagnostics",
                                font_size = 25,
                                font_weight = "normal")
    }
    tbl
  })

  output$drug_diagnostics_gt_download <- shiny::downloadHandler(
    filename = "summarise_drug_diagnostics_gt.docx",
    content = function(file){
      gt::gtsave(data = createDrugDiagnosticsGT(), filename = file)
    }
  )

  # summarise_cohort_count -----
  filterCohortCount <- eventReactive(input$updateCohortCount, ({
    req(shared_cdm_names())
    req(shared_cohort_names())
    req(inputs_initialized())
    if (is.null(dataFiltered$summarise_cohort_count)) {
      validate("No cohort count in results")
    }

    result <- dataFiltered$summarise_cohort_count |>
      dplyr::filter(.data$cdm_name %in% shared_cdm_names(),
                    .data$group_level %in% shared_cohort_names())

    if(isFALSE(input$cohort_count_person_count)){
      result <- result  |>
        dplyr::filter(.data$variable_name != "Number subjects")
    }
    if(isFALSE(input$cohort_count_record_count)){
      result <- result  |>
        dplyr::filter(.data$variable_name != "Number records")
    }

    validateFilteredResult(result)

    return(result)
  }))

  ## Table summarise_cohort_count ----
  createTableCohortCount <- shiny::reactive({
    res <- filterCohortCount()
    CohortCharacteristics::tableCohortCount(res,
                                            hide = c("variable_level",
                                                     "estimate_name",
                                                     settingsColumns(res))) |>
      tab_header(
        title = "Cohort count",
        subtitle = "Number of records and subjects in the study cohorts."
      ) |>
      tab_options(
        heading.align = "left"
      )
  })
  output$summarise_cohort_count_gt <- gt::render_gt({
    createTableCohortCount()
  })
  output$summarise_cohort_count_gt_download <- shiny::downloadHandler(
    filename = "summarise_cohort_count_gt.docx",
    content = function(file) {
      obj <- createTableCohortCount()
      gt::gtsave(data = obj, filename = file)
    }
  )

  # summarise_cohort_attrition -----
  filterCohortAttrition <- eventReactive(input$updateCohortCount,({
    req(shared_cdm_names())
    req(shared_cohort_names())
    req(inputs_initialized())
    if (is.null(dataFiltered$summarise_cohort_attrition)) {
      validate("No cohort attrition in results")
    }

    result <- dataFiltered$summarise_cohort_attrition |>
      dplyr::filter(.data$cdm_name %in% shared_cdm_names(),
                    .data$group_level %in% shared_cohort_names())

    if(isFALSE(input$cohort_count_person_count)){
      result <- result  |>
        dplyr::filter(!stringr::str_detect(.data$variable_name, "subjects"))
    }
    if(isFALSE(input$cohort_count_record_count)){
      result <- result  |>
        dplyr::filter(!stringr::str_detect(.data$variable_name, "records"))
    }

    validateFilteredResult(result)

    return(result)
  }))

  ## Table summarise_cohort_attrition ----
  createTableCohortAttrition <- shiny::reactive({
    filterCohortAttrition() |>
      CohortCharacteristics::tableCohortAttrition() |>
      tab_header(
        title = "Cohort attrition",
        subtitle = "Attrition of the study cohorts."
      ) |>
      tab_options(
        heading.align = "left"
      )
  })
  output$summarise_cohort_attrition_gt <- gt::render_gt({
    createTableCohortAttrition()
  })
  output$summarise_cohort_attrition_gt_download <- shiny::downloadHandler(
    filename = "summarise_cohort_attrition_gt.docx",
    content = function(file) {
      obj <- createTableCohortAttrition()
      gt::gtsave(data = obj, filename = file)
    }
  )
  ## Diagram summarise_cohort_attrition -----
  createDiagramCohortAttrition <- shiny::reactive({
    result <- filterCohortAttrition()

    n <- result |>
      select(cdm_name, group_level) |>
      distinct() |>
      nrow()

    CohortCharacteristics::plotCohortAttrition(
      result
    )

  })
  output$summarise_cohort_attrition_grViz <- DiagrammeR::renderGrViz({
    createDiagramCohortAttrition()
  })
  output$summarise_cohort_attrition_grViz_download <- shiny::downloadHandler(
    filename = "summarise_cohort_attrition_diagram.png",
    content = function(file) {
      createDiagramCohortAttrition() |>
        DiagrammeRsvg::export_svg() |>
        charToRaw() |>
        rsvg::rsvg_png(file,
                       width = input$summarise_cohort_attrition_grViz_download_width,
                       height = input$summarise_cohort_attrition_grViz_download_height)
    }
  )


  # summarise_characteristics -----
  filterSummariseCharacteristics <- eventReactive(input$updateCohortCharacteristics, ({
    req(shared_cdm_names())
    req(shared_cohort_names())
    req(inputs_initialized())
    if (is.null(dataFiltered$summarise_characteristics)) {
      validate("No cohort characteristics in results")
    }

    selectedCohorts <- shared_cohort_names()

    if(isTRUE(input$summarise_characteristics_include_matched)){
      selectedCohorts <- as.vector(t(outer(selectedCohorts, c("", "_sampled", "_matched"), paste0)))
    }

    result <- dataFiltered$summarise_characteristics |>
      dplyr::filter(.data$cdm_name %in% shared_cdm_names(),
                    .data$group_level %in% selectedCohorts) |>
      dplyr::mutate(group_level = factor(group_level, levels = selectedCohorts)) |>
      arrange(group_level)

    validateFilteredResult(result)

    return(result)
  }))
  ## Table summarise_characteristics -----
  createTableSummariseCharacteristics <- shiny::reactive({
    filterSummariseCharacteristics() |>
      CohortCharacteristics::tableCharacteristics(
        header = input$summarise_characteristics_gt_header,
        groupColumn = input$summarise_characteristics_gt_groupColumn,
        hide = c(input$summarise_characteristics_gt_hide,
                 "table_name", "value", "window", "table",
                 "diagnostic", "cohort_sample", "matched_sample", "phenotyper_version")
      ) |>
      tab_header(
        title = "Patient characteristics",
        subtitle = "Summary of patient characteristics relative to cohort entry. Please be aware that statistics are calculated by record, not by subject."
      ) |>
      tab_options(
        heading.align = "left"
      )
  })
  output$summarise_characteristics_gt <- gt::render_gt({
    createTableSummariseCharacteristics()
  })
  output$summarise_characteristics_gt_download <- shiny::downloadHandler(
    filename = "summarise_characteristics_gt.docx",
    content = function(file) {
      obj <- createTableSummariseCharacteristics()
      gt::gtsave(data = obj, filename = file)
    }
  )

  ## Plot age_pyramid ----
  createAgePyramid <- eventReactive(input$updateCohortCharacteristics, ({
    summarise_characteristics <- filterSummariseCharacteristics()

    summarise_table <- dataFiltered$summarise_table |>
      filter(cdm_name %in% shared_cdm_names())
    selectedCohorts <- shared_cohort_names()
    if(isTRUE(input$summarise_characteristics_include_matched)){
      selectedCohorts <- as.vector(t(outer(selectedCohorts, c("", "_sampled", "_matched"), paste0)))
    }
    summarise_table <- summarise_table |>
      dplyr::filter(.data$group_level %in% selectedCohorts) |>
      dplyr::mutate(group_level = factor(group_level, levels = selectedCohorts)) |>
      arrange(group_level)

    if (nrow(summarise_table) == 0 || nrow(summarise_characteristics) == 0 ) {
      validate("No results found for selected inputs")
    }


    if (nrow(summarise_table) == 0 || nrow(summarise_characteristics) == 0 ) {
      validate("No results found for selected inputs")
    }

    plotAgeDensity(summarise_table, summarise_characteristics, input$summarise_characteristics_add_interquantile_range)

  }))

  output$plot_age_pyramid <- shiny::renderPlot({
    createAgePyramid()
  })

  output$plot_age_pyramid_download <- shiny::downloadHandler(
    filename = "age_pyramid_plot.png",
    content = function(file) {
      obj <- createAgePyramid()
      ggplot2::ggsave(
        filename = file,
        plot = obj,
        width = as.numeric(input$plot_age_pyramid_download_width),
        height = as.numeric(input$plot_age_pyramid_download_height),
        units = input$plot_age_pyramid_download_units,
        dpi = as.numeric(input$plot_age_pyramid_download_dpi)
      )
    }
  )

  # summarise_large_scale_characteristics -----
  filterLargeScaleCharacteristics <- eventReactive(input$updateLSC, ({
    req(shared_cdm_names())
    req(shared_cohort_names())
    req(inputs_initialized())
    if (is.null(dataFiltered$summarise_large_scale_characteristics)) {
      validate("No large scale characteristics in results")
    }

    lsc_data <- dataFiltered$summarise_large_scale_characteristics |>
      filter(!is.na(estimate_value)) |>
      filter(estimate_value != "-") |>
      visOmopResults::filterSettings(table_name %in% input$summarise_large_scale_characteristics_table_name,
                                     analysis %in% input$summarise_large_scale_characteristics_analysis) |>
      dplyr::filter(cdm_name %in% shared_cdm_names()) |>
      dplyr::filter(group_level  %in% shared_cohort_names()) |>
      dplyr::filter(variable_level  %in% input$summarise_large_scale_characteristics_variable_level)

    validateFilteredResult(lsc_data)

    return(lsc_data)
  }))
  ## Table summarise_characteristics -----
  tidyLargeScaleCharacteristics <- shiny::reactive({
    tidy_lsc <- filterLargeScaleCharacteristics()

    tidy_lsc <- tidy_lsc |>
      tidy() |>
      mutate(concept = paste0(variable_name, " (",
                              concept_id, ")"))

    if("source_concept_id" %in% colnames(tidy_lsc)){
      tidy_lsc <- tidy_lsc |>
        mutate(source_concept = paste0(source_concept_name, " (",
                                       source_concept_id, ")"))
    }

    tidy_lsc <- tidy_lsc |>
      dplyr::select(dplyr::any_of(c("cdm_name",
                                    "cohort_name",
                                    "concept",
                                    "source_concept",
                                    "time_window" = "variable_level",
                                    "count",
                                    "percentage")))
    return(tidy_lsc)
  })

  ## Table summarise_large_scale_characteristics -----
  output$summarise_large_scale_characteristics_tidy <- shiny::renderUI({
    tbl_data <- tidyLargeScaleCharacteristics()

    if("source_concept" %in% colnames(tbl_data)){
      tbl_data <- tbl_data |>
        rename("CDM name" = "cdm_name",
               "Cohort" = "cohort_name",
               "Time window" = "time_window",
               "Concept name (concept ID)" = "concept",
               "Source concept name (concept ID)" = "source_concept")
      cols <- list("Concept name (concept ID)" = colDef(name = "Concept name (concept ID)",
                                                        cell = function(value){
                                                          value_concept <- gsub(".*\\(|\\)","",value)
                                                          url   <- sprintf("https://athena.ohdsi.org/search-terms/terms/%s", value_concept)
                                                          htmltools::tags$a(href = url, target = "_blank", as.character(value))
                                                        }),
                   "Source concept name (concept ID)" = colDef(name = "Source concept name (concept ID)",
                                                               cell = function(value){
                                                                 value_concept <- gsub(".*\\(|\\)","",value)
                                                                 url   <- sprintf("https://athena.ohdsi.org/search-terms/terms/%s", value_concept)
                                                                 htmltools::tags$a(href = url, target = "_blank", as.character(value))
                                                               }),
                   count = colDef(format = colFormat(separators = TRUE)),
                   percentage = colDef(format = colFormat(percent = TRUE))
      )
    } else {
      tbl_data <- tbl_data |>
        rename("CDM name" = "cdm_name",
               "Cohort" = "cohort_name",
               "Time window" = "time_window",
               "Concept name (concept ID)" = "concept")
      cols <- list("Concept name (concept ID)" = colDef(name = "Concept name (concept ID)",
                                                        cell = function(value){
                                                          value_concept <- gsub(".*\\(|\\)","",value)
                                                          url   <- sprintf("https://athena.ohdsi.org/search-terms/terms/%s", value_concept)
                                                          htmltools::tags$a(href = url, target = "_blank", as.character(value))
                                                        }),
                   count = colDef(format = colFormat(separators = TRUE)),
                   percentage = colDef(format = colFormat(percent = TRUE))
      )

    }

    reactable(tbl_data |>
                mutate(percentage = percentage / 100), # to use colFormat
              defaultSorted = list("percentage"  = "desc"),
              columns = cols,
              filterable = TRUE,
              searchable = TRUE,
              defaultPageSize = 25,
              highlight = TRUE,
              striped = TRUE,
              compact = TRUE,
              showSortable = TRUE) |>
      reactablefmtr_add_title("Large scale characteristics",
                              font_size = 25,
                              font_weight = "normal") |>
      reactablefmtr_add_subtitle("Summary of all records from clinical tables within a time window. The sampled cohort represents individuals from the original cohort, the matched cohort comprises individuals of similar age and sex from the database.",
                                 font_size = 15,
                                 font_weight = "normal")

  })

  output$summarise_large_scale_characteristics_tidy_download <- shiny::downloadHandler(
    filename = "summarise_large_scale_characteristics_tidy.csv",
    content = function(file) {
      tidyLargeScaleCharacteristics() |>
        readr::write_csv(file = file)
    }
  )
  ## Table summarise_large_scale_characteristics -----
  createTableLargeScaleCharacteristics <- shiny::reactive({

    lsc_data <- filterLargeScaleCharacteristics()

    lsc_data |>
      CohortCharacteristics::tableTopLargeScaleCharacteristics(
        topConcepts = input$summarise_large_scale_characteristics_top_concepts) |>
      tab_header(
        title = "Top concepts in large scale characteristics",
        subtitle = "Summary of the most prevalent concepts by percentage across each cohort."
      ) |>
      tab_options(
        heading.align = "left"
      )
  })
  output$summarise_large_scale_characteristics_gt <- gt::render_gt({
    createTableLargeScaleCharacteristics()
  })
  output$summarise_large_scale_characteristics_gt_download <- shiny::downloadHandler(
    filename = "summarise_large_scale_characteristics_gt.docx",
    content = function(file) {
      obj <- createTableLargeScaleCharacteristics()
      gt::gtsave(data = obj, filename = file)
    }
  )

  # compare large_scale_characteristics ----
  getComparedCohorts <- eventReactive(input$updateCompareLSC, ({
    req(shared_cdm_names())
    req(shared_cohort_names())
    req(inputs_initialized())
    cohort <- shared_cohort_names()

    if(length(cohort) > 1){
      validate("Please select only one cohort")
    }
    if(length(cohort) == 0){
      validate("Please select a cohort")
    }

    cohort1 <- switch(input$compare_large_scale_characteristics_cohort_1,
                      "original" = cohort,
                      "sampled" = paste0(cohort,"_sampled"),
                      "matched" = paste0(cohort,"_matched"))

    cohort2 <- switch(input$compare_large_scale_characteristics_cohort_2,
                      "original" = input$compare_large_scale_characteristics_cohort_compare,
                      "sampled" = paste0(input$compare_large_scale_characteristics_cohort_compare,"_sampled"),
                      "matched" = paste0(input$compare_large_scale_characteristics_cohort_compare,"_matched"))

    if(length(cohort2) > 1){
      validate("Please select only one comparator cohort")
    }
    if(length(cohort2) == 0){
      validate("Please select a comparator cohort")
    }

    return(list("cohort1" = cohort1,
                "cohort2" = cohort2))
  }))
  filterCompareLargeScaleCharacteristics <- shiny::reactive({
    shiny::observeEvent(shared_cdm_names(), {
      updatePickerInput(session, "compare_large_scale_characteristics_cdm_name",   selected = shared_cdm_names())
    })

    shiny::observeEvent(shared_cohort_names(), {
      updatePickerInput(session, "compare_large_scale_characteristics_cohort_name",   selected = shared_cohort_names())
    })

    if (is.null(dataFiltered$summarise_large_scale_characteristics)) {
      validate("No large scale characteristics in results")
    }

    cohorts <- getComparedCohorts()
    cohort1 <- cohorts$cohort1
    cohort2 <- cohorts$cohort2

    lsc_filtered <- dataFiltered$summarise_large_scale_characteristics |>
      visOmopResults::filterSettings(.data$table_name %in% input$compare_large_scale_characteristics_table_name,
                                     .data$analysis %in% input$compare_large_scale_characteristics_analysis) |>
      dplyr::filter(.data$cdm_name %in% shared_cdm_names(),
                    .data$group_level %in% c(cohort1, cohort2),
                    .data$variable_level %in% input$compare_large_scale_characteristics_variable_level)

    validateFilteredResult(lsc_filtered)

    return(lsc_filtered)
  })
  ## Tidy compare large_scale_characteristics ----
  createTidyDataCompareLargeScaleCharacteristics <- shiny::reactive({

    lscFiltered <- filterCompareLargeScaleCharacteristics()
    cohorts <- getComparedCohorts()
    target_cohort     <- cohorts$cohort1
    comparator_cohort <- cohorts$cohort2

    if("matchedSample" %in% (lscFiltered |> omopgenerics::settings() |> colnames())){
      msg <- paste0("Matched cohort was created based on a subsample of ", omopgenerics::settings(lscFiltered) |> dplyr::pull("matchedSample") |> unique()," individuals.")
    }else{
      msg <- ""
    }
    lsc <- lscFiltered |>
      dplyr::filter(.data$estimate_name == "percentage") |>
      tidy() |>
      tidyr::pivot_wider(names_from = cohort_name,
                         values_from = percentage)

    lsc <- lscFiltered |>
      dplyr::filter(.data$estimate_name == "percentage") |>
      tidy() |>
      tidyr::pivot_wider(names_from = cohort_name,
                         values_from = percentage)

    missing_target_col <- setdiff(target_cohort, colnames(lsc))
    if(length(missing_target_col)>0){
      lsc[missing_target_col] <- NA_integer_
    }

    if(isTRUE(input$compare_large_scale_characteristics_impute_missings)){
      lsc <- lsc |>
        dplyr::mutate(across(c(target_cohort, comparator_cohort), ~if_else(is.na(.x), 0, .x)))
    }

    lsc <- lsc |>
      dplyr::mutate(across(c(target_cohort, comparator_cohort), ~ as.numeric(.x)/100)) |>
      dplyr::mutate(smd = (!!sym(target_cohort) - !!sym(comparator_cohort))/sqrt((!!sym(target_cohort)*(1-!!sym(target_cohort)) + !!sym(comparator_cohort)*(1-!!sym(comparator_cohort)))/2)) |>
      dplyr::mutate(smd = if_else(is.na(smd), 0, round(smd, 3))) |>
      dplyr::arrange(desc(smd))  |>
      mutate(concept = paste0(variable_name, " (",concept_id, ")"))

    if("source_concept_name" %in% colnames(lsc)){
      lsc <- lsc |>
        mutate(source_concept = paste0(source_concept_name, " (",source_concept_id, ")"))
    }

    lsc <- lsc |>
      select(dplyr::any_of(c(
        "CDM name" = "cdm_name",
        "Concept name (concept ID)" = "concept",
        "Source concept name (concept ID)" = "source_concept",
        "Table" = "table_name",
        "Time window" = "variable_level",
        target_cohort,
        comparator_cohort,
        "Standardised mean difference" = "smd"))) |>
      dplyr::mutate(msg)

    return(lsc)
  })

  output$compare_large_scale_characteristics_tidy <- reactable::renderReactable({
    cohorts <- getComparedCohorts()
    target_cohort <- cohorts$cohort1
    comparator_cohort <- cohorts$cohort2

    tbl <- createTidyDataCompareLargeScaleCharacteristics()
    msg <- tbl$msg |> unique()
    tbl <- tbl |> dplyr::select(-"msg")

    if("Source concept name (concept ID)" %in% colnames(tbl)){
      cols <- list(target_cohort = colDef(name = paste0(target_cohort, " percentage"),
                                          format = colFormat(percent = TRUE),
                                          sortNALast = TRUE),
                   comparator_cohort = colDef(name = paste0(comparator_cohort, " percentage"),
                                              format = colFormat(percent = TRUE),
                                              sortNALast = TRUE),
                   "Concept name (concept ID)" = colDef(name = "Concept name (concept ID)",
                                                        cell = function(value){
                                                          value_concept <- gsub(".*\\(|\\)","",value)
                                                          url   <- sprintf("https://athena.ohdsi.org/search-terms/terms/%s", value_concept)
                                                          htmltools::tags$a(href = url, target = "_blank", as.character(value))
                                                        }),
                   "Source concept name (concept ID)" = colDef(name = "Source  concept name (concept ID)",
                                                               cell = function(value){
                                                                 value_concept <- gsub(".*\\(|\\)","",value)
                                                                 url   <- sprintf("https://athena.ohdsi.org/search-terms/terms/%s", value_concept)
                                                                 htmltools::tags$a(href = url, target = "_blank", as.character(value))
                                                               }),
                   "Standardised mean difference" = colDef(name = "Standardised mean difference",
                                                           sortNALast = TRUE)
      )
    } else {
      cols <- list(target_cohort = colDef(name = paste0(target_cohort, " percentage"),
                                          format = colFormat(percent = TRUE),
                                          sortNALast = TRUE),
                   comparator_cohort = colDef(name = paste0(comparator_cohort, " percentage"),
                                              format = colFormat(percent = TRUE),
                                              sortNALast = TRUE),
                   "Concept name (concept ID)" = colDef(name = "Concept name (concept ID)",
                                                        cell = function(value){
                                                          value_concept <- gsub(".*\\(|\\)","",value)
                                                          url   <- sprintf("https://athena.ohdsi.org/search-terms/terms/%s", value_concept)
                                                          htmltools::tags$a(href = url, target = "_blank", as.character(value))
                                                        }),
                   "Standardised mean difference" = colDef(name = "Standardised mean difference",
                                                           sortNALast = TRUE)
      )
    }


    names(cols)[1] <- target_cohort
    names(cols)[2] <- comparator_cohort

    table <- reactable::reactable(tbl,
                                  defaultSorted = list("Standardised mean difference"  = "desc"),
                                  columns = cols,
                                  filterable = TRUE,
                                  searchable = TRUE,
                                  defaultPageSize = 25,
                                  highlight = TRUE,
                                  striped = TRUE,
                                  compact = TRUE,
                                  showSortable = TRUE)

    return(table)
  })

  output$compare_large_scale_characteristics_tidy_download <- shiny::downloadHandler(
    filename = "compare_large_scale_characteristics_tidy.csv",
    content = function(file) {
      createTidyDataCompareLargeScaleCharacteristics() |>
        readr::write_csv(file = file)
    }
  )

  ## Plot compare large_scale_characteristics ----
  getPlotlyCompareLsc <- shiny::reactive({
    cohorts <- getComparedCohorts()
    plotComparedLsc(lsc = filterCompareLargeScaleCharacteristics(),
                    cohorts = c(cohorts$cohort1, cohorts$cohort2),
                    colour = c(input$compare_large_scale_characteristics_colour_1),
                    facet  = c(input$compare_large_scale_characteristics_facet_1),
                    imputeMissings = input$compare_large_scale_characteristics_impute_missings
    )
  })

  output$plotly_compare_lsc <- renderPlotly({
    ggplotly(getPlotlyCompareLsc(), tooltip = "Details")
  })

  output$plot_compare_large_scale_characteristics_download <- shiny::downloadHandler(
    filename = "output_ggplot2_compare_large_scale_characteristics.png",
    content = function(file) {
      obj <- getPlotlyCompareLsc()
      ggplot2::ggsave(
        filename = file,
        plot = obj,
        width = as.numeric(input$plot_compare_large_scale_characteristics_download_width),
        height = as.numeric(input$plot_compare_large_scale_characteristics_download_height),
        units = input$plot_compare_large_scale_characteristics_download_units,
        dpi = as.numeric(input$plot_compare_large_scale_characteristics_download_dpi)
      )
    }
  )

  # summarise_cohort_overlap -----
  filterCohortOverlap <- eventReactive(input$updateCompareCohorts, ({
    req(shared_cdm_names())
    req(shared_cohort_names())
    req(inputs_initialized())
    if (is.null(dataFiltered$summarise_cohort_overlap)) {
      validate("No cohort overlap in results")
    }

    result <- dataFiltered$summarise_cohort_overlap |>
      visOmopResults::splitGroup(keep = TRUE) |>
      dplyr::filter(.data$cdm_name %in% shared_cdm_names(),
                    .data$cohort_name_reference %in% shared_cohort_names(),
                    .data$cohort_name_comparator %in% input$summarise_cohort_overlap_cohort_comparator,
                    .data$variable_name %in% input$summarise_cohort_overlap_variable_name
      ) |>
      dplyr::select(-visOmopResults::groupColumns(dataFiltered$summarise_cohort_overlap))

    validateFilteredResult(result)
    return(result)
  }))
  ## Table cohort_overlap -----
  createTableCohortOverlap <- shiny::reactive({

    result <- filterCohortOverlap()

    CohortCharacteristics::tableCohortOverlap(
      result,
      uniqueCombinations = input$summarise_cohort_overlap_gt_uniqueCombinations,
      header = input$summarise_cohort_overlap_gt_header,
      groupColumn = input$summarise_cohort_overlap_gt_groupColumn,
      hide = c(input$summarise_cohort_overlap_gt_hide,
               "overlap_by",
               "diagnostic", "matchedSample", "phenotyper_version")
    ) |>
      tab_header(
        title = "Cohort overlap",
        subtitle = "Overlap is where the same individual is in both cohorts. Note their cohort entries do not necessarily overlap."
      ) |>
      tab_options(
        heading.align = "left"
      )
  })
  output$summarise_cohort_overlap_gt <- gt::render_gt({
    createTableCohortOverlap()
  })
  output$summarise_cohort_overlap_gt_download <- shiny::downloadHandler(
    filename = "summarise_cohort_overlap_gt.docx",
    content = function(file) {
      obj <- createTableCohortOverlap()
      gt::gtsave(data = obj, filename = file)
    }
  )

  ## Plot cohort_overlap -----
  createPlotCohortOverlap <- shiny::reactive({
    result <- filterCohortOverlap()

    CohortCharacteristics::plotCohortOverlap(
      result,
      colour = input$summarise_cohort_overlap_plot_colour,
      facet = input$summarise_cohort_overlap_plot_facet,
      uniqueCombinations = input$summarise_cohort_overlap_plot_uniqueCombinations
    )
  })
  output$summarise_cohort_overlap_plot <- plotly::renderPlotly({
    createPlotCohortOverlap()
  })
  output$summarise_cohort_overlap_plot_download <- shiny::downloadHandler(
    filename = "summarise_cohort_overlap_plot.png",
    content = function(file) {
      obj <- createPlotCohortOverlap()
      ggplot2::ggsave(
        filename = file,
        plot = obj,
        width = as.numeric(input$summarise_cohort_overlap_plot_download_width),
        height = as.numeric(input$summarise_cohort_overlap_plot_download_height),
        units = input$summarise_cohort_overlap_plot_download_units,
        dpi = as.numeric(input$summarise_cohort_overlap_plot_download_dpi)
      )
    }
  )

  # summarise_cohort_timing ----
  filterCohortTiming <- eventReactive(input$updateCompareCohorts, ({
    req(shared_cdm_names())
    req(shared_cohort_names())
    req(inputs_initialized())
    if (is.null(dataFiltered$summarise_cohort_timing)) {
      validate("No cohort timing in results")
    }

    result <- dataFiltered$summarise_cohort_timing |>
      visOmopResults::splitGroup(keep = TRUE) |>
      dplyr::filter(.data$cdm_name %in% shared_cdm_names(),
                    .data$cohort_name_reference %in% shared_cohort_names(),
                    .data$cohort_name_comparator %in% input$summarise_cohort_overlap_cohort_comparator) |>
      dplyr::select(-visOmopResults::groupColumns(dataFiltered$summarise_cohort_timing))

    validateFilteredResult(result)

    return(result)
  }))

  ## Table cohort_timing -----
  createTableCohortTiming <- shiny::reactive({

    result <- filterCohortTiming()

    CohortCharacteristics::tableCohortTiming(
      result,
      timeScale = input$summarise_cohort_timing_gt_time_scale,
      uniqueCombinations = input$summarise_cohort_timing_gt_uniqueCombinations,
    ) |>
      tab_header(
        title = "Cohort timing",
        subtitle = "Cohort timing refers to the time between an individual entering one cohort and another cohort."
      ) |>
      tab_options(
        heading.align = "left"
      )
  })
  output$summarise_cohort_timing_gt <- gt::render_gt({
    createTableCohortTiming()
  })
  output$summarise_cohort_timing_gt_download <- shiny::downloadHandler(
    filename = "summarise_cohort_timing_gt.docx",
    content = function(file) {
      obj <- createTableCohortTiming()
      gt::gtsave(data = obj, filename = file)
    }
  )

  ## Plot cohort_timing -----
  createPlotCohortTiming <- shiny::reactive({
    CohortCharacteristics::plotCohortTiming(
      filterCohortTiming(),
      plotType = "densityplot",
      timeScale = input$summarise_cohort_timing_plot_time_scale,
      uniqueCombinations = input$summarise_cohort_timing_plot_uniqueCombinations,
      facet = input$summarise_cohort_timing_plot_facet,
      colour = input$summarise_cohort_timing_plot_colour
    )
  })
  output$summarise_cohort_timing_plot <- shiny::renderUI({
    renderPlot(createPlotCohortTiming())
  })
  output$summarise_cohort_timing_plot_download <- shiny::downloadHandler(
    filename = "summarise_cohort_timing_plot.png",
    content = function(file) {
      obj <- createPlotCohortTiming()
      ggplot2::ggsave(
        filename = file,
        plot = obj,
        width = as.numeric(input$summarise_cohort_timing_plot_download_width),
        height = as.numeric(input$summarise_cohort_timing_plot_download_height),
        units = input$summarise_cohort_timing_plot_download_units,
        dpi = as.numeric(input$summarise_cohort_timing_plot_download_dpi)
      )
    }
  )
  # summarise cohort survival -----
  filterCohortSurvival <- eventReactive(input$updateCohortSurvival, ({
    req(shared_cdm_names())
    req(shared_cohort_names())
    req(inputs_initialized())
    if (is.null(dataFiltered$survival_estimates)) {
      validate("No survival in results")
    }

    if(input$survival_porbability_include_matches){
      cohorts <- c(paste0(shared_cohort_names(),"_sampled"),
                   paste0(shared_cohort_names(),"_matched"))
    }else{
      cohorts <- shared_cohort_names()
    }
    result <- omopgenerics::bind(
      dataFiltered[str_detect(names(dataFiltered), "survival_")]) |>
      dplyr::filter(.data$cdm_name %in% shared_cdm_names()) |>
      visOmopResults::filterGroup(.data$target_cohort %in% cohorts)

    validateFilteredResult(result)
    return(result)
  }))


  getTimeScale <- eventReactive(input$updateCohortSurvival, ({
    timeScale <- input$survival_probability_time_scale
    return(timeScale)
  }))

  ## Table cohort survival -----
  createTableSurvival <- shiny::reactive({
    result <- filterCohortSurvival()
    timeScale <- getTimeScale()

    table <- CohortSurvival::tableSurvival(result,
                                           timeScale = timeScale,
                                           header    = input$survival_table_header,
                                           groupColumn = input$survival_table_groupColumn) |>
      tab_header(
        title = "Single Event Survival Summary",
        subtitle = "Time from cohort entry to death"
      ) |>
      tab_options(
        heading.align = "left"
      )

    return(table)
  })

  output$summarise_cohort_survival_gt <- gt::render_gt({
    createTableSurvival()
  })
  output$summarise_cohort_survival_gt_download <- shiny::downloadHandler(
    filename = "summarise_cohort_survival_gt.docx",
    content = function(file) {
      obj <- createTableSurvival()
      gt::gtsave(data = obj, filename = file)
    }
  )

  ## Plot cohort survival ----
  createPlotSurvival <- shiny::reactive({
    result <- filterCohortSurvival()
    timeScale <- getTimeScale()

    CohortSurvival::plotSurvival(result,
                                 timeScale = timeScale,
                                 ribbon = input$survival_plot_ribbon,
                                 facet = input$survival_plot_facet,
                                 colour = input$survival_plot_colour,
                                 logLog = input$survival_plot_log_log,
                                 cumulativeFailure = input$survival_plot_cf) +
      labs(color = "Color") +
      guides(fill = "none")
  })
  output$summarise_cohort_survival_plot <- shiny::renderUI({
    if(isTRUE(input$survival_plot_interactive)){
      plot <- plotly::ggplotly(createPlotSurvival())
    } else {
      plot <- renderPlot(createPlotSurvival())
    }
    plot
  })
  output$summarise_cohort_survival_plot_download <- shiny::downloadHandler(
    filename = "summarise_cohort_survival_plot.png",
    content = function(file) {
      obj <- createPlotSurvival()
      ggplot2::ggsave(
        filename = file,
        plot = obj,
        width = as.numeric(input$summarise_cohort_survival_plot_download_width),
        height = as.numeric(input$summarise_cohort_survival_plot_download_height),
        units = input$summarise_cohort_survival_plot_download_units,
        dpi = as.numeric(input$summarise_cohort_survival_plot_download_dpi)
      )
    }
  )

  # incidence -----
  filterIncidence <- eventReactive(input$updateIncidence, ({
    req(shared_cdm_names())
    req(shared_cohort_names())
    req(inputs_initialized())
    if (is.null(dataFiltered$incidence)) {
      validate("No incidence in results")
    }

    result <- dataFiltered$incidence |>
      filter(cdm_name %in% shared_cdm_names()) |>
      filterGroup(outcome_cohort_name %in% shared_cohort_names()) |>
      filterSettings(denominator_age_group %in%
                       input$incidence_denominator_age_group,
                     denominator_sex %in%
                       input$incidence_denominator_sex,
                     denominator_days_prior_observation %in%
                       input$incidence_denominator_days_prior_observation) |>
      filterAdditional(analysis_interval %in%
                         input$incidence_analysis_interval)

    validateFilteredResult(result)
    return(result)
  }))

  ## Table incidence -----
  createTableIncidence <- shiny::reactive({

    IncidencePrevalence::tableIncidence(
      filterIncidence(),
      groupColumn = c("cdm_name", "outcome_cohort_name"),
      hide = "denominator_cohort_name",
      settingsColumn = c("denominator_age_group",
                         "denominator_sex",
                         "denominator_days_prior_observation",
                         "outcome_cohort_name")
    ) |>
      tab_header(
        title = "Incidence estimates",
        subtitle = "Incidence rates estimated for outcomes of interest"
      ) |>
      tab_options(
        heading.align = "left"
      )
  })
  output$incidence_gt <- gt::render_gt({
    createTableIncidence()
  })
  output$incidence_gt_download <- shiny::downloadHandler(
    filename = "incidence_gt.docx",
    content = function(file) {
      obj <- createTableIncidence()
      gt::gtsave(data = obj, filename = file)
    }
  )
  ## Plot incidence -----
  createPlotIncidence <- shiny::reactive({
    result <- filterIncidence()

    x <- input$incidence_plot_x
    y <- input$incidence_plot_y
    facet      <- input$incidence_plot_facet
    facet_free <- input$incidence_plot_facet_free
    colour     <- input$incidence_plot_colour

    # Plot incidence estimates
    if(y == "Incidence"){
      plot <- IncidencePrevalence::plotIncidence(
        result,
        x = x,
        ribbon = FALSE,
        facet = facet,
        colour = colour
      )
      plot$data <- plot$data |>
        filter(incidence_100000_pys > 0)

      if(!is.null(facet) && isTRUE(facet_free)){
        plot <- plot +
          facet_wrap(facets = facet, scales = "free")
      }
    }else{
      # Plot incidence population
      y_input <- case_when(
        y == "Denominator count" ~ "denominator_count",
        y == "Denominator person years" ~ "person_years",
        y == "Outcome count" ~ "outcome_count"
      )
      if(!is.null(facet) && isTRUE(facet_free)){
        plot <- plotIncidencePopulation(x = x,
                                        y =  y_input,
                                        result = result,
                                        facet  = NULL,
                                        colour = colour

        ) +
          facet_wrap(facets = facet, scales = "free")
      } else {
        plot <- plotIncidencePopulation(x = x,
                                        y =  y_input,
                                        result = result,
                                        facet  = facet,
                                        colour = colour

        )
      }
    }
    return(plot)
  })

  output$incidence_plot <- shiny::renderUI({
    if(isTRUE(input$incidence_plot_interactive)){
      plot <- plotly::ggplotly(createPlotIncidence())
    } else {
      plot <- renderPlot(createPlotIncidence())
    }
    plot
  })
  output$incidence_plot_download <- shiny::downloadHandler(
    filename = "incidence_plot.png",
    content = function(file) {
      obj <- createPlotIncidence()
      ggplot2::ggsave(
        filename = file,
        plot = obj,
        width = as.numeric(input$incidence_plot_download_width),
        height = as.numeric(input$incidence_plot_download_height),
        units = input$incidence_plot_download_units,
        dpi = as.numeric(input$incidence_plot_download_dpi)
      )
    }
  )

  # prevalence -----
  filterPrevalence <- eventReactive(input$updatePrevalence, ({
    req(shared_cdm_names())
    req(shared_cohort_names())
    req(inputs_initialized())
    if (is.null(dataFiltered$prevalence)) {
      validate("No prevalence in results")
    }

    result <- dataFiltered$prevalence |>
      filter(cdm_name %in% shared_cdm_names()) |>
      filterGroup(outcome_cohort_name %in% shared_cohort_names()) |>
      filterSettings(denominator_age_group %in%
                       input$prevalence_denominator_age_group,
                     denominator_sex %in%
                       input$prevalence_denominator_sex,
                     denominator_days_prior_observation %in%
                       input$prevalence_denominator_days_prior_observation) |>
      filterAdditional(analysis_interval %in%
                         input$prevalence_analysis_interval)

    validateFilteredResult(result)

    return(result)
  }))
  ## Table prevalence ----
  createTablePrevalence <- shiny::reactive({
    result <- filterPrevalence()

    IncidencePrevalence::tablePrevalence(
      result,
      groupColumn = c("cdm_name", "outcome_cohort_name"),
      hide = "denominator_cohort_name",
      settingsColumn = c("denominator_age_group",
                         "denominator_sex",
                         "denominator_days_prior_observation",
                         "outcome_cohort_name")
    ) |>
      tab_header(
        title = "Period Prevalence Estimates",
        subtitle = "Period prevalence estimated for outcomes of interest"
      ) |>
      tab_options(
        heading.align = "left"
      )
  })
  output$prevalence_gt <- gt::render_gt({
    createTablePrevalence()
  })
  output$prevalence_gt_download <- shiny::downloadHandler(
    filename = "period_prevalence_gt.docx",
    content = function(file) {
      obj <- createTablePrevalence()
      gt::gtsave(data = obj, filename = file)
    }
  )
  ## Plot prevalence ----
  createPlotPrevalence <- shiny::reactive({
    result <- filterPrevalence()

    x <- input$prevalence_plot_x
    y <- input$prevalence_plot_y
    facet <- input$prevalence_plot_facet
    facet_free <- input$prevalence_plot_facet_free
    colour <- input$prevalence_plot_colour

    if(y == "Prevalence"){
      plot <- IncidencePrevalence::plotPrevalence(
        result,
        x = x,
        ribbon = FALSE,
        facet = facet,
        colour = colour
      )
      plot$data$prevalence_95CI_lower <- round(plot$data$prevalence_95CI_lower, 6)
      plot$data$prevalence_95CI_upper <- round(plot$data$prevalence_95CI_upper, 6)
      plot$data <- plot$data |>
        dplyr::mutate(prevalence = round((outcome_count/denominator_count),6))

      if(!is.null(facet) && isTRUE(facet_free)){
        plot <- plot +
          facet_wrap(facets = facet, scales = "free")
      }
    }else{
      y_input <- case_when(
        y == "Denominator count" ~ "denominator_count",
        y == "Outcome count" ~ "outcome_count"
      )
      if(!is.null(facet) && isTRUE(input$facet_free)){
        plot <- IncidencePrevalence::plotPrevalencePopulation(
          result = result,
          x = x,
          y = y_input,
          facet = NULL,
          colour = colour) +
          facet_wrap(facets = facet, scales = "free")
      } else {
        plot <- IncidencePrevalence::plotPrevalencePopulation(
          result = result,
          x = x,
          y = y_input,
          facet = facet,
          colour = colour
        )
      }
    }

    return(plot)

  })

  output$prevalence_plot <- shiny::renderUI({
    if(isTRUE(input$prevalence_plot_interactive)){
      plot <- plotly::ggplotly(createPlotPrevalence())
    } else {
      plot <- renderPlot(createPlotPrevalence())
    }
    plot

  })
  output$prevalence_plot_download <- shiny::downloadHandler(
    filename = "prevalence_plot.png",
    content = function(file) {
      obj <- createPlotPrevalence()
      ggplot2::ggsave(
        filename = file,
        plot = obj,
        width = as.numeric(input$prevalence_plot_download_width),
        height = as.numeric(input$prevalence_plot_download_height),
        units = input$prevalence_plot_download_units,
        dpi = as.numeric(input$prevalence_plot_download_dpi)
      )
    }
  )

  # log file -----
  output$summarise_log_file_gt <- gt::render_gt({
    dataFiltered$summarise_log_file |>
      omopgenerics::tidy() |>
      dplyr::mutate(log_id = as.integer(log_id)) |>
      dplyr::arrange("cdm_name", "log_id")  |>
      dplyr::select(cdm_name, variable_name,
                    elapsed_time) |>
      dplyr::mutate(elapsed_time =
                      dplyr::if_else(!is.na(elapsed_time),
                                     sprintf("%d hours, %d minutes, %d seconds",
                                             lubridate::hour(elapsed_time),
                                             lubridate::minute(elapsed_time),
                                             lubridate::second(elapsed_time)),
                                     NA_character_)) |>
      dplyr::mutate(elapsed_time = stringr::str_replace(
        elapsed_time, "0 hours, 0 minutes, ", "")) |>
      dplyr::mutate(elapsed_time = stringr::str_replace(
        elapsed_time, "0 hours, ", "")) |>
      dplyr::mutate(elapsed_time = stringr::str_replace(
        elapsed_time, "1 hours", "1 hour")) |>
      dplyr::mutate(elapsed_time = stringr::str_replace(
        elapsed_time, "1 minutes", "1 minute")) |>
      dplyr::mutate(elapsed_time = stringr::str_replace(
        elapsed_time, "1 seconds", "1 second")) |>
      dplyr::rename("task" = "variable_name",
                    estimate_value = "elapsed_time") |>
      dplyr::mutate(estimate_type = "character",
                    estimate_name = "Time taken") |>
      visOmopResults::visTable(
        header = c("cdm_name", "estimate_name"),
        rename = c("Database name" = "cdm_name"),
        hide = "estimate_type") |>
      gt::tab_options(container.width = "100%")
  })

  # expectations ----
  createExpectationsOutput <- function(trigger_input, output_id) {
    filteredExpectations <- eventReactive(trigger_input(), {
      req(shared_cohort_names())
      req(inputs_initialized())
      validateExpectations(expectations)

      section_name <- gsub("_expectations","",output_id)

      result <- expectations |>
        dplyr::filter(.data$cohort_name %in% shared_cohort_names())

      section_name <- gsub("_expectations","",output_id)

      if("diagnostics" %in% colnames(expectations)){
        result <- result |>
          mutate(diagnostics = strsplit(diagnostics, ",\\s*"))  |>
          unnest(diagnostics)

        validateExpectationSections(result)

        result <- result |>
          dplyr::filter(.data$diagnostics %in% section_name)
      }

      if(nrow(result) == 0){
        section_name_nice <- stringr::str_replace_all(section_name, "_", " ")
        validate(glue::glue("No expectations for {section_name_nice} results."))
      }
      result
    })

    output[[output_id]] <- reactable::renderReactable({
      filteredExpectations() |>
        PhenotypeR::tableCohortExpectations()
    })
  }

  createExpectationsOutput(reactive(input$updateCohortCount), "cohort_count_expectations")
  createExpectationsOutput(reactive(input$updateCohortCharacteristics), "cohort_characteristics_expectations")
  createExpectationsOutput(reactive(input$updateLSC), "large_scale_characteristics_expectations")
  createExpectationsOutput(reactive(input$updateCompareLSC), "compare_large_scale_characteristics_expectations")
  createExpectationsOutput(reactive(input$updateCompareCohorts), "compare_cohorts_expectations")
  createExpectationsOutput(reactive(input$updateCohortSurvival), "cohort_survival_expectations")
}


