#' Creates the base cache folder where `tidywikidatar` caches data.
#'
#' @inheritParams tw_reset_item_cache
#'
#' @return Nothing, used for its side effects.
#' @export
#'
#' @examples
#' \donttest{
#' if (interactive()) {
#'   tw_create_cache_folder()
#' }
#' }
tw_create_cache_folder <- function(ask = TRUE) {
  if (!fs::file_exists(tidywikidatar::tw_get_cache_folder())) {
    if (!ask) {
      fs::dir_create(
        path = tidywikidatar::tw_get_cache_folder(),
        recurse = TRUE
      )
    } else {
      cli::cli_inform(c(
        "The cache folder {.path {tw_get_cache_folder()}} does not exist.",
        "If you prefer to cache files elsewhere, reply no and set your preferred cache folder with {.fn tw_set_cache_folder}."
      ))
      msg_yes_no <- cli::format_inline(
        "Do you want to create {.path {tw_get_cache_folder()}} for caching data?"
      )
      check <- utils::menu(choices = c("Yes", "No"), title = msg_yes_no)
      if (check == 1) {
        fs::dir_create(
          path = tidywikidatar::tw_get_cache_folder(),
          recurse = TRUE
        )
      }
    }
    if (!fs::file_exists(tidywikidatar::tw_get_cache_folder())) {
      cli::cli_abort(c(
        "This function requires a valid cache folder.",
        i = "Set one with {.fn tw_set_cache_folder}."
      ))
    }
  }
}


#' Set folder for caching data
#'
#' Consider using a folder out of your current project directory, e.g.
#' `tw_set_cache_folder("~/R/tw_data/")`: you will be able to use the same cache
#' in different projects, and prevent cached files from being sync-ed if you use
#' services such as Nextcloud or Dropbox.
#'
#' @param path A path to a location used for caching data. If the folder does
#'   not exist, it will be created.
#'
#' @return The path to the caching folder, if previously set; the same path as
#'   given to the function; or the default, `tw_data` is none is given.
#' @export
#' @examples
#' \donttest{
#' if (interactive()) {
#'   tw_set_cache_folder(fs::path(fs::path_home_r(), "R", "tw_data"))
#' }
#' }
tw_set_cache_folder <- function(path = NULL) {
  if (is.null(path)) {
    path <- Sys.getenv("tw_cache_folder")
  } else {
    Sys.setenv(tw_cache_folder = path)
  }
  if (path == "") {
    path <- fs::path("tw_data")
  }
  invisible(path)
}

#' @rdname tw_set_cache_folder
#' @examples
#' tw_get_cache_folder()
#' @export
tw_get_cache_folder <- tw_set_cache_folder


#' Set database connection settings for the session
#'
#'
#' @param db_settings A list of database connection settings (see example)
#' @param driver A database driver. Common database drivers include `MySQL`,
#'   `PostgreSQL`, and `MariaDB`. See `unique(odbc::odbcListDrivers()[[1]])` for
#'   a list of locally available drivers.
#' @param host Host address, e.g. "localhost". Different drivers use server or
#'   host parameter, only one of them is likely needed.
#' @param server Server address, e.g. "localhost". Different drivers use server
#'   or host parameter, only one of them is likely needed.
#' @param port Port to use to connect to the database.
#' @param database Database name.
#' @param user Database user name.
#' @param pwd Password for the database user.
#'
#' @return A list with all given parameters (invisibly).
#' @export
#'
#' @examples
#' \donttest{
#' if (interactive()) {
#'   # Settings can be provided either as a list
#'   db_settings <- list(
#'     driver = "MySQL",
#'     host = "localhost",
#'     server = "localhost",
#'     port = 3306,
#'     database = "tidywikidatar",
#'     user = "secret_username",
#'     pwd = "secret_password"
#'   )
#'
#'   tw_set_cache_db(db_settings)
#'
#'   # or as parameters
#'
#'   tw_set_cache_db(
#'     driver = "MySQL",
#'     host = "localhost",
#'     server = "localhost",
#'     port = 3306,
#'     database = "tidywikidatar",
#'     user = "secret_username",
#'     pwd = "secret_password"
#'   )
#'
#'   # or ignoring fields that can be left to default values, such as "localhost" and port 3306
#'
#'   tw_set_cache_db(
#'     driver = "MySQL",
#'     database = "tidywikidatar",
#'     user = "secret_username",
#'     pwd = "secret_password"
#'   )
#' }
#' }
tw_set_cache_db <- function(
  db_settings = NULL,
  driver = NULL,
  host = NULL,
  server = NULL,
  port = NULL,
  database = NULL,
  user = NULL,
  pwd = NULL
) {
  if (is.null(db_settings)) {
    if (is.null(driver) == FALSE) {
      Sys.setenv(tw_db_driver = driver)
    }
    if (is.null(host) == FALSE) {
      Sys.setenv(tw_db_host = host)
    }
    if (is.null(server) == FALSE) {
      Sys.setenv(tw_db_host = server)
    }
    if (is.null(port) == FALSE) {
      Sys.setenv(tw_db_port = port)
    }
    if (is.null(database) == FALSE) {
      Sys.setenv(tw_db_database = database)
    }
    if (is.null(user) == FALSE) {
      Sys.setenv(tw_db_user = user)
    }
    if (is.null(pwd) == FALSE) {
      Sys.setenv(tw_db_pwd = pwd)
    }
    return(invisible(
      list(
        driver = driver,
        host = host,
        server = server,
        port = port,
        database = database,
        user = user,
        pwd = pwd
      )
    ))
  } else {
    if (!is.null(db_settings$driver)) {
      Sys.setenv(tw_db_driver = db_settings$driver)
    }
    if (!is.null(db_settings$host)) {
      Sys.setenv(tw_db_host = db_settings$host)
    }
    if (!is.null(db_settings$server)) {
      Sys.setenv(tw_db_server = db_settings$server)
    }
    if (!is.null(db_settings$port)) {
      Sys.setenv(tw_db_port = db_settings$port)
    }
    if (!is.null(db_settings$database)) {
      Sys.setenv(tw_db_database = db_settings$database)
    }
    if (!is.null(db_settings$user)) {
      Sys.setenv(tw_db_user = db_settings$user)
    }
    if (!is.null(db_settings$pwd)) {
      Sys.setenv(tw_db_pwd = db_settings$pwd)
    }
    return(invisible(db_settings))
  }
}

