#' Filter search result and keep only items with matching property and Q
#' identifier
#'
#' @param search A data frame generated by `tw_search()`, or a search query. If
#'   a data frame is given, language and limits are ignore.
#' @param p A character vector of length 1, a property. Must always start with
#'   the capital letter "P", e.g. "P31" for "instance of".
#' @param q A character vector of length 1, a wikidata id. Must always start
#'   with the capital letter "Q", e.g. "Q5" for "human being".
#' @inheritParams tw_search
#'
#' @return A data frame with three columns, `id`, `label`, and `description`,
#'   filtered by the above criteria.
#' @export
#'
#' @examples
#' \dontrun{
#' tw_search(search = "Margaret Mead", limit = 3) %>%
#'   tw_filter(p = "P31", q = "Q5")
#' }
tw_filter <- function(
  search,
  p,
  q,
  language = tidywikidatar::tw_get_language(),
  response_language = tidywikidatar::tw_get_language(),
  limit = 10,
  include_search = FALSE,
  wait = 0,
  cache = NULL,
  overwrite_cache = FALSE,
  cache_connection = NULL,
  disconnect_db = TRUE
) {
  db <- tw_connect_to_cache(
    connection = cache_connection,
    language = language,
    cache = cache
  )

  search_result <- tw_check_search(
    search = search,
    language = language,
    response_language = response_language,
    limit = limit,
    include_search = include_search,
    wait = wait,
    cache = cache,
    overwrite_cache = overwrite_cache,
    cache_connection = db,
    disconnect_db = FALSE
  )

  if (nrow(search_result) == 0) {
    tw_disconnect_from_cache(
      cache = cache,
      cache_connection = db,
      disconnect_db = disconnect_db,
      language = language
    )

    return(search_result)
  }

  p_df <- tw_get_property(
    id = search_result$id,
    p = p,
    language = language,
    cache = cache,
    overwrite_cache = overwrite_cache,
    cache_connection = db,
    disconnect_db = FALSE,
    wait = wait
  ) %>%
    dplyr::filter(.data$value %in% q)

  tw_disconnect_from_cache(
    cache = cache,
    cache_connection = db,
    disconnect_db = disconnect_db,
    language = language
  )

  search_result %>%
    dplyr::semi_join(
      y = p_df,
      by = "id"
    )
}

#' Filter search result and keep only and keep only the first match
#'
#' Same as [tw_filter()], but consistently returns data frames with a single
#' row.
#'
#' @inheritParams tw_filter
#'
#' @return A data frame with one row and three columns, `id`, `label`, and
#'   `description`, filtered by the above criteria.
#' @export
#'
#' @examples
#' \dontrun{
#' tw_search("Margaret Mead") %>%
#'   tw_filter_first(p = "P31", q = "Q5")
#' }
tw_filter_first <- function(
  search,
  p,
  q,
  language = tidywikidatar::tw_get_language(),
  response_language = tidywikidatar::tw_get_language(),
  limit = 10,
  include_search = FALSE,
  wait = 0,
  cache = NULL,
  overwrite_cache = FALSE,
  cache_connection = NULL,
  disconnect_db = TRUE
) {
  if (tw_check_cache(cache)) {
    db <- tw_connect_to_cache(
      connection = cache_connection,
      language = language,
      cache = cache
    )
  }

  search_result <- tw_check_search(
    search = search,
    language = language,
    response_language = response_language,
    limit = limit,
    include_search = include_search,
    wait = wait,
    cache = cache,
    overwrite_cache = overwrite_cache,
    cache_connection = db,
    disconnect_db = FALSE
  )

  if (nrow(search_result) == 0) {
    return(search_result)
  }

  first_match_id <- purrr::detect(
    .x = seq_along(search_result$id),
    .f = function(current_row_number) {
      check_v <- search_result %>%
        dplyr::slice(current_row_number) %>%
        tw_filter(
          p = p,
          q = q,
          language = language,
          response_language = response_language,
          limit = limit,
          include_search = include_search,
          wait = wait,
          cache = cache,
          overwrite_cache = overwrite_cache,
          cache_connection = cache_connection,
          disconnect_db = FALSE
        ) %>%
        nrow()

      check_v > 0
    }
  )

  tw_disconnect_from_cache(
    cache = cache,
    cache_connection = db,
    disconnect_db = disconnect_db,
    language = language
  )

  if (is.null(first_match_id)) {
    search_result %>%
      dplyr::slice(0)
  } else {
    search_result %>%
      dplyr::slice(first_match_id)
  }
}

#' Filter search result and keep only people
#'
#' A wrapper of [tw_filter()] that defaults to keep only "instance of" (P31)
#' "human being" (Q5).
#'
#' @param stop_at_first Logical, defaults to TRUE. If TRUE, returns only the
#'   first match from the search that satisfies the criteria.
#' @inheritParams tw_filter
#' @return A data frame with three columns, `id`, `label`, and `description`;
#'   all rows refer to a human being.
#' @export
#'
#' @examples
#' \dontrun{
#'   tw_search("Ruth Benedict")
#'
#'   tw_search("Ruth Benedict") %>%
#'     tw_filter_people()
#' }
tw_filter_people <- function(
  search,
  language = tidywikidatar::tw_get_language(),
  response_language = tidywikidatar::tw_get_language(),
  limit = 10,
  include_search = FALSE,
  stop_at_first = TRUE,
  wait = 0,
  overwrite_cache = FALSE,
  cache_connection = NULL,
  disconnect_db = TRUE
) {
  if (stop_at_first) {
    tw_filter_first(
      search = search,
      p = "P31",
      q = "Q5",
      language = language,
      response_language = response_language,
      limit = limit,
      include_search = include_search,
      wait = wait,
      overwrite_cache = overwrite_cache,
      cache_connection = cache_connection,
      disconnect_db = disconnect_db
    )
  } else {
    tw_filter(
      search = search,
      p = "P31",
      q = "Q5",
      language = language,
      response_language = response_language,
      limit = limit,
      include_search = include_search,
      wait = wait,
      overwrite_cache = overwrite_cache,
      cache_connection = cache_connection,
      disconnect_db = disconnect_db
    )
  }
}
