# ---------------------------
#
# Script name: visualise_server.R
# data4health Shiny app - visualise server
#
# Purpose of script: creates the server module for the
# visualisation tab of the shiny app
# Author: Daniela Lührsen
# Date Created: 2026-02
# Email: daniela.luhrsen@bsc.es
#
# ---------------------------

visualise_server <- function(id, data_store, app_state) {
  moduleServer(id, function(input, output, session) {

    ns <- NS(id)

    # load  -----------------------------------------------------------------

    # UI - for loading data
    output$vis_load <- renderUI({
      # if user is coming from the cleaning tab,
      # show message that data is loaded and give option to upload new data
      # if (app_state$go_visregation) {
      if (length(data_store$get()) != 0) {
        tagList(
          column(
            width = 6,
            helpText("The pre-processed data from the previous tab is automatically loaded.")
          ),
          column(
            width = 6,
          helpText("If you wish to use a different dataset, you can upload it here. Each row should be a case, and the columns (with headers) are information about it."),
          fileInput(ns("vis_file_input"), "",
                    accept = c('.csv','.dbf','.dbc', '.rds', '.xlsx', '.xls', '.json'),
                    multiple = TRUE)
          )
        )
      # if user is coming directly to the visualisation tab, show only the file input
      } else {
        helpText("Each row should be a case, and the columns (with headers) are information about it.")
        fileInput(ns("vis_file_input"), "",
                  accept = c('.csv','.dbf','.dbc', '.rds', '.xlsx', '.xls', '.json'),
                  multiple = TRUE)
      }
    })

    # load dataset for filter
    observeEvent(input$vis_file_input, {
      inFile <- input$vis_file_input
      data_store$set(data4health::d4h_load(inFile$datapath))
    })

    # updating the dropdown options for cases, timesteps, and spatial scale
    observe({

      req(data_store$get())
      col_names <- names(data_store$get())

      updateSelectInput(session, "cases_col",
                        choices = col_names,
                        selected = ifelse("cases" %in% col_names, "cases", col_names[1]))
      updateSelectInput(session, "time_col",
                        choices = col_names, selected = col_names[1])
      updateSelectInput(session, "area_col",
                        choices = col_names, selected = "-")
    })

    # timeseries ------------------------------------------------------------

    # container for warning message
    warning_msg_ts <- reactiveVal("")

    # make the slider interactive, to always show the range of the data
    output$ts_slider <- renderUI({
      req(data_store$get())

      df <- data_store$get()
      if (inherits(df[[input$time_col]], "Date")) {
        # compute min & max dates
        min_date <- min(df[[input$time_col]], na.rm = TRUE)
        max_date <- max(df[[input$time_col]], na.rm = TRUE)
      } else {
        min_date <- as.Date("2000-01-01", "%Y-%m-%d")
        max_date  <- as.Date("2025-12-31", "%Y-%m-%d")
      }

      # create slider dynamically
      sliderInput(
        ns("Timestep"),
        label = "Timeperiod",
        min = min_date,
        max = max_date,
        value = c(min_date, max_date),
        timeFormat = "%Y-%m-%d"
      )
    })

    # reactive expression
    ts <- eventReactive(input$tsbutton, {
      req(input$cases_col, input$time_col, input$Timestep) # requiring these four elements
      df <- data_store$get() # data that GHRexplore can work with
      df[[input$cases_col]] <- as.numeric(df[[input$cases_col]]) # making sure cases are numeric
      df[[input$time_col]] <- as.Date(df[[input$time_col]]) # making sure timestep is in date format
      # constrain time based on slider
      df[df[[input$time_col]] >= input$Timestep[1] &
           df[[input$time_col]] <= input$Timestep[2], ]
    })

clean_optional <- function(x) {
  if (is.null(x) || length(x) == 0 || x == "") NULL else x
}

    # plot
    output$ts_plot <- renderPlot({
      warning_msg_ts("")  # reset each time

      withCallingHandlers(
        {
          GHRexplore::plot_timeseries(
            data = ts(),
            var = input$cases_col,
            time = input$time_col,
            type = "counts",
            area = clean_optional(input$area_col),
            title = "Time series"
          )
        },
        # add warning message
        warning = function(wrn) {
          if (grepl("More than 15 time series detected", wrn$message, fixed = TRUE)) {
            warning_msg_ts("Warning: more than 15 time series detected. Consider spatial aggregation (previous step).")
          }
          invokeRestart("muffleWarning")
        }
      )
    })

    # text for warning message
    output$warning_msg_ts <- renderText({
      warning_msg_ts()
    })

    # button for download
    output$download_timeseries <- downloadHandler(
      filename = function() {
        paste0("d4h_timeseries_", format(Sys.time(), "%Y%m%d_%H%M"),".png")
      },
      content = function(file) {
        png(file, width = 800, height = 600)
        plot_obj <- GHRexplore::plot_timeseries(
          data = ts(),
          var  = input$cases_col,
          time = input$time_col,
          type = "counts",
          area = input$area_col
        )
        dev.off()
      })

    # seasonality -----------------------------------------------------------

    # container for warning message
    warning_msg_season <- reactiveVal("")

    # reactive expression
    season <- eventReactive(input$seasonbutton, {
      req(input$cases_col, input$time_col, input$area_col)
      df <- data_store$get()
      df[[input$cases_col]] <- as.numeric(df[[input$cases_col]])
      df[[input$time_col]] <- as.Date(df[[input$time_col]])
      df
    })

    # plot
    output$season_plot <- renderPlot({
      warning_msg_season("")  # reset each time

      withCallingHandlers(
        {
          GHRexplore::plot_seasonality(
            data = season(),
            var = input$cases_col,
            time = input$time_col,
            type = "counts",
            area = input$area_col,
            title = "Seasonality"
          )
        },
        # add warning message
        warning = function(wrn) {
          if (grepl("More than 15 time series detected", wrn$message, fixed = TRUE)) {
            warning_msg_season(paste0("Warning: more than 15 spatial units ",
                                      "detected. Consider spatial aggregation",
                                      " (previous step)."))
          }
          invokeRestart("muffleWarning")
        }
      )
    })

    # text for warning message
    output$warning_msg_season <- renderText({
      warning_msg_season()
    })

    # button for download
    output$download_season <- downloadHandler(
      filename = function() {
        paste0("d4h_seasonality_", format(Sys.time(), "%Y%m%d_%H%M"),".png")
      },
      content = function(file) {
        png(file, width = 800, height = 600)
        plot_obj <- GHRexplore::plot_seasonality(
          data = season(),
          var  = input$cases_col,
          time = input$time_col,
          type = "counts",
          area = input$area_col
        )
        dev.off()
      }
    )

    # heatmap ---------------------------------------------------------------

    # container for warning message
    warning_msg_heat <- reactiveVal("")

    # reactive expression
    heat <- eventReactive(input$heatbutton, {
      req(input$cases_col, input$time_col, input$area_col)
      df <- data_store$get()
      df[[input$cases_col]] <- as.numeric(df[[input$cases_col]])
      df[[input$time_col]] <- as.Date(df[[input$time_col]])
      df
    })

    # plot
    output$heat_plot <- renderPlot({
      warning_msg_heat("")  # reset each time

      withCallingHandlers(
        {
          GHRexplore::plot_heatmap(
            data = heat(),
            var = input$cases_col,
            time = input$time_col,
            type = "counts",
            area = input$area_col,
            title = "Heatmap"
          )
        },
        # add warning message
        warning = function(wrn) {
          if (grepl("More than 15 time series detected", wrn$message, fixed = TRUE)) {
            warning_msg_heat("Warning: more than 15 spatial units detected.",
                             "Consider spatial aggregation (previous step).")
          }
          invokeRestart("muffleWarning")
        }
      )
    })

    # text for warning message
    output$warning_msg_heat <- renderText({
      warning_msg_heat()
    })

    # button for download
    output$download_heat <- downloadHandler(
      filename = function() {
        paste0("d4h_heatmap_", format(Sys.time(), "%Y%m%d_%H%M"), ".png")
      },
      content = function(file) {
        png(file, width = 800, height = 600)
        plot_obj <- GHRexplore::plot_heatmap(
          data = heat(),
          var  = input$cases_col,
          time = input$time_col,
          type = "counts",
          area = input$area_col
        )
        dev.off()
      })
  })
}

### END