#' Get database connection settings from the environment
#'
#' Typically set with [tw_set_cache_db()].
#'
#' @return A list with all database parameters as stored in environment
#'   variables.
#' @export
#'
#' @examples
#'
#' tw_get_cache_db()
tw_get_cache_db <- function() {
  list(
    driver = Sys.getenv("tw_db_driver"),
    host = Sys.getenv("tw_db_host"),
    server = Sys.getenv("tw_db_server"),
    port = Sys.getenv("tw_db_port"),
    database = Sys.getenv("tw_db_database"),
    user = Sys.getenv("tw_db_user"),
    pwd = Sys.getenv("tw_db_pwd")
  )
}


#' Enable caching for the current session
#'
#' @param SQLite Logical, defaults to `TRUE`. Set to `FALSE` to use custom
#'   database options. See [tw_set_cache_db()] for details.
#'
#' @return Nothing, used for its side effects.
#' @export
#' @examples
#' \donttest{
#' if (interactive()) {
#'   tw_enable_cache()
#' }
#' }
tw_enable_cache <- function(SQLite = TRUE) {
  Sys.setenv(tw_cache = TRUE)
  Sys.setenv(tw_cache_SQLite = SQLite)
}


#' Disable caching for the current session
#'
#' @return Nothing, used for its side effects.
#' @export

#' @examples
#' \donttest{
#' if (interactive()) {
#'   tw_disable_cache()
#' }
#' }
tw_disable_cache <- function() {
  Sys.setenv(tw_cache = FALSE)
}

#' Check caching status in the current session, and override it upon request
#'
#' Mostly used internally in functions, exported for reference.
#'
#' @param cache Defaults to `NULL`. If `NULL`, checks current cache settings. If
#'   given, returns given value, ignoring cache.
#'
#' @return Either `TRUE` or `FALSE`, depending on current cache settings.
#' @export
#' @examples
#' \donttest{
#' if (interactive()) {
#'   tw_check_cache()
#' }
#' }
tw_check_cache <- function(cache = NULL) {
  if (!is.null(cache)) {
    return(as.logical(cache))
  }
  current_cache <- Sys.getenv("tw_cache", unset = FALSE)
  as.logical(current_cache)
}

#' Checks if cache folder exists, if not returns an informative message
#'
#' @return If the cache folder exists, returns `TRUE`. Otherwise throws an
#'   error.
#' @export
#'
#' @examples
#'
#' # If cache folder does not exist, it throws an error
#' tryCatch(tw_check_cache_folder(),
#'   error = function(e) {
#'     return(e)
#'   }
#' )
#'
#' # Create cache folder
#' tw_set_cache_folder(path = fs::path(
#'   tempdir(),
#'   "tw_cache_folder"
#' ))
#' tw_create_cache_folder(ask = FALSE)
#'
#' tw_check_cache_folder()
tw_check_cache_folder <- function() {
  if (!fs::file_exists(tw_get_cache_folder())) {
    cli::cli_abort(c(
      "Cache folder does not exist.",
      "i" = "Set it with {.fn tw_set_cache_folder} and create it with {.fn tw_create_cache_folder}."
    ))
  }
  TRUE
}


#' Ensure that connection to cache is disconnected consistently
#'
#' @param cache Defaults to NULL. If given, it should be given either `TRUE` or
#'   FALSE. Typically set with [tw_enable_cache()] or [tw_disable_cache()].
#' @param cache_connection Defaults to `NULL`. If `NULL`, and caching is enabled,
#'   `tidywikidatar` will use a local sqlite database. A custom connection to
#'   other databases can be given (see vignette `caching` for details).
#' @param disconnect_db Defaults to `TRUE`. If `FALSE`, leaves the connection to
#'   cache open.
#' @param language Defaults to language set with [tw_set_language()]; if not
#'   set, "en". Use "all_available" to keep all languages. For available
#'   language values, see the
#'   \href{https://www.wikidata.org/wiki/Help:Wikimedia_language_codes/lists/all}{relevant
#'   Wikimedia page}.
#'
#' @return Nothing, used for its side effects.
#' @export
#'
#' @examples
#' if (interactive()) {
#'   tw_get(
#'     id = c("Q180099"),
#'     language = "en"
#'   )
#'   tw_disconnect_from_cache()
#' }
tw_disconnect_from_cache <- function(
  cache = NULL,
  cache_connection = NULL,
  disconnect_db = TRUE,
  language = tidywikidatar::tw_get_language()
) {
  if (isFALSE(disconnect_db)) {
    return(invisible(NULL))
  }

  if (isTRUE(tw_check_cache(cache))) {
    db <- tw_connect_to_cache(
      connection = cache_connection,
      language = language,
      cache = cache
    )

    if (pool::dbIsValid(dbObj = db)) {
      if (inherits(db, "Pool")) {
        pool::poolClose(db)
      } else {
        DBI::dbDisconnect(db)
      }
    }
  }
}
