R/espn_wnba_data.R

Defines functions espn_wnba_scoreboard espn_wnba_teams espn_wnba_game_rosters espn_wnba_player_box espn_wnba_team_box espn_wnba_pbp espn_wnba_game_all

Documented in espn_wnba_game_all espn_wnba_game_rosters espn_wnba_pbp espn_wnba_player_box espn_wnba_scoreboard espn_wnba_team_box espn_wnba_teams

#' Get ESPN's WNBA game data (play-by-play, team and player box)
#' @author Saiem Gilani
#' @param game_id Game ID
#' @return A named list of dataframes: Plays, Team, Player
#' 
#'    **Plays** 
#'    
#'    
#'    Columns as documented in the shared [espn_basketball_game_all_plays_schema] table.
#'    
#'    **Team** 
#'    
#'    
#'    Columns as documented in the shared [espn_basketball_team_box_schema] table.
#'    
#'    **Player** 
#'    
#'    
#'    Columns as documented in the shared [espn_basketball_game_all_player_schema] table.
#' 
#' @importFrom rlang .data
#' @importFrom jsonlite fromJSON toJSON
#' @importFrom dplyr filter select rename bind_cols bind_rows
#' @importFrom tidyr unnest unnest_wider everything
#' @import rvest
#' @export
#' @keywords WNBA Game
#' @family ESPN WNBA Functions
#' @examples
#' \donttest{
#'   try(espn_wnba_game_all(game_id = 401244185))
#' }

espn_wnba_game_all <- function(game_id){
  .args <- mget(setdiff(names(formals()), "..."))
  old <- options(list(stringsAsFactors = FALSE, scipen = 999))
  on.exit(options(old))
  
  summary_url <- "http://site.api.espn.com/apis/site/v2/sports/basketball/wnba/summary?"
  
  ## Inputs
  ## game_id
  full_url <- paste0(summary_url,
                     "event=", game_id)

  pbp <- list(Plays = NULL, Team = NULL, Player = NULL)
  resp <- NULL
  plays_df <- NULL
  team_box_score <- NULL
  player_box_score <- NULL

  #---- Fetch the summary endpoint (single outer tryCatch) -------------------
  tryCatch(
    expr = {
      res <- .retry_request(full_url)
      check_status(res)
      resp <- res %>%
        .resp_text()
    },
    error = function(e) .report_api_error(
      e,
      hint = "Could not fetch game summary for game_id = {game_id}",
      args = .args
    ),
    warning = function(w) .report_api_warning(w, args = .args),
    finally = {}
  )

  if (is.null(resp)) {
    return(pbp)
  }

  #---- Play-by-Play ------
  tryCatch(
    expr = {

      plays_df <- helper_espn_wnba_pbp(resp)

      if (is.null(plays_df)) {
        cli::cli_alert_danger("{Sys.time()}: No play-by-play data for {game_id} available!")
      }

    },
    error = function(e) .report_api_error(
      e,
      hint = "Invalid arguments or no play-by-play data for {game_id} available!",
      args = .args
    ),
    warning = function(w) .report_api_warning(w, args = .args),
    finally = {

    }
  )
  #---- Team Box ------
  tryCatch(
    expr = {
      
      team_box_score <- helper_espn_wnba_team_box(resp)
      
      if (is.null(team_box_score)) {
        cli::cli_alert_danger("{Sys.time()}: No team box score data for {game_id} available!")
      }
      
    },
    error = function(e) .report_api_error(
      e,
      hint = "Invalid arguments or no team box score data for {game_id} available!",
      args = .args
    ),
    warning = function(w) .report_api_warning(w, args = .args),
    finally = {
      
    }
  )
  #---- Player Box ------
  tryCatch(
    expr = {
      
      player_box_score <- helper_espn_wnba_player_box(resp)
      
      if (is.null(player_box_score)) {
        cli::cli_alert_danger("{Sys.time()}: No player box score data for {game_id} available!")
      }
      
    },
    error = function(e) .report_api_error(
      e,
      hint = "Invalid arguments or no player box score data for {game_id} available!",
      args = .args
    ),
    warning = function(w) .report_api_warning(w, args = .args),
    finally = {
      
    }
  )
  
  
  pbp <- c(list(plays_df), list(team_box_score), list(player_box_score))
  names(pbp) <- c("Plays", "Team", "Player")
  return(pbp)
}

#' Get ESPN's WNBA play by play data
#' @rdname espn_wnba_game_all
#' @author Saiem Gilani
#' @param game_id Game ID
#' @return Returns a play-by-play data frame
#' 
#'    **Plays** 
#'    
#'    
#'    Columns as documented in the shared [espn_basketball_game_all_plays_schema] table.
#' 
#' @importFrom rlang .data
#' @importFrom jsonlite fromJSON toJSON
#' @importFrom dplyr filter select rename bind_cols bind_rows
#' @importFrom tidyr unnest unnest_wider everything
#' @import rvest
#' @export
#' @keywords WNBA PBP
#' @family ESPN WNBA Functions
#' @examples
#' 
#' \donttest{
#'    try(espn_wnba_pbp(game_id = 401455681))
#' }
espn_wnba_pbp <- function(game_id){
  .args <- mget(setdiff(names(formals()), "..."))
  old <- options(list(stringsAsFactors = FALSE, scipen = 999))
  on.exit(options(old))
  
  summary_url <- "http://site.api.espn.com/apis/site/v2/sports/basketball/wnba/summary?"
  
  ## Inputs
  ## game_id
  full_url <- paste0(summary_url,
                     "event=", game_id)

  #---- Play-by-Play ------
  plays_df <- NULL

  tryCatch(
    expr = {
      res <- .retry_request(full_url)
      check_status(res)
      resp <- res %>%
        .resp_text()

      plays_df <- helper_espn_wnba_pbp(resp)
      
      if (is.null(plays_df)) {
        return(cli::cli_alert_danger("{Sys.time()}: No play-by-play data for {game_id} available!"))
      }
      
    },
    error = function(e) .report_api_error(
      e,
      hint = "Invalid arguments or no play-by-play data for {game_id} available!",
      args = .args
    ),
    warning = function(w) .report_api_warning(w, args = .args),
    finally = {
    }
  )
  
  
  return(plays_df)
}

#' Get ESPN's WNBA team box data
#' @rdname espn_wnba_game_all
#' @author Saiem Gilani
#' @param game_id Game ID
#' @return Returns a team boxscore data frame
#' 
#'    **Team** 
#'    
#'    
#'    Columns as documented in the shared [espn_basketball_team_box_schema] table.
#' 
#' @importFrom rlang .data
#' @importFrom jsonlite fromJSON toJSON
#' @importFrom dplyr filter select rename bind_cols bind_rows
#' @importFrom tidyr unnest unnest_wider everything
#' @import rvest
#' @export
#' @keywords WNBA Team Box
#' @family ESPN WNBA Functions
#' @examples
#' 
#' \donttest{
#'    try(espn_wnba_team_box(game_id = 401244185))
#' }
espn_wnba_team_box <- function(game_id){
  .args <- mget(setdiff(names(formals()), "..."))
  old <- options(list(stringsAsFactors = FALSE, scipen = 999))
  on.exit(options(old))
  summary_url <- "http://site.api.espn.com/apis/site/v2/sports/basketball/wnba/summary?"
  
  ## Inputs
  ## game_id
  full_url <- paste0(summary_url,
                     "event=", game_id)

  #---- Team Box ------
  team_box_score <- NULL

  tryCatch(
    expr = {
      res <- .retry_request(full_url)
      check_status(res)
      resp <- res %>%
        .resp_text()

      team_box_score <- helper_espn_wnba_team_box(resp)
      
      if (is.null(team_box_score)) {
        return(cli::cli_alert_danger("{Sys.time()}: No team box score data for {game_id} available!"))
      }
      
    },
    error = function(e) .report_api_error(
      e,
      hint = "Invalid arguments or no team box score data for {game_id} available!",
      args = .args
    ),
    warning = function(w) .report_api_warning(w, args = .args),
    finally = {
    }
  )
  return(team_box_score)
}

#' Get ESPN's WNBA player box data
#' @rdname espn_wnba_game_all
#' @author Saiem Gilani
#' @param game_id Game ID
#' @return Returns a player boxscore data frame
#' 
#'    **Player** 
#'    
#'    
#'    Columns as documented in the shared [espn_basketball_game_all_player_schema] table.
#' 
#' @importFrom rlang .data
#' @importFrom jsonlite fromJSON toJSON
#' @importFrom dplyr filter select rename bind_cols bind_rows
#' @importFrom tidyr unnest unnest_wider everything
#' @import rvest
#' @export
#' @keywords WNBA Player Box
#' @family ESPN WNBA Functions
#' @examples
#' \donttest{
#'   try(espn_wnba_player_box(game_id = 401244185))
#' }
#' 
espn_wnba_player_box <- function(game_id){
  .args <- mget(setdiff(names(formals()), "..."))
  old <- options(list(stringsAsFactors = FALSE, scipen = 999))
  on.exit(options(old))
  summary_url <- "http://site.api.espn.com/apis/site/v2/sports/basketball/wnba/summary?"
  
  ## Inputs
  ## game_id
  full_url <- paste0(summary_url,
                     "event=", game_id)

  #---- Player Box ------
  player_box_score <- NULL

  tryCatch(
    expr = {
      res <- .retry_request(full_url)
      check_status(res)
      resp <- res %>%
        .resp_text()

      player_box_score <- helper_espn_wnba_player_box(resp)
      
      if (is.null(player_box_score)) {
        return(cli::cli_alert_danger("{Sys.time()}: No player box score data for {game_id} available!"))
      }
      
    },
    error = function(e) .report_api_error(
      e,
      hint = "Invalid arguments or no player box score data for {game_id} available!",
      args = .args
    ),
    warning = function(w) .report_api_warning(w, args = .args),
    finally = {
    }
  )
  return(player_box_score)
}



#' **Get ESPN WNBA game rosters**
#' @rdname espn_wnba_game_all
#' @author Saiem Gilani
#' @param game_id Game ID
#' @return A game rosters data frame
#' 
#'    \if{html}{\tabular{lll}{
#'       col_name \tab types \tab description \cr
#'       athlete_id \tab integer \tab Unique athlete identifier (ESPN). \cr
#'       athlete_uid \tab character \tab ESPN athlete UID (universal identifier). \cr
#'       athlete_guid \tab character \tab ESPN athlete GUID. \cr
#'       athlete_type \tab character \tab Athlete type / class. \cr
#'       sdr \tab integer \tab Sdr. \cr
#'       first_name \tab character \tab Player's first name. \cr
#'       last_name \tab character \tab Player's last name. \cr
#'       full_name \tab character \tab Player's full name. \cr
#'       athlete_display_name \tab character \tab Athlete display name (full). \cr
#'       short_name \tab character \tab Short display name. \cr
#'       weight \tab numeric \tab Player weight in pounds. \cr
#'       display_weight \tab character \tab Player weight in display format (e.g. '180 lbs'). \cr
#'       height \tab numeric \tab Player height (string e.g. '6-2' or inches). \cr
#'       display_height \tab character \tab Player height in display format (e.g. '6-2'). \cr
#'       age \tab integer \tab Player age (in years). \cr
#'       date_of_birth \tab character \tab Date of birth (YYYY-MM-DD). \cr
#'       slug \tab character \tab URL-safe identifier. \cr
#'       headshot_href \tab character \tab Headshot image URL. \cr
#'       headshot_alt \tab character \tab Alternative-text label for the headshot. \cr
#'       jersey \tab character \tab Jersey number worn by the player. \cr
#'       position_id \tab integer \tab Unique position identifier. \cr
#'       position_name \tab character \tab Listed roster position ('Guard', 'Forward', 'Center'). \cr
#'       position_display_name \tab character \tab Position display name. \cr
#'       position_abbreviation \tab character \tab Position abbreviation ('G' / 'F' / 'C'). \cr
#'       position_leaf \tab logical \tab Position leaf. \cr
#'       linked \tab logical \tab TRUE if the record is linked to a related entity. \cr
#'       years \tab integer \tab Years. \cr
#'       active \tab logical \tab TRUE if the row represents an active record (player / team / season). \cr
#'       status_id \tab integer \tab Status identifier. \cr
#'       status_name \tab character \tab Status label. \cr
#'       status_type \tab character \tab Status type. \cr
#'       status_abbreviation \tab character \tab Status abbreviation. \cr
#'       birth_place_city \tab character \tab Birth place city. \cr
#'       birth_place_state \tab character \tab Birth place state. \cr
#'       birth_place_country \tab character \tab Birth place country. \cr
#'       starter \tab logical \tab TRUE if the player was in the starting lineup; FALSE otherwise. \cr
#'       valid \tab logical \tab Valid. \cr
#'       did_not_play \tab logical \tab TRUE if the player did not appear in the game. \cr
#'       display_name \tab character \tab Display name. \cr
#'       reason \tab character \tab Reason. \cr
#'       ejected \tab logical \tab TRUE if the player was ejected from the game. \cr
#'       team_id \tab integer \tab Unique team identifier. \cr
#'       team_guid \tab character \tab ESPN team GUID. \cr
#'       team_uid \tab character \tab ESPN universal team identifier (UID format 's:40~l:...~t:...'). \cr
#'       team_sdr \tab integer \tab ESPN team SDR identifier. \cr
#'       team_slug \tab character \tab URL-safe team identifier (e.g. 'lasvegas-aces' / 'aces'). \cr
#'       team_location \tab character \tab Team city or location string. \cr
#'       team_name \tab character \tab Full team display name (e.g. 'Las Vegas Aces'). \cr
#'       team_abbreviation \tab character \tab Short team abbreviation (e.g. 'LAS'). \cr
#'       team_display_name \tab character \tab Full team display name. \cr
#'       team_short_display_name \tab character \tab Short team display name (e.g. 'Aces'). \cr
#'       team_color \tab character \tab Team primary color (hex without leading '#'). \cr
#'       team_alternate_color \tab character \tab Team alternate color (hex without leading '#'). \cr
#'       team_is_active \tab logical \tab TRUE if the team is currently active. \cr
#'       is_all_star \tab logical \tab Is all star. \cr
#'       logo_href \tab character \tab Team or league logo URL. \cr
#'       logo_dark_href \tab character \tab Logo URL for dark backgrounds. \cr
#'       logos_href_2 \tab character \tab Logos href 2. \cr
#'       logos_href_3 \tab character \tab Logos href 3. \cr
#'       game_id \tab integer \tab Unique game identifier. \cr
#'       order \tab integer \tab Display order within the result set. \cr
#'       home_away \tab character \tab Game venue label ('home' or 'away'). \cr
#'       winner \tab logical \tab Winner. \cr
#'       draft_display_text \tab character \tab Draft display text. \cr
#'       draft_round \tab integer \tab Round of the draft selection. \cr
#'       draft_year \tab integer \tab Draft year (4-digit). \cr
#'       draft_selection \tab integer \tab Draft selection. \cr
#'       hand_type \tab character \tab Hand type. \cr
#'       hand_abbreviation \tab character \tab Hand abbreviation. \cr
#'       hand_display_value \tab character \tab Hand display value. \cr
#'       citizenship \tab character \tab Citizenship. \cr
#'    }}
#'    \if{latex}{See the HTML help or pkgdown reference for the column table.}
#'    
#' @importFrom jsonlite fromJSON toJSON
#' @importFrom dplyr filter select rename bind_cols bind_rows
#' @importFrom tidyr unnest unnest_wider everything
#' @import rvest
#' @export
#' @keywords WNBA Game Roster
#' @family ESPN WNBA Functions
#'
#' @examples
#' \donttest{
#'   try(espn_wnba_game_rosters(game_id = 401244185))
#' }
espn_wnba_game_rosters <- function(game_id) {
  .args <- mget(setdiff(names(formals()), "..."))
  old <- options(list(stringsAsFactors = FALSE, scipen = 999))
  on.exit(options(old))
  athlete_roster_df <- data.frame()

  tryCatch(
    expr = {
      play_base_url <- paste0(
        "https://sports.core.api.espn.com/v2/sports/basketball/leagues/wnba/events/",
        game_id, "/competitions/",
        game_id,"/competitors/")
      game_res <- .retry_request(play_base_url)
      # Check the result
      check_status(game_res)
      
      game_resp <- game_res %>%
        .resp_text()
      game_df <- jsonlite::fromJSON(game_resp)[["items"]] %>%
        jsonlite::toJSON() %>%
        jsonlite::fromJSON(flatten = TRUE) %>%
        dplyr::rename("team_statistics_href" = "statistics.$ref")
      
      colnames(game_df) <- gsub(".\\$ref","_href", colnames(game_df))
      
      game_df <- game_df %>%
        dplyr::rename(
          "team_id" = "id",
          "team_uid" = "uid")
      
      game_df$game_id <- game_id
      
      teams_df <- purrr::map_dfr(game_df$team_href, function(x){
        
        res <- .retry_request(x)
        # Check the result
        check_status(res)
        
        team_df <- res %>%
          .resp_text() %>%
          jsonlite::fromJSON(simplifyDataFrame = FALSE, simplifyVector = FALSE, simplifyMatrix = FALSE)
        
        team_df[["links"]] <- NULL
        team_df[["injuries"]] <- NULL
        team_df[["record"]] <- NULL
        team_df[["athletes"]] <- NULL
        team_df[["venue"]] <- NULL
        team_df[["groups"]] <- NULL
        team_df[["ranks"]] <- NULL
        team_df[["statistics"]] <- NULL
        team_df[["leaders"]] <- NULL
        team_df[["links"]] <- NULL
        team_df[["notes"]] <- NULL
        team_df[["franchise"]] <- NULL
        team_df[["againstTheSpreadRecords"]] <- NULL
        team_df[["oddsRecords"]] <- NULL
        team_df[["college"]] <- NULL
        team_df[["transactions"]] <- NULL
        team_df[["leaders"]] <- NULL
        team_df[["depthCharts"]] <- NULL
        team_df[["awards"]] <- NULL
        team_df[["events"]] <- NULL
        
        team_df <- team_df %>%
          purrr::map_if(is.list, as.data.frame) %>%
          as.data.frame() %>%
          dplyr::select(
            -dplyr::any_of(
              c("logos.width",
                "logos.height",
                "logos.alt",
                "logos.rel..full.",
                "logos.rel..default.",
                "logos.rel..scoreboard.",
                "logos.rel..scoreboard..1",
                "logos.rel..scoreboard.2",
                "logos.lastUpdated",
                "logos.width.1",
                "logos.height.1",
                "logos.alt.1",
                "logos.rel..full..1",
                "logos.rel..dark.",
                "logos.rel..dark..1",
                "logos.lastUpdated.1",
                "logos.width.2",
                "logos.height.2",
                "logos.alt.2",
                "logos.rel..full..2",
                "logos.rel..scoreboard.",
                "logos.lastUpdated.2",
                "logos.width.3",
                "logos.height.3",
                "logos.alt.3",
                "logos.rel..full..3",
                "logos.lastUpdated.3",
                "X.ref",
                "X.ref.1",
                "X.ref.2"))) %>%
          janitor::clean_names()
        
        colnames(team_df)[1:13] <- paste0("team_", colnames(team_df)[1:13])
        
        team_df <- team_df %>%
          dplyr::rename(
            "logo_href" = "logos_href",
            "logo_dark_href" = "logos_href_1") %>%
          dplyr::left_join(
            game_df %>%
              dplyr::select(
                "game_id",
                "team_id",
                "team_uid",
                "order",
                "homeAway",
                "winner",
                "roster_href"),
            by = c("team_id" = "team_id",
                   "team_uid" = "team_uid")
          )
        
      })
      
      ## Inputs
      ## game_id
      team_roster_df <- purrr::map_dfr(teams_df$team_id, function(x){
        
        res <- .retry_request(paste0(play_base_url, x, "/roster"))
        
        # Check the result
        check_status(res)
        
        resp <- res %>%
          .resp_text()
        
        raw_play_df <- jsonlite::fromJSON(resp)[["entries"]]
        
        raw_play_df <- raw_play_df %>%
          jsonlite::toJSON() %>%
          jsonlite::fromJSON(flatten = TRUE) %>%
          dplyr::mutate(team_id = x) %>%
          dplyr::select(-"period", -"forPlayerId", -"active")
        
        raw_play_df <- raw_play_df %>%
          dplyr::left_join(teams_df, by = c("team_id" = "team_id"))
        
      })
      
      colnames(team_roster_df) <- gsub(".\\$ref","_href", colnames(team_roster_df))
      
      athlete_roster_df <- purrr::map_dfr(team_roster_df$athlete_href, function(x){
        
        res <- .retry_request(x)
        
        # Check the result
        check_status(res)
        
        resp <- res %>%
          .resp_text()
        
        raw_play_df <- jsonlite::fromJSON(resp, flatten = TRUE)
        raw_play_df[["links"]] <- NULL
        raw_play_df[["injuries"]] <- NULL
        raw_play_df[["teams"]] <- NULL
        raw_play_df[["team"]] <- NULL
        raw_play_df[["college"]] <- NULL
        raw_play_df[["proAthlete"]] <- NULL
        raw_play_df[["statistics"]] <- NULL
        raw_play_df[["notes"]] <- NULL
        raw_play_df[["eventLog"]] <- NULL
        raw_play_df[["$ref"]] <- NULL
        raw_play_df[["position"]][["$ref"]] <- NULL
        birth_place <- raw_play_df %>% 
          purrr::pluck("birthPlace") %>% 
          as.data.frame()
        raw_play_df[["birthPlace"]] <- NULL
        
        raw_play_df2 <- raw_play_df %>%
          as.data.frame() %>%
          dplyr::bind_cols(birth_place) %>% 
          dplyr::mutate(id = as.integer(.data$id)) %>%
          dplyr::rename(
            "athlete_id" = "id",
            "athlete_uid" = "uid",
            "athlete_guid" = "guid",
            "athlete_type" = "type",
            "athlete_display_name" = "displayName"
          )
        
        raw_play_df2 <- raw_play_df2 %>%
          dplyr::left_join(team_roster_df, by = c("athlete_id" = "playerId"))
        
      })
      
      colnames(athlete_roster_df) <- gsub(".\\$ref","_href", colnames(athlete_roster_df))
      
      athlete_roster_df <- athlete_roster_df %>%
        janitor::clean_names() %>%
        dplyr::rename(
          "birth_place_city" = "city",
          "birth_place_state" = "state",
          "birth_place_country" = "country") %>% 
        dplyr::select(-dplyr::any_of(c(
          "x_ref",
          "x_ref_1",
          "contract_ref",
          "contract_ref_1",
          "contract_ref_2",
          "draft_ref",
          "draft_ref_1",
          "athlete_href",
          "position_ref",
          "position_href",
          "roster_href",
          "statistics_href"))) %>%
        dplyr::mutate_at(c(
          "game_id",
          "athlete_id",
          "team_id",
          "position_id",
          "status_id",
          "sdr",
          "team_sdr"), as.integer) %>%
        make_wehoop_data("ESPN WNBA Game Roster Information from ESPN.com",Sys.time())
      
    },
    error = function(e) .report_api_error(
      e,
      hint = "Invalid arguments or no game roster data for {game_id} available!",
      args = .args
    ),
    warning = function(w) .report_api_warning(w, args = .args),
    finally = {
      
    }
  )
  return(athlete_roster_df)
}


#' Get ESPN's WNBA team names and ids
#' @author Saiem Gilani
#' @return Returns a tibble
#' 
#'    \if{html}{\tabular{lll}{
#'       col_name \tab types \tab description \cr
#'       team_id \tab integer \tab Unique team identifier. \cr
#'       team \tab character \tab Team-side label or team identifier. \cr
#'       mascot \tab character \tab Team mascot. \cr
#'       display_name \tab character \tab Display name. \cr
#'       short_name \tab character \tab Short display name. \cr
#'       abbreviation \tab character \tab Short abbreviation. \cr
#'       color \tab character \tab Primary color (hex without leading '#'). \cr
#'       alternate_color \tab character \tab Alternate color (hex without leading '#'). \cr
#'       logo \tab character \tab Team or league logo URL. \cr
#'       logo_dark \tab character \tab Logo dark. \cr
#'    }}
#'    \if{latex}{See the HTML help or pkgdown reference for the column table.}
#' 
#' @importFrom rlang .data
#' @importFrom jsonlite fromJSON toJSON
#' @importFrom dplyr filter select rename bind_cols bind_rows row_number group_by mutate as_tibble ungroup
#' @importFrom tidyr unnest unnest_wider everything pivot_wider
#' @import rvest
#' @export
#' @keywords WNBA Teams
#' @family ESPN WNBA Functions
#' @examples
#' \donttest{
#'   try(espn_wnba_teams())
#' }

espn_wnba_teams <- function(){
  .args <- .capture_args()
  old <- options(list(stringsAsFactors = FALSE, scipen = 999))
  on.exit(options(old))
  play_base_url <- "http://site.api.espn.com/apis/site/v2/sports/basketball/wnba/teams?limit=1000"

  wnba_teams <- data.frame()

  tryCatch(
    expr = {
      res <- .retry_request(play_base_url)
      check_status(res)
      resp <- res %>%
        .resp_text()

      leagues <- jsonlite::fromJSON(resp)[["sports"]][["leagues"]][[1]][['teams']][[1]][['team']] %>%
        dplyr::group_by(.data$id) %>%
        tidyr::unnest_wider("logos", names_sep = "_") %>%
        tidyr::unnest_wider("logos_href", names_sep = "_") %>%
        dplyr::select(
          -"logos_width",
          -"logos_height",
          -"logos_alt",
          -"logos_rel") %>%
        dplyr::ungroup()

      wnba_teams <- leagues %>%
        dplyr::select(
          "id",
          "location",
          "name",
          "displayName",
          "shortDisplayName",
          "abbreviation",
          "color",
          "alternateColor",
          "logos_href_1",
          "logos_href_2") %>%
        dplyr::rename(
          "logo" = "logos_href_1",
          "logo_dark" = "logos_href_2",
          "mascot" = "name",
          "team" = "location",
          "team_id" = "id",
          "alternate_color" = "alternateColor",
          "short_name" = "shortDisplayName",
          "display_name" = "displayName") %>%
        dplyr::mutate(team_id = as.integer(.data$team_id)) %>%
        make_wehoop_data("ESPN WNBA Teams Information from ESPN.com", Sys.time())
    },
    error = function(e) .report_api_error(
      e,
      hint = "Could not fetch ESPN WNBA teams",
      args = .args
    ),
    warning = function(w) .report_api_warning(w, args = .args),
    finally = {}
  )

  return(wnba_teams)
}

#' **Get WNBA schedule for a specific year/date from ESPN's API**
#'
#' @param season Either numeric or character
#' @author Saiem Gilani.
#' @return Returns a tibble
#' 
#'    Columns as documented in the shared [espn_basketball_scoreboard_schema] table.
#' 
#' @import utils
#' @import rvest
#' @importFrom dplyr select rename any_of mutate
#' @importFrom jsonlite fromJSON
#' @importFrom tidyr unnest_wider unchop hoist
#' @importFrom lubridate with_tz ymd_hm
#' @import rvest
#' @export
#' @keywords WNBA Scoreboard
#' @family ESPN WNBA Functions
#' @examples
#' # Get schedule from date 2022-08-31
#' \donttest{
#'   try(espn_wnba_scoreboard (season = "20220831"))
#' }

espn_wnba_scoreboard <- function(season){
  .args <- mget(setdiff(names(formals()), "..."))
  
  # cli::cli_alert_danger("Returning data for {season}!"))
  
  max_year <- substr(Sys.Date(), 1,4)
  
  if (!(as.integer(substr(season, 1, 4)) %in% c(2001:max_year))) {
    message(paste("Error: Season must be between 2001 and", max_year))
  }
  
  # year > 2000
  season <- as.character(season)
  
  season_dates <- season
  
  schedule_api <- paste0("http://site.api.espn.com/apis/site/v2/sports/basketball/wnba/scoreboard?limit=1000&dates=",
                         season_dates)

  tryCatch(
    expr = {
      res <- .retry_request(schedule_api)
      check_status(res)
      raw_sched <- res %>%
        .resp_text() %>%
        jsonlite::fromJSON(simplifyDataFrame = FALSE, simplifyVector = FALSE, simplifyMatrix = FALSE)
      
      wnba_data <- raw_sched[["events"]] %>%
        tibble::tibble(data = .data$.) %>%
        tidyr::unnest_wider("data") %>%
        tidyr::unchop("competitions") %>%
        dplyr::select(
          -"id",
          -"uid",
          -"date",
          -"status") %>%
        tidyr::unnest_wider("competitions") %>%
        dplyr::rename(
          "matchup" = "name",
          "matchup_short" = "shortName",
          "game_id" = "id",
          "game_uid" = "uid",
          "game_date" = "date"
        ) %>%
        tidyr::hoist("status",
                     status_name = list("type", "name")) %>%
        dplyr::select(!dplyr::any_of(
          c(
            "timeValid",
            "neutralSite",
            "conferenceCompetition",
            "recent",
            "venue",
            "type"
          )
        )) %>%
        tidyr::unnest_wider("season", names_sep = "_") %>%
        dplyr::rename("season" = "season_year") %>%
        dplyr::select(-dplyr::any_of("status")) 
      
      wnba_data <- wnba_data %>%
        dplyr::mutate(
          game_date_time = lubridate::ymd_hm(substr(.data$game_date, 1, nchar(.data$game_date) - 1)) %>%
            lubridate::with_tz(tzone = "America/New_York"),
          game_date = as.Date(substr(.data$game_date_time, 1, 10)))
      
      wnba_data <- wnba_data %>% 
        tidyr::hoist(
          "competitors",
          homeAway = list(1,"homeAway")
        )
      wnba_data <- wnba_data %>%
        tidyr::hoist(
          "competitors",
          team1_team_name = list(1, "team", "name"),
          team1_team_logo = list(1, "team", "logo"),
          team1_team_abb = list(1, "team", "abbreviation"),
          team1_team_id = list(1, "team", "id"),
          team1_team_location = list(1, "team", "location"),
          team1_team_full = list(1, "team", "displayName"),
          team1_team_color = list(1, "team", "color"),
          team1_score = list(1, "score"),
          team1_win = list(1, "winner"),
          team1_record = list(1, "records", 1, "summary"),
          # away team
          team2_team_name = list(2, "team", "name"),
          team2_team_logo = list(2, "team", "logo"),
          team2_team_abb = list(2, "team", "abbreviation"),
          team2_team_id = list(2, "team", "id"),
          team2_team_location = list(2, "team", "location"),
          team2_team_full = list(2, "team", "displayName"),
          team2_team_color = list(2, "team", "color"),
          team2_score = list(2, "score"),
          team2_win = list(2, "winner"),
          team2_record = list(2, "records", 1, "summary")) 
      
      
      wnba_data <- wnba_data %>% 
        dplyr::mutate(
          home_team_name = ifelse(.data$homeAway == "home",.data$team1_team_name, .data$team2_team_name),
          home_team_logo = ifelse(.data$homeAway == "home",.data$team1_team_logo, .data$team2_team_logo),
          home_team_abb = ifelse(.data$homeAway == "home",.data$team1_team_abb, .data$team2_team_abb),
          home_team_id = ifelse(.data$homeAway == "home",.data$team1_team_id, .data$team2_team_id),
          home_team_location = ifelse(.data$homeAway == "home",.data$team1_team_location, .data$team2_team_location),
          home_team_full_name = ifelse(.data$homeAway == "home",.data$team1_team_full, .data$team2_team_full),
          home_team_color = ifelse(.data$homeAway == "home",.data$team1_team_color, .data$team2_team_color),
          home_score = ifelse(.data$homeAway == "home",.data$team1_score, .data$team2_score),
          home_win = ifelse(.data$homeAway == "home",.data$team1_win, .data$team2_win),
          home_record = ifelse(.data$homeAway == "home",.data$team1_record, .data$team2_record),
          away_team_name = ifelse(.data$homeAway == "away",.data$team1_team_name, .data$team2_team_name),
          away_team_logo = ifelse(.data$homeAway == "away",.data$team1_team_logo, .data$team2_team_logo),
          away_team_abb = ifelse(.data$homeAway == "away",.data$team1_team_abb, .data$team2_team_abb),
          away_team_id = ifelse(.data$homeAway == "away",.data$team1_team_id, .data$team2_team_id),
          away_team_location = ifelse(.data$homeAway == "away",.data$team1_team_location, .data$team2_team_location),
          away_team_full_name = ifelse(.data$homeAway == "away",.data$team1_team_full, .data$team2_team_full),
          away_team_color = ifelse(.data$homeAway == "away",.data$team1_team_color, .data$team2_team_color),
          away_score = ifelse(.data$homeAway == "away",.data$team1_score, .data$team2_score),
          away_win = ifelse(.data$homeAway == "away",.data$team1_win, .data$team2_win),
          away_record = ifelse(.data$homeAway == "away",.data$team1_record, .data$team2_record)
        )
      
      wnba_data <- wnba_data %>%
        dplyr::mutate_at(c(
          "game_id",
          "home_team_id",
          "home_win",
          "away_team_id",
          "away_win",
          "home_score",
          "away_score"), as.integer)
      wnba_data <- wnba_data %>% 
        dplyr::select(-dplyr::any_of(dplyr::starts_with("team1")),
                      -dplyr::any_of(dplyr::starts_with("team2")),
                      -dplyr::any_of(c("homeAway")))
      
      
      
      if ("broadcasts" %in% names(wnba_data) && !any(is.na(wnba_data[['broadcasts']]))) {
        wnba_data %>%
          tidyr::hoist(
            "broadcasts",
            broadcast_market = list(1, "market"),
            broadcast_name = list(1, "names", 1)) %>%
          dplyr::select(!where(is.list)) %>%
          janitor::clean_names() %>%
          make_wehoop_data("ESPN WNBA Scoreboard Information from ESPN.com",Sys.time())
      } else {
        wnba_data %>%
          dplyr::select(!where(is.list)) %>%
          janitor::clean_names() %>%
          make_wehoop_data("ESPN WNBA Scoreboard Information from ESPN.com",Sys.time())
      }
    },
    error = function(e) .report_api_error(
      e,
      hint = "Invalid arguments or no scoreboard data available!",
      args = .args
    ),
    warning = function(w) .report_api_warning(w, args = .args),
    finally = {
    }
  )
}

#' **Get ESPN WNBA Standings**
#'
#' @author Geoff Hutchinson
#' @param year Either numeric or character (YYYY)
#' @return Returns a tibble
#' 
#'    \if{html}{\tabular{lll}{
#'       col_name \tab types \tab description \cr
#'       team_id \tab integer \tab Unique team identifier. \cr
#'       team \tab character \tab Team-side label or team identifier. \cr
#'       avgpointsagainst \tab numeric \tab Avgpointsagainst. \cr
#'       avgpointsfor \tab numeric \tab Avgpointsfor. \cr
#'       clincher \tab numeric \tab Clincher. \cr
#'       differential \tab numeric \tab Differential. \cr
#'       divisionwinpercent \tab numeric \tab Divisionwinpercent. \cr
#'       gamesbehind \tab numeric \tab Gamesbehind. \cr
#'       leaguewinpercent \tab numeric \tab Leaguewinpercent. \cr
#'       losses \tab numeric \tab Total losses. \cr
#'       playoffseed \tab numeric \tab Playoffseed. \cr
#'       streak \tab numeric \tab Current streak (e.g. 'W3' for three-game win streak). \cr
#'       winpercent \tab numeric \tab Winpercent. \cr
#'       wins \tab numeric \tab Total wins. \cr
#'       leaguestandings \tab character \tab Leaguestandings. \cr
#'       home \tab character \tab Home. \cr
#'       road \tab character \tab Road. \cr
#'       vsdiv \tab character \tab Vsdiv. \cr
#'       vsconf \tab character \tab Vsconf. \cr
#'       lasttengames \tab character \tab Lasttengames. \cr
#'    }}
#'    \if{latex}{See the HTML help or pkgdown reference for the column table.}
#' 
#' @importFrom rlang .data
#' @importFrom jsonlite fromJSON toJSON
#' @importFrom dplyr select rename
#' @importFrom tidyr pivot_wider
#' @importFrom data.table rbindlist
#' @keywords WNBA Standings
#' @family ESPN WNBA Functions
#' @export
#' @examples
#' \donttest{
#'   try(espn_wnba_standings(year = 2021))
#' }
espn_wnba_standings <- function(year){
  .args <- mget(setdiff(names(formals()), "..."))
  
  standings_url <- "https://site.web.api.espn.com/apis/v2/sports/basketball/wnba/standings?region=us&lang=en&contentorigin=espn&type=0&level=1&sort=winpercent%3Adesc%2Cwins%3Adesc%2Cgamesbehind%3Aasc&"
  
  ## Inputs
  ## year
  full_url <- paste0(standings_url,
                     "season=", year)

  standings <- data.frame()

  tryCatch(
    expr = {
      res <- .retry_request(full_url)
      check_status(res)
      resp <- res %>%
        .resp_text()

      raw_standings <- jsonlite::fromJSON(resp)[["standings"]]
      
      #Create a dataframe of all NBA teams by extracting from the raw_standings file
      
      teams <- raw_standings[["entries"]][["team"]]
      
      teams <- teams %>%
        dplyr::select(
          "id", 
          "displayName") %>%
        dplyr::rename(
          "team_id" = "id",
          "team" = "displayName")
      
      #creating a dataframe of the WNBA raw standings table from ESPN
      
      standings_df <- raw_standings[["entries"]][["stats"]]
      
      standings_data <- data.table::rbindlist(standings_df, fill = TRUE, idcol = T)
      
      #Use the following code to replace NA's in the dataframe with the correct corresponding values and removing all unnecessary columns
      
      standings_data$value <- ifelse(is.na(standings_data$value) & !is.na(standings_data$summary), standings_data$summary, standings_data$value)
      
      standings_data <- standings_data %>%
        dplyr::select(
          ".id", 
          "type", 
          "value")
      
      #Use pivot_wider to transpose the dataframe so that we now have a standings row for each team
      
      standings_data <- standings_data %>%
        tidyr::pivot_wider(names_from = "type", values_from = "value")
      
      standings_data <- standings_data %>%
        dplyr::select(-".id")
      
      #joining the 2 dataframes together to create a standings table
      
      standings <- cbind(teams, standings_data) %>%
        dplyr::mutate(team_id = as.integer(.data$team_id)) %>%
        dplyr::mutate_at(c(
          "avgpointsagainst",
          "avgpointsfor",
          "clincher",
          "differential",
          "divisionwinpercent",
          "gamesbehind",
          "leaguewinpercent",
          "losses",
          "playoffseed",
          "streak",
          "winpercent",
          "wins"
        ), as.numeric) %>% 
        make_wehoop_data("ESPN WNBA Standings Information from ESPN.com",Sys.time())
    },
    error = function(e) .report_api_error(
      e,
      hint = "Invalid arguments or no standings data available!",
      args = .args
    ),
    warning = function(w) .report_api_warning(w, args = .args),
    finally = {
    }
    
  )
  return(standings)
}

#' @import utils
utils::globalVariables(c("where"))

#' @title
#' **Get ESPN WNBA team stats data**
#' @author Saiem Gilani
#' @param team_id Team ID
#' @param year Year
#' @param season_type (character, default: regular): Season type - regular or postseason
#' @param total (boolean, default: FALSE): Totals
#' @return Returns a tibble with the team stats data
#' 
#'    \if{html}{\tabular{lll}{
#'       col_name \tab types \tab description \cr
#'       team_id \tab integer \tab Unique team identifier. \cr
#'       team_guid \tab character \tab ESPN team GUID. \cr
#'       team_uid \tab character \tab ESPN universal team identifier (UID format 's:40~l:...~t:...'). \cr
#'       team_sdr \tab integer \tab ESPN team SDR identifier. \cr
#'       team_slug \tab character \tab URL-safe team identifier (e.g. 'lasvegas-aces' / 'aces'). \cr
#'       team_location \tab character \tab Team city or location string. \cr
#'       team_name \tab character \tab Full team display name (e.g. 'Las Vegas Aces'). \cr
#'       team_abbreviation \tab character \tab Short team abbreviation (e.g. 'LAS'). \cr
#'       team_display_name \tab character \tab Full team display name. \cr
#'       team_short_display_name \tab character \tab Short team display name (e.g. 'Aces'). \cr
#'       team_color \tab character \tab Team primary color (hex without leading '#'). \cr
#'       team_alternate_color \tab character \tab Team alternate color (hex without leading '#'). \cr
#'       team_is_active \tab logical \tab TRUE if the team is currently active. \cr
#'       team_is_all_star \tab logical \tab TRUE if the row represents an All-Star team. \cr
#'       logo_href \tab character \tab Team or league logo URL. \cr
#'       logo_dark_href \tab character \tab Logo URL for dark backgrounds. \cr
#'       defensive_blocks \tab numeric \tab Short for blocked shot, number of times when a defensive player legally deflects a field goal attempt from an offensive player. \cr
#'       defensive_defensive_rebounds \tab numeric \tab The number of times when the defense obtains the possession of the ball after a missed shot by the offense. \cr
#'       defensive_steals \tab numeric \tab The number of times a defensive player forced a turnover by intercepting or deflecting a pass or a dribble of an offensive player. \cr
#'       defensive_avg_defensive_rebounds \tab numeric \tab The average defensive rebounds per game. \cr
#'       defensive_avg_blocks \tab numeric \tab The average blocks per game. \cr
#'       defensive_avg_steals \tab numeric \tab The average steals per game. \cr
#'       defensive_avg48defensive_rebounds \tab numeric \tab The average number of defensive rebounds per 48 minutes. \cr
#'       defensive_avg48blocks \tab numeric \tab The average number of blocks per 48 minutes. \cr
#'       defensive_avg48steals \tab numeric \tab The average number of steals per 48 minutes. \cr
#'       general_disqualifications \tab numeric \tab The number of times a player reached the foul limit. \cr
#'       general_flagrant_fouls \tab numeric \tab The number of fouls that the officials thought were unnecessary or excessive. \cr
#'       general_fouls \tab numeric \tab The number of times a player had illegal contact with the opponent. \cr
#'       general_ejections \tab numeric \tab The number of times a player or coach is removed from the game as a result of a serious offense. \cr
#'       general_technical_fouls \tab numeric \tab The number of times an player or coach was called for a technical foul (unsportsmanlike conduct or violations). \cr
#'       general_rebounds \tab numeric \tab The total number of rebounds (offensive and defensive). \cr
#'       general_avg_minutes \tab numeric \tab The average number of minutes per game. \cr
#'       general_nba_rating \tab numeric \tab General nba rating. \cr
#'       general_plus_minus \tab numeric \tab A player's estimated on-court impact on team performance measured in point differential per 100 possessions. \cr
#'       general_game_day_of_year \tab numeric \tab The day of the season on which this game falls; if it's the 15th day of the regular season in Nov, the game day of year is 15 (ie, game day of year != day of year). \cr
#'       general_avg_rebounds \tab numeric \tab The average rebounds per game. \cr
#'       general_avg_fouls \tab numeric \tab The average fouls committed per game. \cr
#'       general_avg_flagrant_fouls \tab numeric \tab The average number of flagrant fouls per game. \cr
#'       general_avg_technical_fouls \tab numeric \tab The average number of technical fouls per game. \cr
#'       general_avg_ejections \tab numeric \tab The average ejections per game. \cr
#'       general_avg_disqualifications \tab numeric \tab The average number of disqualifications per game. \cr
#'       general_assist_turnover_ratio \tab numeric \tab The average number of assists a player or team records per turnover. \cr
#'       general_steal_foul_ratio \tab numeric \tab The average number of steals a player or team records per foul committed. \cr
#'       general_block_foul_ratio \tab numeric \tab The average number of blocks a player or record per foul committed. \cr
#'       general_avg_team_rebounds \tab numeric \tab The average number of rebounds for a team per game. \cr
#'       general_total_rebounds \tab numeric \tab The total number of rebounds for a team or player. \cr
#'       general_total_technical_fouls \tab numeric \tab The total number of technical fouls for a team or player. \cr
#'       general_team_assist_turnover_ratio \tab numeric \tab The number of assists per turnover for a team. \cr
#'       general_team_rebounds \tab numeric \tab The total number of rebounds for a team. \cr
#'       general_steal_turnover_ratio \tab numeric \tab The number of steals per turnover. \cr
#'       general_avg48rebounds \tab numeric \tab The average number of rebounds per 48 minutes. \cr
#'       general_avg48fouls \tab numeric \tab The average number of fouls committed per 48 minutes. \cr
#'       general_avg48flagrant_fouls \tab numeric \tab The average number of flagrant fouls committed per 48 minutes. \cr
#'       general_avg48technical_fouls \tab numeric \tab The average number of technical fouls committed per 48 minutes. \cr
#'       general_avg48ejections \tab numeric \tab The average number of ejections per 48 minutes. \cr
#'       general_avg48disqualifications \tab numeric \tab The average number of disqualifications per 48 minutes. \cr
#'       general_games_played \tab numeric \tab Games Played. \cr
#'       general_games_started \tab numeric \tab The number of games started by an athlete. \cr
#'       general_double_double \tab numeric \tab The number of times double digit values were accumulated in 2 of the following categories: points, rebounds, assists, steals, and blocked shots. \cr
#'       general_triple_double \tab numeric \tab The number of times double digit values were accumulated in 3 of the following categories: points, rebounds, assists, steals, and blocked shots. \cr
#'       offensive_assists \tab numeric \tab The number of times a player who passes the ball to a teammate in a way that leads to a score by field goal, meaning that he or she was "assisting" in the basket. There is some judgment involved in deciding whether a passer should be credited with an assist. \cr
#'       offensive_field_goals \tab numeric \tab Field Goal makes and attempts. \cr
#'       offensive_field_goals_attempted \tab numeric \tab The number of times a 2pt field goal was attempted. \cr
#'       offensive_field_goals_made \tab numeric \tab The number of times a 2pt field goal was made. \cr
#'       offensive_field_goal_pct \tab numeric \tab The ratio of field goals made to field goals attempted: FGM / FGA. \cr
#'       offensive_free_throws \tab numeric \tab Free Throw makes and attempts. \cr
#'       offensive_free_throw_pct \tab numeric \tab The ratio of free throws made to free throws attempted: FTM / FTA. \cr
#'       offensive_free_throws_attempted \tab numeric \tab The number of times a free throw was attempted. \cr
#'       offensive_free_throws_made \tab numeric \tab The number of times a free throw was made. \cr
#'       offensive_offensive_rebounds \tab numeric \tab The number of times when the offense obtains the possession of the ball after a missed shot. \cr
#'       offensive_points \tab numeric \tab The number of points scored. \cr
#'       offensive_turnovers \tab numeric \tab The number of times a player loses possession to the other team. \cr
#'       offensive_three_point_pct \tab numeric \tab The ratio of 3pt field goals made to 3pt field goals attempted: 3PM / 3PA. \cr
#'       offensive_three_point_field_goals_attempted \tab numeric \tab The number of times a 3pt field goal was attempted. \cr
#'       offensive_three_point_field_goals_made \tab numeric \tab The number of times a 3pt field goal was made. \cr
#'       offensive_team_turnovers \tab numeric \tab The number of turnovers for the team. \cr
#'       offensive_total_turnovers \tab numeric \tab The number of turnovers plus team turnovers for the team. \cr
#'       offensive_points_in_paint \tab numeric \tab The amount of points scored in the area known as "the Paint"(the rectangle between the foul line and the baseline). \cr
#'       offensive_brick_index \tab numeric \tab How many points a player costs his team with his shooting compared with the league average on a per-40-minute basis. ((52.8 - TS\%) x (FGA + (FTA x 0.44))) / (Min/40) . \cr
#'       offensive_avg_field_goals_made \tab numeric \tab The average field goals made per game. \cr
#'       offensive_avg_field_goals_attempted \tab numeric \tab The average field goals attempted per game. \cr
#'       offensive_avg_three_point_field_goals_made \tab numeric \tab The average three point field goals made per game. \cr
#'       offensive_avg_three_point_field_goals_attempted \tab numeric \tab The average three point field goals attempted per game. \cr
#'       offensive_avg_free_throws_made \tab numeric \tab The average free throw shots made per game. \cr
#'       offensive_avg_free_throws_attempted \tab numeric \tab The average free throw shots attempted per game. \cr
#'       offensive_avg_points \tab numeric \tab The average number of points scored per game. \cr
#'       offensive_avg_points_allowed \tab numeric \tab The average number of points allowed per game. \cr
#'       offensive_avg_offensive_rebounds \tab numeric \tab The average offensive rebounds per game. \cr
#'       offensive_avg_assists \tab numeric \tab The average assists per game. \cr
#'       offensive_avg_turnovers \tab numeric \tab The average turnovers committed per game. \cr
#'       offensive_offensive_rebound_pct \tab numeric \tab The percentage of the number of times they obtain the possession of the ball after a missed shot. \cr
#'       offensive_estimated_possessions \tab numeric \tab An estimation of the number of possessions for a team or player. \cr
#'       offensive_avg_estimated_possessions \tab numeric \tab The average number of estimated possessions per game for a team or player. \cr
#'       offensive_points_per_estimated_possessions \tab numeric \tab The number of points per estimated possession for a team or player. \cr
#'       offensive_avg_team_turnovers \tab numeric \tab The average number of turnovers for a team per game. \cr
#'       offensive_avg_total_turnovers \tab numeric \tab The average number of total turnovers for a team per game. \cr
#'       offensive_three_point_field_goal_pct \tab numeric \tab The ratio of 3pt field goals made to 3pt field goals attempted: 3PM / 3PA. \cr
#'       offensive_two_point_field_goals_made \tab numeric \tab The number of 2-point field goals made for a team or player. \cr
#'       offensive_two_point_field_goals_attempted \tab numeric \tab The number of 2-point field goals attempted for a team or player. \cr
#'       offensive_avg_two_point_field_goals_made \tab numeric \tab The number of 2-point field goals made per game for a team or player. \cr
#'       offensive_avg_two_point_field_goals_attempted \tab numeric \tab The number of 2-point field goals attempted per game for a team or player. \cr
#'       offensive_two_point_field_goal_pct \tab numeric \tab The percentage of 2-points fields goals made by a team or player. \cr
#'       offensive_shooting_efficiency \tab numeric \tab The efficiency with which a team or player shoots the basketball. \cr
#'       offensive_scoring_efficiency \tab numeric \tab The efficiency with which a team or player scores the basketball. \cr
#'       offensive_avg48field_goals_made \tab numeric \tab The average number of fieldgoals made per 48 minutes. \cr
#'       offensive_avg48field_goals_attempted \tab numeric \tab The average number of fieldgoals attempted per 48 minutes. \cr
#'       offensive_avg48three_point_field_goals_made \tab numeric \tab The average per number of 3-Pointers made per 48 minutes. \cr
#'       offensive_avg48three_point_field_goals_attempted \tab numeric \tab The average number of 3-pointers attempted per 48 minutes. \cr
#'       offensive_avg48free_throws_made \tab numeric \tab The average number of Free Throws made per 48 minutes. \cr
#'       offensive_avg48free_throws_attempted \tab numeric \tab The average number of free throws attempted per 48 minutes. \cr
#'       offensive_avg48points \tab numeric \tab The average number of points scored per 48 minutes. \cr
#'       offensive_avg48offensive_rebounds \tab numeric \tab The average number of offenseive rebounds per 48 minutes. \cr
#'       offensive_avg48assists \tab numeric \tab The average number of assists per 48 minutes. \cr
#'       offensive_avg48turnovers \tab numeric \tab The average number of turnovers per 48 minutes. \cr
#'    }}
#'    \if{latex}{See the HTML help or pkgdown reference for the column table.}
#'    
#' @importFrom jsonlite fromJSON toJSON
#' @importFrom dplyr filter select rename bind_cols bind_rows
#' @importFrom tidyr unnest unnest_wider everything
#' @export
#' @keywords WNBA Team Stats
#' @family ESPN WNBA Functions
#'
#' @examples
#' \donttest{
#'   try(espn_wnba_team_stats(team_id = 17, year = 2020))
#' }

espn_wnba_team_stats <- function(
    team_id, 
    year, 
    season_type = 'regular', 
    total = FALSE
){
  .args <- mget(setdiff(names(formals()), "..."))
  if (!(tolower(season_type) %in% c("regular","postseason"))) {
    # Check if season_type is appropriate, if not regular
    cli::cli_abort("Enter valid season_type: regular or postseason")
  }
  s_type <- ifelse(season_type == "postseason", 3, 2)
  
  base_url <- "https://sports.core.api.espn.com/v2/sports/basketball/leagues/wnba/seasons/"
  
  totals <- ifelse(total == TRUE, 0, "")
  full_url <- paste0(
    base_url,
    year,
    '/types/',s_type,
    '/teams/',team_id,
    '/statistics/', totals
  )
  
  df <- data.frame()
  tryCatch(
    expr = {
      
      # Create the GET request and set response as res
      res <- .retry_request(full_url)
      
      # Check the result
      check_status(res)
      
      # Get the content and return result as data.frame
      df <- res %>%
        .resp_text() %>%
        jsonlite::fromJSON() 
      
      
      team_url <- df[["team"]][["$ref"]]
      
      # Create the GET request and set response as res
      team_res <- .retry_request(team_url)
      
      # Check the result
      check_status(team_res)
      
      # Get the content and return result as data.frame
      team_df <- team_res %>%
        .resp_text() %>%
        jsonlite::fromJSON(simplifyDataFrame = FALSE, simplifyVector = FALSE, simplifyMatrix = FALSE) 
      
      team_df[["links"]] <- NULL
      team_df[["injuries"]] <- NULL
      team_df[["record"]] <- NULL
      team_df[["athletes"]] <- NULL 
      team_df[["venue"]] <- NULL 
      team_df[["groups"]] <- NULL 
      team_df[["ranks"]] <- NULL 
      team_df[["statistics"]] <- NULL 
      team_df[["leaders"]] <- NULL 
      team_df[["links"]] <- NULL 
      team_df[["notes"]] <- NULL 
      team_df[["franchise"]] <- NULL 
      team_df[["record"]] <- NULL
      team_df[["college"]] <- NULL                              
      
      team_df <- team_df %>%
        purrr::map_if(is.list,as.data.frame) %>% 
        as.data.frame() %>% 
        dplyr::select(
          -dplyr::any_of(
            c("logos.width",  
              "logos.height",
              "logos.alt",
              "logos.rel..full.",
              "logos.rel..default.", 
              "logos.lastUpdated",
              "logos.width.1",
              "logos.height.1",
              "logos.alt.1",
              "logos.rel..full..1",
              "logos.rel..dark.",
              "logos.lastUpdated.1",
              "X.ref",
              "X.ref.1",
              "X.ref.2",
              "X.ref.3"))) %>% 
        janitor::clean_names() 
      colnames(team_df)[1:14] <- paste0("team_",colnames(team_df)[1:14])
      
      team_df <- team_df %>%   
        dplyr::rename(
          "logo_href" = "logos_href",
          "logo_dark_href" = "logos_href_1")
      
      df <- df %>%
        purrr::pluck("splits") %>%
        purrr::pluck("categories") %>%
        tidyr::unnest("stats", names_sep = "_")
      df <- df %>%
        dplyr::mutate(
          stats_category_name = paste0(.data$name, "_", .data$stats_name)) %>%
        dplyr::select(
          "stats_category_name", 
          "stats_value") %>%
        tidyr::pivot_wider(
          names_from = "stats_category_name",
          values_from = "stats_value",
          values_fn = dplyr::first) %>%
        janitor::clean_names()
      
      df <- team_df %>% 
        dplyr::bind_cols(df)
      df <- df %>%
        dplyr::mutate_at(c(
          "team_id",
          "team_sdr"), as.integer) %>%
        make_wehoop_data("ESPN WNBA Team Season Stats from ESPN.com",Sys.time())
      
    },
    error = function(e) .report_api_error(
      e,
      hint = "Invalid arguments or no team season stats data available!",
      args = .args
    ),
    warning = function(w) .report_api_warning(w, args = .args),
    finally = {
    }
  )
  return(df)
}

#' @title
#' **Get ESPN WNBA player stats data**
#' @author Saiem Gilani
#' @param athlete_id Athlete ID
#' @param year Year
#' @param season_type (character, default: regular): Season type - regular or postseason
#' @param total (boolean, default: FALSE): Totals
#' @return Returns a tibble with the player stats data
#' 
#'    \if{html}{\tabular{lll}{
#'       col_name \tab types \tab description \cr
#'       athlete_id \tab integer \tab Unique athlete identifier (ESPN). \cr
#'       athlete_uid \tab character \tab ESPN athlete UID (universal identifier). \cr
#'       athlete_guid \tab character \tab ESPN athlete GUID. \cr
#'       athlete_type \tab character \tab Athlete type / class. \cr
#'       sdr \tab integer \tab Sdr. \cr
#'       first_name \tab character \tab Player's first name. \cr
#'       last_name \tab character \tab Player's last name. \cr
#'       full_name \tab character \tab Player's full name. \cr
#'       display_name \tab character \tab Display name. \cr
#'       short_name \tab character \tab Short display name. \cr
#'       weight \tab numeric \tab Player weight in pounds. \cr
#'       display_weight \tab character \tab Player weight in display format (e.g. '180 lbs'). \cr
#'       height \tab numeric \tab Player height (string e.g. '6-2' or inches). \cr
#'       display_height \tab character \tab Player height in display format (e.g. '6-2'). \cr
#'       age \tab integer \tab Player age (in years). \cr
#'       date_of_birth \tab character \tab Date of birth (YYYY-MM-DD). \cr
#'       slug \tab character \tab URL-safe identifier. \cr
#'       headshot_href \tab character \tab Headshot image URL. \cr
#'       headshot_alt \tab character \tab Alternative-text label for the headshot. \cr
#'       position_id \tab integer \tab Unique position identifier. \cr
#'       position_name \tab character \tab Listed roster position ('Guard', 'Forward', 'Center'). \cr
#'       position_display_name \tab character \tab Position display name. \cr
#'       position_abbreviation \tab character \tab Position abbreviation ('G' / 'F' / 'C'). \cr
#'       position_leaf \tab logical \tab Position leaf. \cr
#'       linked \tab logical \tab TRUE if the record is linked to a related entity. \cr
#'       years \tab integer \tab Years. \cr
#'       active \tab logical \tab TRUE if the row represents an active record (player / team / season). \cr
#'       status_id \tab integer \tab Status identifier. \cr
#'       status_name \tab character \tab Status label. \cr
#'       status_type \tab character \tab Status type. \cr
#'       status_abbreviation \tab character \tab Status abbreviation. \cr
#'       defensive_blocks \tab numeric \tab Short for blocked shot, number of times when a defensive player legally deflects a field goal attempt from an offensive player. \cr
#'       defensive_defensive_rebounds \tab numeric \tab The number of times when the defense obtains the possession of the ball after a missed shot by the offense. \cr
#'       defensive_steals \tab numeric \tab The number of times a defensive player forced a turnover by intercepting or deflecting a pass or a dribble of an offensive player. \cr
#'       defensive_avg_defensive_rebounds \tab numeric \tab The average defensive rebounds per game. \cr
#'       defensive_avg_blocks \tab numeric \tab The average blocks per game. \cr
#'       defensive_avg_steals \tab numeric \tab The average steals per game. \cr
#'       defensive_avg48defensive_rebounds \tab numeric \tab The average number of defensive rebounds per 48 minutes. \cr
#'       defensive_avg48blocks \tab numeric \tab The average number of blocks per 48 minutes. \cr
#'       defensive_avg48steals \tab numeric \tab The average number of steals per 48 minutes. \cr
#'       general_disqualifications \tab numeric \tab The number of times a player reached the foul limit. \cr
#'       general_flagrant_fouls \tab numeric \tab The number of fouls that the officials thought were unnecessary or excessive. \cr
#'       general_fouls \tab numeric \tab The number of times a player had illegal contact with the opponent. \cr
#'       general_ejections \tab numeric \tab The number of times a player or coach is removed from the game as a result of a serious offense. \cr
#'       general_technical_fouls \tab numeric \tab The number of times an player or coach was called for a technical foul (unsportsmanlike conduct or violations). \cr
#'       general_rebounds \tab numeric \tab The total number of rebounds (offensive and defensive). \cr
#'       general_vorp \tab numeric \tab Value Over Replacement Player. \cr
#'       general_minutes \tab numeric \tab The total number of minutes played. \cr
#'       general_avg_minutes \tab numeric \tab The average number of minutes per game. \cr
#'       general_fantasy_rating \tab numeric \tab The Fantasy Rating of a player. \cr
#'       general_nba_rating \tab numeric \tab General nba rating. \cr
#'       general_plus_minus \tab numeric \tab A player's estimated on-court impact on team performance measured in point differential per 100 possessions. \cr
#'       general_avg_rebounds \tab numeric \tab The average rebounds per game. \cr
#'       general_avg_fouls \tab numeric \tab The average fouls committed per game. \cr
#'       general_avg_flagrant_fouls \tab numeric \tab The average number of flagrant fouls per game. \cr
#'       general_avg_technical_fouls \tab numeric \tab The average number of technical fouls per game. \cr
#'       general_avg_ejections \tab numeric \tab The average ejections per game. \cr
#'       general_avg_disqualifications \tab numeric \tab The average number of disqualifications per game. \cr
#'       general_assist_turnover_ratio \tab numeric \tab The average number of assists a player or team records per turnover. \cr
#'       general_steal_foul_ratio \tab numeric \tab The average number of steals a player or team records per foul committed. \cr
#'       general_block_foul_ratio \tab numeric \tab The average number of blocks a player or record per foul committed. \cr
#'       general_avg_team_rebounds \tab numeric \tab The average number of rebounds for a team per game. \cr
#'       general_total_rebounds \tab numeric \tab The total number of rebounds for a team or player. \cr
#'       general_total_technical_fouls \tab numeric \tab The total number of technical fouls for a team or player. \cr
#'       general_team_assist_turnover_ratio \tab numeric \tab The number of assists per turnover for a team. \cr
#'       general_steal_turnover_ratio \tab numeric \tab The number of steals per turnover. \cr
#'       general_avg48rebounds \tab numeric \tab The average number of rebounds per 48 minutes. \cr
#'       general_avg48fouls \tab numeric \tab The average number of fouls committed per 48 minutes. \cr
#'       general_avg48flagrant_fouls \tab numeric \tab The average number of flagrant fouls committed per 48 minutes. \cr
#'       general_avg48technical_fouls \tab numeric \tab The average number of technical fouls committed per 48 minutes. \cr
#'       general_avg48ejections \tab numeric \tab The average number of ejections per 48 minutes. \cr
#'       general_avg48disqualifications \tab numeric \tab The average number of disqualifications per 48 minutes. \cr
#'       general_games_played \tab numeric \tab Games Played. \cr
#'       general_games_started \tab numeric \tab The number of games started by an athlete. \cr
#'       general_double_double \tab numeric \tab The number of times double digit values were accumulated in 2 of the following categories: points, rebounds, assists, steals, and blocked shots. \cr
#'       general_triple_double \tab numeric \tab The number of times double digit values were accumulated in 3 of the following categories: points, rebounds, assists, steals, and blocked shots. \cr
#'       offensive_assists \tab numeric \tab The number of times a player who passes the ball to a teammate in a way that leads to a score by field goal, meaning that he or she was "assisting" in the basket. There is some judgment involved in deciding whether a passer should be credited with an assist. \cr
#'       offensive_field_goals \tab numeric \tab Field Goal makes and attempts. \cr
#'       offensive_field_goals_attempted \tab numeric \tab The number of times a 2pt field goal was attempted. \cr
#'       offensive_field_goals_made \tab numeric \tab The number of times a 2pt field goal was made. \cr
#'       offensive_field_goal_pct \tab numeric \tab The ratio of field goals made to field goals attempted: FGM / FGA. \cr
#'       offensive_free_throws \tab numeric \tab Free Throw makes and attempts. \cr
#'       offensive_free_throw_pct \tab numeric \tab The ratio of free throws made to free throws attempted: FTM / FTA. \cr
#'       offensive_free_throws_attempted \tab numeric \tab The number of times a free throw was attempted. \cr
#'       offensive_free_throws_made \tab numeric \tab The number of times a free throw was made. \cr
#'       offensive_offensive_rebounds \tab numeric \tab The number of times when the offense obtains the possession of the ball after a missed shot. \cr
#'       offensive_points \tab numeric \tab The number of points scored. \cr
#'       offensive_turnovers \tab numeric \tab The number of times a player loses possession to the other team. \cr
#'       offensive_three_point_pct \tab numeric \tab The ratio of 3pt field goals made to 3pt field goals attempted: 3PM / 3PA. \cr
#'       offensive_three_point_field_goals_attempted \tab numeric \tab The number of times a 3pt field goal was attempted. \cr
#'       offensive_three_point_field_goals_made \tab numeric \tab The number of times a 3pt field goal was made. \cr
#'       offensive_total_turnovers \tab numeric \tab The number of turnovers plus team turnovers for the team. \cr
#'       offensive_points_in_paint \tab numeric \tab The amount of points scored in the area known as "the Paint"(the rectangle between the foul line and the baseline). \cr
#'       offensive_brick_index \tab numeric \tab How many points a player costs his team with his shooting compared with the league average on a per-40-minute basis. ((52.8 - TS\%) x (FGA + (FTA x 0.44))) / (Min/40) . \cr
#'       offensive_avg_field_goals_made \tab numeric \tab The average field goals made per game. \cr
#'       offensive_avg_field_goals_attempted \tab numeric \tab The average field goals attempted per game. \cr
#'       offensive_avg_three_point_field_goals_made \tab numeric \tab The average three point field goals made per game. \cr
#'       offensive_avg_three_point_field_goals_attempted \tab numeric \tab The average three point field goals attempted per game. \cr
#'       offensive_avg_free_throws_made \tab numeric \tab The average free throw shots made per game. \cr
#'       offensive_avg_free_throws_attempted \tab numeric \tab The average free throw shots attempted per game. \cr
#'       offensive_avg_points \tab numeric \tab The average number of points scored per game. \cr
#'       offensive_avg_offensive_rebounds \tab numeric \tab The average offensive rebounds per game. \cr
#'       offensive_avg_assists \tab numeric \tab The average assists per game. \cr
#'       offensive_avg_turnovers \tab numeric \tab The average turnovers committed per game. \cr
#'       offensive_offensive_rebound_pct \tab numeric \tab The percentage of the number of times they obtain the possession of the ball after a missed shot. \cr
#'       offensive_estimated_possessions \tab numeric \tab An estimation of the number of possessions for a team or player. \cr
#'       offensive_avg_estimated_possessions \tab numeric \tab The average number of estimated possessions per game for a team or player. \cr
#'       offensive_points_per_estimated_possessions \tab numeric \tab The number of points per estimated possession for a team or player. \cr
#'       offensive_avg_team_turnovers \tab numeric \tab The average number of turnovers for a team per game. \cr
#'       offensive_avg_total_turnovers \tab numeric \tab The average number of total turnovers for a team per game. \cr
#'       offensive_three_point_field_goal_pct \tab numeric \tab The ratio of 3pt field goals made to 3pt field goals attempted: 3PM / 3PA. \cr
#'       offensive_two_point_field_goals_made \tab numeric \tab The number of 2-point field goals made for a team or player. \cr
#'       offensive_two_point_field_goals_attempted \tab numeric \tab The number of 2-point field goals attempted for a team or player. \cr
#'       offensive_avg_two_point_field_goals_made \tab numeric \tab The number of 2-point field goals made per game for a team or player. \cr
#'       offensive_avg_two_point_field_goals_attempted \tab numeric \tab The number of 2-point field goals attempted per game for a team or player. \cr
#'       offensive_two_point_field_goal_pct \tab numeric \tab The percentage of 2-points fields goals made by a team or player. \cr
#'       offensive_shooting_efficiency \tab numeric \tab The efficiency with which a team or player shoots the basketball. \cr
#'       offensive_scoring_efficiency \tab numeric \tab The efficiency with which a team or player scores the basketball. \cr
#'       offensive_avg48field_goals_made \tab numeric \tab The average number of fieldgoals made per 48 minutes. \cr
#'       offensive_avg48field_goals_attempted \tab numeric \tab The average number of fieldgoals attempted per 48 minutes. \cr
#'       offensive_avg48three_point_field_goals_made \tab numeric \tab The average per number of 3-Pointers made per 48 minutes. \cr
#'       offensive_avg48three_point_field_goals_attempted \tab numeric \tab The average number of 3-pointers attempted per 48 minutes. \cr
#'       offensive_avg48free_throws_made \tab numeric \tab The average number of Free Throws made per 48 minutes. \cr
#'       offensive_avg48free_throws_attempted \tab numeric \tab The average number of free throws attempted per 48 minutes. \cr
#'       offensive_avg48points \tab numeric \tab The average number of points scored per 48 minutes. \cr
#'       offensive_avg48offensive_rebounds \tab numeric \tab The average number of offenseive rebounds per 48 minutes. \cr
#'       offensive_avg48assists \tab numeric \tab The average number of assists per 48 minutes. \cr
#'       offensive_avg48turnovers \tab numeric \tab The average number of turnovers per 48 minutes. \cr
#'       team_id \tab integer \tab Unique team identifier. \cr
#'       team_guid \tab character \tab ESPN team GUID. \cr
#'       team_uid \tab character \tab ESPN universal team identifier (UID format 's:40~l:...~t:...'). \cr
#'       team_sdr \tab integer \tab ESPN team SDR identifier. \cr
#'       team_slug \tab character \tab URL-safe team identifier (e.g. 'lasvegas-aces' / 'aces'). \cr
#'       team_location \tab character \tab Team city or location string. \cr
#'       team_name \tab character \tab Full team display name (e.g. 'Las Vegas Aces'). \cr
#'       team_abbreviation \tab character \tab Short team abbreviation (e.g. 'LAS'). \cr
#'       team_display_name \tab character \tab Full team display name. \cr
#'       team_short_display_name \tab character \tab Short team display name (e.g. 'Aces'). \cr
#'       team_color \tab character \tab Team primary color (hex without leading '#'). \cr
#'       team_alternate_color \tab character \tab Team alternate color (hex without leading '#'). \cr
#'       team_is_active \tab logical \tab TRUE if the team is currently active. \cr
#'       team_is_all_star \tab logical \tab TRUE if the row represents an All-Star team. \cr
#'       logo_href \tab character \tab Team or league logo URL. \cr
#'       logo_dark_href \tab character \tab Logo URL for dark backgrounds. \cr
#'    }}
#'    \if{latex}{See the HTML help or pkgdown reference for the column table.}
#'    
#' @export
#' @keywords WNBA Player Stats
#' @family ESPN WNBA Functions
#'
#' @examples
#' \donttest{
#'   try(espn_wnba_player_stats(athlete_id = 2529130, year = 2022))
#' }

espn_wnba_player_stats <- function(
    athlete_id, 
    year, 
    season_type = 'regular', 
    total = FALSE
){
  .args <- mget(setdiff(names(formals()), "..."))
  if (!(tolower(season_type) %in% c("regular","postseason"))) {
    # Check if season_type is appropriate, if not regular
    cli::cli_abort("Enter valid season_type: regular or postseason")
  }
  s_type <- ifelse(season_type == "postseason", 3, 2)
  
  base_url <- "https://sports.core.api.espn.com/v2/sports/basketball/leagues/wnba/seasons/"
  
  totals <- ifelse(total == TRUE, 0, "")
  full_url <- paste0(
    base_url,
    year,
    '/types/',s_type,
    '/athletes/', athlete_id,
    '/statistics/', totals
  )
  athlete_url <- paste0(
    base_url,
    year,
    '/athletes/', athlete_id
  )
  df <- data.frame()
  tryCatch(
    expr = {
      
      # Create the GET request and set response as res
      res <- .retry_request(full_url)
      
      # Check the result
      check_status(res)
      # Create the GET request and set response as res
      athlete_res <- .retry_request(athlete_url)
      
      # Check the result
      check_status(athlete_res)
      
      athlete_df <- athlete_res %>%
        .resp_text() %>%
        jsonlite::fromJSON(simplifyDataFrame = FALSE, simplifyVector = FALSE, simplifyMatrix = FALSE) 
      
      team_url <- athlete_df[["team"]][["$ref"]]
      
      # Create the GET request and set response as res
      team_res <- .retry_request(team_url)
      
      # Check the result
      check_status(team_res)
      
      team_df <- team_res %>%
        .resp_text() %>%
        jsonlite::fromJSON(simplifyDataFrame = FALSE, simplifyVector = FALSE, simplifyMatrix = FALSE) 
      
      team_df[["links"]] <- NULL
      team_df[["injuries"]] <- NULL
      team_df[["record"]] <- NULL
      team_df[["athletes"]] <- NULL 
      team_df[["venue"]] <- NULL 
      team_df[["groups"]] <- NULL 
      team_df[["ranks"]] <- NULL 
      team_df[["statistics"]] <- NULL 
      team_df[["leaders"]] <- NULL 
      team_df[["links"]] <- NULL 
      team_df[["notes"]] <- NULL 
      team_df[["franchise"]] <- NULL 
      team_df[["record"]] <- NULL
      team_df[["college"]] <- NULL                              
      
      team_df <- team_df %>%
        purrr::map_if(is.list,as.data.frame) %>% 
        as.data.frame() %>% 
        dplyr::select(
          -dplyr::any_of(
            c("logos.width",  
              "logos.height",
              "logos.alt",
              "logos.rel..full.",
              "logos.rel..default.", 
              "logos.lastUpdated",
              "logos.width.1",
              "logos.height.1",
              "logos.alt.1",
              "logos.rel..full..1",
              "logos.rel..dark.",
              "logos.lastUpdated.1",
              "X.ref",
              "X.ref.1",
              "X.ref.2",
              "X.ref.3"))) %>% 
        janitor::clean_names() 
      colnames(team_df)[1:14] <- paste0("team_",colnames(team_df)[1:14])
      
      team_df <- team_df %>%   
        dplyr::rename(
          "logo_href" = "logos_href",
          "logo_dark_href" = "logos_href_1")
      
      athlete_df[["links"]] <- NULL
      athlete_df[["injuries"]] <- NULL
      athlete_df[["birthPlace"]] <- NULL
      
      athlete_df <- athlete_df %>% 
        purrr::map_if(is.list, as.data.frame) %>% 
        tibble::tibble(data = .data$.)
      athlete_df <- athlete_df$data %>%
        as.data.frame() %>% 
        dplyr::select(-dplyr::any_of(c("X.ref","X.ref.1","X.ref.2","X.ref.3","X.ref.4","X.ref.5","X.ref.6","X.ref.7","position.X.ref"))) %>% 
        janitor::clean_names() %>% 
        dplyr::rename(
          "athlete_id" = "id",
          "athlete_uid" = "uid",
          "athlete_guid" = "guid",
          "athlete_type" = "type")
      
      
      # Get the content and return result as data.frame
      df <- res %>%
        .resp_text() %>%
        jsonlite::fromJSON() %>%
        purrr::pluck("splits") %>%
        purrr::pluck("categories") %>%
        tidyr::unnest("stats", names_sep = "_")
      df <- df %>%
        dplyr::mutate(
          stats_category_name = paste0(.data$name, "_", .data$stats_name)) %>%
        dplyr::select(
          "stats_category_name", 
          "stats_value") %>%
        tidyr::pivot_wider(
          names_from = "stats_category_name",
          values_from = "stats_value",
          values_fn = dplyr::first) %>%
        janitor::clean_names()
      df <- athlete_df %>% 
        dplyr::bind_cols(df) %>% 
        dplyr::bind_cols(team_df)
      df <- df %>%
        dplyr::mutate_at(c(
          "athlete_id",
          "team_id",
          "position_id",
          "status_id",
          "sdr",
          "team_sdr"), as.integer) %>% 
        make_wehoop_data("ESPN WNBA Player Season Stats from ESPN.com",Sys.time())
      
    },
    error = function(e) .report_api_error(
      e,
      hint = "Invalid arguments or no player season stats data available!",
      args = .args
    ),
    warning = function(w) .report_api_warning(w, args = .args),
    finally = {
    }
  )
  return(df)
}


#'  **Parse ESPN WNBA PBP, helper function**
#' @param resp Response object from the ESPN WNBA game summary endpoint
#' @return Returns a tibble
#' @importFrom lubridate with_tz ymd_hm
#' @export
helper_espn_wnba_pbp <- function(resp){
  
  game_json <- resp %>%
    jsonlite::fromJSON()
  pbp_source <- game_json[["header"]][["competitions"]][["playByPlaySource"]]
  plays <- game_json %>%
    purrr::pluck("plays") %>%
    dplyr::as_tibble()
  if (pbp_source != "none" && nrow(plays) > 10) {
    homeAway1 <- jsonlite::fromJSON(resp)[['header']][['competitions']][['competitors']][[1]][['homeAway']][1]
    
    gameId <- as.integer(game_json[["header"]][["id"]])
    season <- game_json[['header']][['season']][['year']]
    season_type <- game_json[['header']][['season']][['type']]
    game_date_time <- substr(game_json[['header']][['competitions']][['date']], 1,
                             nchar(game_json[['header']][['competitions']][['date']]) - 1) %>%
      lubridate::ymd_hm() %>%
      lubridate::with_tz(tzone = "America/New_York")
    
    game_date <- as.Date(substr(game_date_time, 0, 10))
    
    id_vars <- data.frame()
    if (homeAway1 == "home") {
      
      homeTeamId = as.integer(game_json[["header"]][["competitions"]][["competitors"]][[1]][['team']][['id']] %>%
                                purrr::pluck(1, .default = NA_integer_))
      homeTeamMascot = game_json[['header']][['competitions']][['competitors']][[1]][['team']][['name']] %>%
        purrr::pluck(1, .default = NA_character_)
      homeTeamName = game_json[['header']][['competitions']][['competitors']][[1]][['team']][['location']] %>%
        purrr::pluck(1, .default = NA_character_)
      homeTeamAbbrev = game_json[['header']][['competitions']][['competitors']][[1]][['team']][['abbreviation']] %>%
        purrr::pluck(1, .default = NA_character_)
      homeTeamLogo = game_json[['header']][['competitions']][['competitors']][[1]][['team']][['logos']][[1]][['href']] %>%
        purrr::pluck(1, .default = NA_character_)
      homeTeamLogoDark = game_json[['header']][['competitions']][['competitors']][[1]][['team']][['logos']][[1]][['href']] %>%
        purrr::pluck(2, .default = NA_character_)
      homeTeamFullName = game_json[['header']][['competitions']][['competitors']][[1]][['team']][["displayName"]] %>%
        purrr::pluck(1, .default = NA_character_)
      homeTeamColor = game_json[['header']][['competitions']][['competitors']][[1]][['team']][["color"]] %>%
        purrr::pluck(1, .default = NA_character_)
      homeTeamAlternateColor = game_json[['header']][['competitions']][['competitors']][[1]][['team']][["alternateColor"]] %>%
        purrr::pluck(1, .default = NA_character_)
      homeTeamScore = as.integer(game_json[['header']][['competitions']][['competitors']][[1]][['score']] %>%
                                   purrr::pluck(1, .default = NA_character_))
      homeTeamWinner = game_json[['header']][['competitions']][['competitors']][[1]][['winner']] %>%
        purrr::pluck(1, .default = NA_character_)
      homeTeamRecord = game_json[['header']][['competitions']][['competitors']][[1]][['record']][[1]][['summary']] %>%
        purrr::pluck(1, .default = NA_character_)
      awayTeamId = as.integer(game_json[['header']][['competitions']][['competitors']][[1]][['team']][['id']] %>%
                                purrr::pluck(2, .default = NA_integer_))
      awayTeamMascot = game_json[['header']][['competitions']][['competitors']][[1]][['team']][['name']] %>%
        purrr::pluck(2, .default = NA_character_)
      awayTeamName = game_json[['header']][['competitions']][['competitors']][[1]][['team']][['location']] %>%
        purrr::pluck(2, .default = NA_character_)
      awayTeamAbbrev = game_json[['header']][['competitions']][['competitors']][[1]][['team']][['abbreviation']] %>%
        purrr::pluck(2, .default = NA_character_)
      awayTeamLogo = game_json[['header']][['competitions']][['competitors']][[1]][['team']][['logos']][[2]][['href']] %>%
        purrr::pluck(1, .default = NA_character_)
      awayTeamLogoDark = game_json[['header']][['competitions']][['competitors']][[1]][['team']][['logos']][[2]][['href']] %>%
        purrr::pluck(2, .default = NA_character_)
      awayTeamFullName = game_json[['header']][['competitions']][['competitors']][[1]][['team']][["displayName"]] %>%
        purrr::pluck(2, .default = NA_character_)
      awayTeamColor = game_json[['header']][['competitions']][['competitors']][[1]][['team']][["color"]] %>%
        purrr::pluck(2, .default = NA_character_)
      awayTeamAlternateColor = game_json[['header']][['competitions']][['competitors']][[1]][['team']][["alternateColor"]] %>%
        purrr::pluck(2, .default = NA_character_)
      awayTeamScore = as.integer(game_json[['header']][['competitions']][['competitors']][[1]][['score']] %>%
                                   purrr::pluck(2, .default = NA_integer_))
      awayTeamWinner = game_json[['header']][['competitions']][['competitors']][[1]][['winner']] %>%
        purrr::pluck(2, .default = NA_character_)
      awayTeamRecord = game_json[['header']][['competitions']][['competitors']][[1]][['record']][[1]][['summary']] %>%
        purrr::pluck(2, .default = NA_character_)
      id_vars <- data.frame(
        homeTeamId,
        homeTeamMascot,
        homeTeamName,
        homeTeamAbbrev,
        homeTeamLogo,
        homeTeamLogoDark,
        homeTeamFullName,
        homeTeamColor,
        homeTeamAlternateColor,
        homeTeamScore,
        homeTeamWinner,
        homeTeamRecord,
        awayTeamId,
        awayTeamMascot,
        awayTeamName,
        awayTeamAbbrev,
        awayTeamLogo,
        awayTeamLogoDark,
        awayTeamFullName,
        awayTeamColor,
        awayTeamAlternateColor,
        awayTeamScore,
        awayTeamWinner,
        awayTeamRecord
      )
    } else {
      
      awayTeamId = as.integer(game_json[["header"]][["competitions"]][["competitors"]][[1]][['team']][['id']] %>%
                                purrr::pluck(1, .default = NA_integer_))
      awayTeamMascot = game_json[['header']][['competitions']][['competitors']][[1]][['team']][['name']] %>%
        purrr::pluck(1, .default = NA_character_)
      awayTeamName = game_json[['header']][['competitions']][['competitors']][[1]][['team']][['location']] %>%
        purrr::pluck(1, .default = NA_character_)
      awayTeamAbbrev = game_json[['header']][['competitions']][['competitors']][[1]][['team']][['abbreviation']] %>%
        purrr::pluck(1, .default = NA_character_)
      awayTeamLogo = game_json[['header']][['competitions']][['competitors']][[1]][['team']][['logos']][[1]][['href']] %>%
        purrr::pluck(1, .default = NA_character_)
      awayTeamLogoDark = game_json[['header']][['competitions']][['competitors']][[1]][['team']][['logos']][[1]][['href']] %>%
        purrr::pluck(2, .default = NA_character_)
      awayTeamFullName = game_json[['header']][['competitions']][['competitors']][[1]][['team']][["displayName"]] %>%
        purrr::pluck(1, .default = NA_character_)
      awayTeamColor = game_json[['header']][['competitions']][['competitors']][[1]][['team']][["color"]] %>%
        purrr::pluck(1, .default = NA_character_)
      awayTeamAlternateColor = game_json[['header']][['competitions']][['competitors']][[1]][['team']][["alternateColor"]] %>%
        purrr::pluck(1, .default = NA_character_)
      awayTeamScore = as.integer(game_json[['header']][['competitions']][['competitors']][[1]][['score']] %>%
                                   purrr::pluck(1, .default = NA_integer_))
      awayTeamWinner = game_json[['header']][['competitions']][['competitors']][[1]][['winner']] %>%
        purrr::pluck(1, .default = NA_character_)
      awayTeamRecord = game_json[['header']][['competitions']][['competitors']][[1]][['record']][[1]][['summary']] %>%
        purrr::pluck(1, .default = NA_character_)
      homeTeamId = as.integer(game_json[['header']][['competitions']][['competitors']][[1]][['team']][['id']] %>%
                                purrr::pluck(2, .default = NA_integer_))
      homeTeamMascot = game_json[['header']][['competitions']][['competitors']][[1]][['team']][['name']] %>%
        purrr::pluck(2, .default = NA_character_)
      homeTeamName = game_json[['header']][['competitions']][['competitors']][[1]][['team']][['location']] %>%
        purrr::pluck(2, .default = NA_character_)
      homeTeamAbbrev = game_json[['header']][['competitions']][['competitors']][[1]][['team']][['abbreviation']] %>%
        purrr::pluck(2, .default = NA_character_)
      homeTeamLogo = game_json[['header']][['competitions']][['competitors']][[1]][['team']][['logos']][[2]][['href']] %>%
        purrr::pluck(2, .default = NA_character_)
      homeTeamLogoDark = game_json[['header']][['competitions']][['competitors']][[1]][['team']][['logos']][[2]][['href']] %>%
        purrr::pluck(2, .default = NA_character_)
      homeTeamFullName = game_json[['header']][['competitions']][['competitors']][[1]][['team']][["displayName"]] %>%
        purrr::pluck(2, .default = NA_character_)
      homeTeamColor = game_json[['header']][['competitions']][['competitors']][[1]][['team']][["color"]] %>%
        purrr::pluck(2, .default = NA_character_)
      homeTeamAlternateColor = game_json[['header']][['competitions']][['competitors']][[1]][['team']][["alternateColor"]] %>%
        purrr::pluck(2, .default = NA_character_)
      homeTeamScore = as.integer(game_json[['header']][['competitions']][['competitors']][[1]][['score']] %>%
                                   purrr::pluck(2, .default = NA_integer_))
      homeTeamWinner = game_json[['header']][['competitions']][['competitors']][[1]][['winner']] %>%
        purrr::pluck(2, .default = NA_character_)
      homeTeamRecord = game_json[['header']][['competitions']][['competitors']][[1]][['record']][[1]][['summary']] %>%
        purrr::pluck(2, .default = NA_character_)
      id_vars <- data.frame(
        homeTeamId,
        homeTeamMascot,
        homeTeamName,
        homeTeamAbbrev,
        homeTeamLogo,
        homeTeamLogoDark,
        homeTeamFullName,
        homeTeamColor,
        homeTeamAlternateColor,
        homeTeamScore,
        homeTeamWinner,
        homeTeamRecord,
        awayTeamId,
        awayTeamMascot,
        awayTeamName,
        awayTeamAbbrev,
        awayTeamLogo,
        awayTeamLogoDark,
        awayTeamFullName,
        awayTeamColor,
        awayTeamAlternateColor,
        awayTeamScore,
        awayTeamWinner,
        awayTeamRecord
      )
      
    }
    
    game_json <- game_json %>%
      jsonlite::toJSON() %>%
      jsonlite::fromJSON(flatten = TRUE)
    
    
    plays <- game_json %>% 
      purrr::pluck("plays")
    
    if (("coordinate.x" %in% names(plays)) && ("coordinate.y" %in% names(plays))) {
      plays <- plays %>%
        dplyr::mutate(
          # convert types
          coordinate.x = as.double(.data$coordinate.x),
          coordinate.y = as.double(.data$coordinate.y),
          # Free throws are adjusted automatically to 19' from baseline, which
          # corresponds to 13.75' from the center of the basket (originally
          # the center of the basket is (25, 0))
          coordinate.y = dplyr::case_when(
            stringr::str_detect(.data$type.text, "Free Throw") ~ 13.75,
            TRUE ~ .data$coordinate.y
          ),
          coordinate.x = dplyr::case_when(
            stringr::str_detect(.data$type.text, "Free Throw") ~ 25,
            TRUE ~ .data$coordinate.x
          ),
          coordinate_x_transformed = dplyr::case_when(
            .data$team.id == homeTeamId ~ -1 * (.data$coordinate.y - 41.75),
            TRUE ~ .data$coordinate.y - 41.75
          ),
          coordinate_y_transformed = dplyr::case_when(
            .data$team.id == homeTeamId ~ -1 * (.data$coordinate.x - 25),
            TRUE ~ .data$coordinate.x - 25
          )
        ) %>%
        dplyr::rename(
          "coordinate.x.raw" = "coordinate.x",
          "coordinate.y.raw" = "coordinate.y",
          "coordinate.x" = "coordinate_x_transformed",
          "coordinate.y" = "coordinate_y_transformed"
        )
    }
    
    ## Written this way for compliance with data repository processing
    if ("participants" %in% names(plays)) {
      plays <- plays %>%
        tidyr::unnest_wider("participants")
      suppressWarnings(
        aths <- plays %>%
          dplyr::group_by(.data$id) %>%
          dplyr::select(
            "id",
            "athlete.id") %>%
          tidyr::unnest_wider("athlete.id", names_sep = "_")
      )
      names(aths) <- c("play.id", paste0("athlete.id.", seq_len(ncol(aths) - 1)))
      for (nm in paste0("athlete.id.", 1:3)) {
        if (!nm %in% names(aths)) aths[[nm]] <- NA_character_
      }
      aths <- aths[, c("play.id", paste0("athlete.id.", 1:3))]
      plays <- plays %>%
        dplyr::bind_cols(aths) %>%
        janitor::clean_names() %>%
        dplyr::mutate(dplyr::across(dplyr::any_of(c(
          "athlete_id_1",
          "athlete_id_2",
          "athlete_id_3"
        )), ~as.integer(.x)))
    }
    ## Written this way for compliance with data repository processing
    if (!("homeTeamName" %in% names(plays))) {
      plays <- plays %>%
        dplyr::bind_cols(id_vars)
    }
    
    plays <- plays %>%
      dplyr::select(-dplyr::any_of(c("athlete.id", "athlete_id")))  %>%
      janitor::clean_names() %>%
      dplyr::mutate(
        game_id = gameId,
        season = season,
        season_type = season_type,
        game_date = game_date,
        game_date_time = game_date_time) %>%
      dplyr::rename(dplyr::any_of(c(
        "athlete_id_1" = "participants_0_athlete_id",
        "athlete_id_2" = "participants_1_athlete_id",
        "athlete_id_3" = "participants_2_athlete_id")))
    
    plays <- plays %>%
      dplyr::mutate(dplyr::across(dplyr::any_of(c(
        "athlete_id_1",
        "athlete_id_2",
        "athlete_id_3",
        "type_id",
        "team_id"
      )), ~as.integer(.x)))
    
    plays_df <- plays %>%
      make_wehoop_data("ESPN WNBA Play-by-Play Information from ESPN.com", Sys.time())
    
    return(plays_df)
  }
}

#'  **Parse ESPN WNBA Team Box, helper function**
#' @rdname helper_espn_wnba_pbp
#' @param resp Response object from the ESPN WNBA game summary endpoint
#' @return Returns a tibble
#' @importFrom lubridate with_tz ymd_hm
#' @export
helper_espn_wnba_team_box <- function(resp){
  
  game_json <- resp %>%
    jsonlite::fromJSON()
  
  gameId <- as.integer(game_json[["header"]][["id"]])
  game_date_time <- substr(game_json[['header']][['competitions']][['date']], 1,
                           nchar(game_json[['header']][['competitions']][['date']]) - 1) %>%
    lubridate::ymd_hm() %>%
    lubridate::with_tz(tzone = "America/New_York")
  
  game_date <- as.Date(substr(game_date_time, 0, 10))
  # ESPN's header `boxscoreAvailable` flag is unreliable for archival games
  # (the WBB twin drops pre-2014 boxscores over it), so availability is
  # derived from the payload itself; the statistics-length check below
  # remains the real gate.
  box_score_available <- length(game_json[["boxscore"]][["teams"]]) > 0
  if (box_score_available == TRUE) {
    teams_box_score_df <- game_json[["boxscore"]][["teams"]] %>%
      jsonlite::toJSON() %>%
      jsonlite::fromJSON(flatten = TRUE)
    if (length(teams_box_score_df[["statistics"]][[1]]) > 0) {
      # Teams info columns and values
      teams_df <- game_json[["header"]][["competitions"]][["competitors"]][[1]]
      
      homeAway1 <- teams_df[["homeAway"]][1]
      homeAway1_team.id <- as.integer(teams_df[["id"]][1])
      homeAway1_team.score <- as.integer(teams_df[["score"]][1])
      homeAway1_team.winner <- teams_df[["winner"]][1]
      
      homeAway2 <- teams_df[["homeAway"]][2]
      homeAway2_team.id <- as.integer(teams_df[["id"]][2])
      homeAway2_team.score <- as.integer(teams_df[["score"]][2])
      homeAway2_team.winner <- teams_df[["winner"]][2]
      
      # Pivoting the table values for each team from long to wide
      statistics_df_1 <- teams_box_score_df[["statistics"]][[1]] %>%
        tibble::tibble() %>%
        dplyr::select("name", "displayValue") %>%
        tidyr::spread("name", "displayValue")
      
      statistics_df_2 <- teams_box_score_df[["statistics"]][[2]] %>%
        tibble::tibble() %>%
        dplyr::select("name", "displayValue") %>%
        tidyr::spread("name", "displayValue")
      
      # Assigning values to the correct data frame rows - 1
      statistics_df_1$team.homeAway <- ifelse(
        as.integer(teams_box_score_df[["team.id"]][1]) == as.integer(homeAway1_team.id),
        homeAway1,
        homeAway2
      )
      statistics_df_1$team.score <- ifelse(
        as.integer(teams_box_score_df[["team.id"]][1]) == as.integer(homeAway1_team.id),
        as.integer(homeAway1_team.score),
        as.integer(homeAway2_team.score)
      )
      statistics_df_1$team.winner <- ifelse(
        as.integer(teams_box_score_df[["team.id"]][1]) == as.integer(homeAway1_team.id),
        homeAway1_team.winner,
        homeAway2_team.winner
      )
      statistics_df_1$team.id <- as.integer(teams_box_score_df[["team.id"]][[1]])
      statistics_df_1$team.uid <- teams_box_score_df[["team.uid"]][[1]]
      statistics_df_1$team.slug <- teams_box_score_df[["team.slug"]][[1]]
      statistics_df_1$team.location <- teams_box_score_df[["team.location"]][[1]]
      statistics_df_1$team.name <- teams_box_score_df[["team.name"]][[1]]
      statistics_df_1$team.abbreviation <- teams_box_score_df[["team.abbreviation"]][[1]]
      statistics_df_1$team.displayName <- teams_box_score_df[["team.displayName"]][[1]]
      statistics_df_1$team.shortDisplayName <- teams_box_score_df[["team.shortDisplayName"]][[1]]
      statistics_df_1$team.color <- teams_box_score_df[["team.color"]][[1]]
      statistics_df_1$team.alternateColor <- teams_box_score_df[["team.alternateColor"]][[1]]
      statistics_df_1$team.logo <- teams_box_score_df[["team.logo"]][[1]]
      statistics_df_1$opponent.team.id <- as.integer(teams_box_score_df[["team.id"]][[2]])
      statistics_df_1$opponent.team.uid <- teams_box_score_df[["team.uid"]][[2]]
      statistics_df_1$opponent.team.slug <- teams_box_score_df[["team.slug"]][[2]]
      statistics_df_1$opponent.team.location <- teams_box_score_df[["team.location"]][[2]]
      statistics_df_1$opponent.team.name <- teams_box_score_df[["team.name"]][[2]]
      statistics_df_1$opponent.team.abbreviation <- teams_box_score_df[["team.abbreviation"]][[2]]
      statistics_df_1$opponent.team.displayName <- teams_box_score_df[["team.displayName"]][[2]]
      statistics_df_1$opponent.team.shortDisplayName <- teams_box_score_df[["team.shortDisplayName"]][[2]]
      statistics_df_1$opponent.team.color <- teams_box_score_df[["team.color"]][[2]]
      statistics_df_1$opponent.team.alternateColor <- teams_box_score_df[["team.alternateColor"]][[2]]
      statistics_df_1$opponent.team.logo <- teams_box_score_df[["team.logo"]][[2]]
      statistics_df_1$opponent.team.score <- ifelse(
        as.integer(teams_box_score_df[["team.id"]][1]) == as.integer(homeAway1_team.id),
        as.integer(homeAway2_team.score),
        as.integer(homeAway1_team.score)
      )
      
      # Assigning values to the correct data frame rows - 2
      statistics_df_2$team.homeAway <- ifelse(
        as.integer(teams_box_score_df[["team.id"]][2]) == as.integer(homeAway2_team.id),
        homeAway2,
        homeAway1
      )
      statistics_df_2$team.score <- ifelse(
        as.integer(teams_box_score_df[["team.id"]][2]) == as.integer(homeAway2_team.id),
        as.integer(homeAway2_team.score),
        as.integer(homeAway1_team.score)
      )
      statistics_df_2$team.winner <- ifelse(
        as.integer(teams_box_score_df[["team.id"]][2]) == as.integer(homeAway2_team.id),
        homeAway2_team.winner,
        homeAway1_team.winner
      )
      statistics_df_2$team.id <- as.integer(teams_box_score_df[["team.id"]][[2]])
      statistics_df_2$team.uid <- teams_box_score_df[["team.uid"]][[2]]
      statistics_df_2$team.slug <- teams_box_score_df[["team.slug"]][[2]]
      statistics_df_2$team.location <- teams_box_score_df[["team.location"]][[2]]
      statistics_df_2$team.name <- teams_box_score_df[["team.name"]][[2]]
      statistics_df_2$team.abbreviation <- teams_box_score_df[["team.abbreviation"]][[2]]
      statistics_df_2$team.displayName <- teams_box_score_df[["team.displayName"]][[2]]
      statistics_df_2$team.shortDisplayName <- teams_box_score_df[["team.shortDisplayName"]][[2]]
      statistics_df_2$team.color <- teams_box_score_df[["team.color"]][[2]]
      statistics_df_2$team.alternateColor <- teams_box_score_df[["team.alternateColor"]][[2]]
      statistics_df_2$team.logo <- teams_box_score_df[["team.logo"]][[2]]
      statistics_df_2$opponent.team.id <- as.integer(teams_box_score_df[["team.id"]][[1]])
      statistics_df_2$opponent.team.uid <- teams_box_score_df[["team.uid"]][[1]]
      statistics_df_2$opponent.team.slug <- teams_box_score_df[["team.slug"]][[1]]
      statistics_df_2$opponent.team.location <- teams_box_score_df[["team.location"]][[1]]
      statistics_df_2$opponent.team.name <- teams_box_score_df[["team.name"]][[1]]
      statistics_df_2$opponent.team.abbreviation <- teams_box_score_df[["team.abbreviation"]][[1]]
      statistics_df_2$opponent.team.displayName <- teams_box_score_df[["team.displayName"]][[1]]
      statistics_df_2$opponent.team.shortDisplayName <- teams_box_score_df[["team.shortDisplayName"]][[1]]
      statistics_df_2$opponent.team.color <- teams_box_score_df[["team.color"]][[1]]
      statistics_df_2$opponent.team.alternateColor <- teams_box_score_df[["team.alternateColor"]][[1]]
      statistics_df_2$opponent.team.logo <- teams_box_score_df[["team.logo"]][[1]]
      statistics_df_2$opponent.team.score <- ifelse(
        as.integer(teams_box_score_df[["team.id"]][2]) == as.integer(homeAway2_team.id),
        as.integer(homeAway1_team.score),
        as.integer(homeAway2_team.score)
      )
      
      complete_statistics_df <- statistics_df_1 %>%
        dplyr::bind_rows(statistics_df_2)
      
      # Assigning game/season level data to team box score and converting types
      complete_statistics_df$season <- game_json[["header"]][["season"]][["year"]]
      complete_statistics_df$season_type <- game_json[["header"]][["season"]][["type"]]
      complete_statistics_df$game_date <- as.Date(substr(game_json[["header"]][["competitions"]][["date"]], 0, 10))
      complete_statistics_df$game_id <- as.integer(gameId)
      complete_statistics_df$game_date_time <- game_date_time
      complete_statistics_df$game_date <- game_date
      
      suppressWarnings(
        complete_statistics_df <- complete_statistics_df %>%
          tidyr::separate("fieldGoalsMade-fieldGoalsAttempted",
                          into = c("fieldGoalsMade", "fieldGoalsAttempted"),
                          sep = "-") %>%
          tidyr::separate("freeThrowsMade-freeThrowsAttempted",
                          into = c("freeThrowsMade", "freeThrowsAttempted"),
                          sep = "-") %>%
          tidyr::separate("threePointFieldGoalsMade-threePointFieldGoalsAttempted",
                          into = c("threePointFieldGoalsMade", "threePointFieldGoalsAttempted"),
                          sep = "-") %>%
          dplyr::mutate(dplyr::across(c(
            "fieldGoalPct",
            "freeThrowPct",
            "threePointFieldGoalPct"
          ), ~as.numeric(.x))) %>%
          dplyr::mutate(dplyr::across(dplyr::any_of(c(
            "assists",
            "blocks",
            "defensiveRebounds",
            "fieldGoalsMade",
            "fieldGoalsAttempted",
            "flagrantFouls",
            "fouls",
            "freeThrowsMade",
            "freeThrowsAttempted",
            "offensiveRebounds",
            "steals",
            "teamTurnovers",
            "technicalFouls",
            "threePointFieldGoalsMade",
            "threePointFieldGoalsAttempted",
            "totalRebounds",
            "totalTechnicalFouls",
            "totalTurnovers",
            "turnovers"
          )), ~as.integer(.x)))
      )
      team_box_score <- complete_statistics_df %>%
        janitor::clean_names() %>%
        dplyr::select(dplyr::any_of(c(
          "game_id",
          "season",
          "season_type",
          "game_date",
          "game_date_time",
          "team_id",
          "team_uid",
          "team_slug",
          "team_location",
          "team_name",
          "team_abbreviation",
          "team_display_name",
          "team_short_display_name",
          "team_color",
          "team_alternate_color",
          "team_logo",
          "team_home_away",
          "team_score",
          "team_winner")),
          tidyr::everything()) %>%
        make_wehoop_data("ESPN WNBA Team Box Information from ESPN.com", Sys.time())
      
      return(team_box_score)
    }
  }
}

#'  **Parse ESPN WNBA Player Box, helper function**
#' @rdname helper_espn_wnba_pbp
#' @param resp Response object from the ESPN WNBA game summary endpoint
#' @return Returns a tibble
#' @importFrom lubridate with_tz ymd_hm
#' @export
helper_espn_wnba_player_box <- function(resp){
  
  game_json <- resp %>%
    jsonlite::fromJSON(flatten = TRUE)
  
  players_box_score_df <- game_json[["boxscore"]][["players"]] %>%
    jsonlite::toJSON() %>%
    jsonlite::fromJSON(flatten = TRUE) %>%
    as.data.frame()
  
  gameId <- as.integer(game_json[["header"]][["id"]])
  season <- game_json[["header"]][["season"]][["year"]]
  season_type <- game_json[["header"]][["season"]][["type"]]
  game_date_time <- substr(game_json[['header']][['competitions']][['date']], 1,
                           nchar(game_json[['header']][['competitions']][['date']]) - 1) %>%
    lubridate::ymd_hm() %>%
    lubridate::with_tz(tzone = "America/New_York")
  
  game_date <- as.Date(substr(game_date_time, 0, 10))
  
  boxScoreAvailable <- game_json[["header"]][["competitions"]][["boxscoreAvailable"]]
  
  boxScoreSource <- game_json[["header"]][["competitions"]][["boxscoreSource"]]
  
  # This is checking if  [[athletes]][[1]]'s stat rebounds is able to be converted to a numeric value
  #  without introducing NA's
  suppressWarnings(
    valid_stats <- players_box_score_df[["statistics"]][[1]][["athletes"]][[1]][["stats"]][[1]] %>% 
      purrr::pluck(7) %>% 
      as.numeric()
  )
  # Payload presence replaces ESPN's unreliable header `boxscoreAvailable`
  # flag (archival games carry stats while the flag says FALSE); the athlete
  # and stat validity conjuncts below remain the real gate.
  if (length(game_json[["boxscore"]][["players"]]) > 0 &&
      length(players_box_score_df[["statistics"]][[1]][["athletes"]][[1]]) > 1 &&
      !is.na(valid_stats)) {
      players_df <- players_box_score_df %>%
        tidyr::unnest("statistics") %>%
        tidyr::unnest("athletes")
    if (length(players_box_score_df[["statistics"]]) > 1 &&
        length(players_df$stats[[1]]) > 0) {
      
      players_df <- jsonlite::fromJSON(jsonlite::toJSON(game_json[["boxscore"]][["players"]]), flatten = TRUE) %>%
        tidyr::unnest("statistics") %>%
        tidyr::unnest("athletes")
      
      stat_cols <- players_df$keys[[1]]
      stats <- players_df$stats
      
      stats_df <- as.data.frame(do.call(rbind,stats))
      colnames(stats_df) <- stat_cols
      suppressWarnings(
        stats_df <- stats_df %>%
          tidyr::separate("fieldGoalsMade-fieldGoalsAttempted",
                          into = c("fieldGoalsMade", "fieldGoalsAttempted"),
                          sep = "-") %>%
          tidyr::separate("freeThrowsMade-freeThrowsAttempted",
                          into = c("freeThrowsMade", "freeThrowsAttempted"),
                          sep = "-") %>%
          tidyr::separate("threePointFieldGoalsMade-threePointFieldGoalsAttempted",
                          into = c("threePointFieldGoalsMade", "threePointFieldGoalsAttempted"),
                          sep = "-") %>%
          dplyr::mutate(dplyr::across(dplyr::any_of(c(
            "minutes",
            "fieldGoalPct",
            "freeThrowPct",
            "threePointFieldGoalPct"
          )), ~as.numeric(.x))) %>%
          dplyr::mutate(dplyr::across(dplyr::any_of(c(
            "assists",
            "blocks",
            "defensiveRebounds",
            "fieldGoalsMade",
            "fieldGoalsAttempted",
            "flagrantFouls",
            "fouls",
            "freeThrowsMade",
            "freeThrowsAttempted",
            "offensiveRebounds",
            "steals",
            "teamTurnovers",
            "technicalFouls",
            "threePointFieldGoalsMade",
            "threePointFieldGoalsAttempted",
            "rebounds",
            "totalTechnicalFouls",
            "totalTurnovers",
            "turnovers",
            "points"
          )), ~as.integer(.x)))
      )
      players_df_did_not_play <- players_df %>%
        dplyr::filter(.data$didNotPlay) %>%
        dplyr::select(dplyr::any_of(c(
          "starter",
          "ejected",
          "didNotPlay",
          "reason",
          "active",
          "athlete.displayName",
          "athlete.jersey",
          "athlete.id",
          "athlete.shortName",
          "athlete.headshot.href",
          "athlete.position.name",
          "athlete.position.abbreviation",
          "team.displayName",
          "team.shortDisplayName",
          "team.location",
          "team.name",
          "team.logo",
          "team.id",
          "team.uid",
          "team.slug",
          "team.abbreviation",
          "team.color",
          "team.alternateColor"
        )))
      
      players_df <- players_df %>%
        dplyr::filter(!.data$didNotPlay) %>%
        dplyr::select(dplyr::any_of(c(
          "starter",
          "ejected",
          "didNotPlay",
          "reason",
          "active",
          "athlete.displayName",
          "athlete.jersey",
          "athlete.id",
          "athlete.shortName",
          "athlete.headshot.href",
          "athlete.position.name",
          "athlete.position.abbreviation",
          "team.displayName",
          "team.shortDisplayName",
          "team.location",
          "team.name",
          "team.logo",
          "team.id",
          "team.uid",
          "team.slug",
          "team.abbreviation",
          "team.color",
          "team.alternateColor"
        )))
      
      players_df <- stats_df %>%
        dplyr::bind_cols(players_df) %>%
        dplyr::bind_rows(players_df_did_not_play)
      
      players_df <- players_df %>%
        dplyr::select(dplyr::any_of(c(
          "athlete.displayName",
          "team.shortDisplayName")),
          tidyr::everything()) %>%
        janitor::clean_names() %>%
        dplyr::mutate(
          game_id = gameId,
          season = season,
          season_type = season_type,
          game_date = game_date,
          game_date_time = game_date_time)
      
      
      teams_df <- game_json[["header"]][["competitions"]][["competitors"]][[1]]
      
      homeAway1 <- teams_df[["homeAway"]] %>%
        purrr::pluck(1, .default = NA_character_)
      homeAway1_team.id <- as.integer(teams_df[["id"]] %>%
                                        purrr::pluck(1, .default = NA_integer_))
      homeAway1_team.location <- teams_df[["team.location"]] %>%
        purrr::pluck(1, .default = NA_character_)
      homeAway1_team.name <- teams_df[["team.name"]] %>%
        purrr::pluck(1, .default = NA_character_)
      homeAway1_team.abbreviation <- teams_df[["team.abbreviation"]] %>%
        purrr::pluck(1, .default = NA_character_)
      homeAway1_team.displayName <- teams_df[["team.displayName"]] %>%
        purrr::pluck(1, .default = NA_character_)
      homeAway1_team.logos <- teams_df[["team.logos"]][[1]][["href"]] %>%
        purrr::pluck(1, .default = NA_character_)
      homeAway1_team.color <- teams_df[["team.color"]] %>%
        purrr::pluck(1, .default = NA_character_)
      homeAway1_team.alternateColor <- teams_df[["team.alternateColor"]] %>%
        purrr::pluck(1, .default = NA_character_)
      homeAway1_team.winner <- teams_df[["winner"]] %>%
        purrr::pluck(1, .default = NA_character_)
      homeAway1_team.score <- as.integer(teams_df[["score"]] %>%
                                           purrr::pluck(1, .default = NA_integer_))
      
      homeAway2 <- teams_df[["homeAway"]] %>%
        purrr::pluck(2, .default = NA_character_)
      homeAway2_team.id <- as.integer(teams_df[["id"]] %>%
                                        purrr::pluck(2, .default = NA_integer_))
      homeAway2_team.location <- teams_df[["team.location"]] %>%
        purrr::pluck(2, .default = NA_character_)
      homeAway2_team.name <- teams_df[["team.name"]] %>%
        purrr::pluck(2, .default = NA_character_)
      homeAway2_team.abbreviation <- teams_df[["team.abbreviation"]] %>%
        purrr::pluck(2, .default = NA_character_)
      homeAway2_team.displayName <- teams_df[["team.displayName"]] %>%
        purrr::pluck(2, .default = NA_character_)
      homeAway2_team.logos <- teams_df[["team.logos"]][[2]][["href"]] %>%
        purrr::pluck(1, .default = NA_character_)
      homeAway2_team.color <- teams_df[["team.color"]] %>%
        purrr::pluck(2, .default = NA_character_)
      homeAway2_team.alternateColor <- teams_df[["team.alternateColor"]] %>%
        purrr::pluck(2, .default = NA_character_)
      homeAway2_team.winner <- teams_df[["winner"]] %>%
        purrr::pluck(2, .default = NA_character_)
      homeAway2_team.score <- as.integer(teams_df[["score"]] %>%
                                           purrr::pluck(2, .default = NA_integer_))
      
      players_df <- players_df %>%
        dplyr::mutate(
          home_away = ifelse(.data$team_id == homeAway1_team.id, homeAway1, homeAway2),
          team_winner = ifelse(.data$team_id == homeAway1_team.id, homeAway1_team.winner, homeAway2_team.winner),
          team_score = ifelse(.data$team_id == homeAway1_team.id, homeAway1_team.score, homeAway2_team.score),
          opponent_team_id = ifelse(.data$team_id == homeAway1_team.id, homeAway2_team.id, homeAway1_team.id),
          opponent_team_name = ifelse(.data$team_id == homeAway1_team.id, homeAway2_team.name, homeAway1_team.name),
          opponent_team_location = ifelse(.data$team_id == homeAway1_team.id, homeAway2_team.location, homeAway1_team.location),
          opponent_team_display_name = ifelse(.data$team_id == homeAway1_team.id, homeAway2_team.displayName, homeAway1_team.displayName),
          opponent_team_abbreviation = ifelse(.data$team_id == homeAway1_team.id, homeAway2_team.abbreviation, homeAway1_team.abbreviation),
          opponent_team_logo = ifelse(.data$team_id == homeAway1_team.id, homeAway2_team.logos, homeAway1_team.logos),
          opponent_team_color = ifelse(.data$team_id == homeAway1_team.id, homeAway2_team.color, homeAway1_team.color),
          opponent_team_alternate_color = ifelse(.data$team_id == homeAway1_team.id, homeAway2_team.alternateColor, homeAway1_team.alternateColor),
          opponent_team_score = ifelse(.data$team_id == homeAway1_team.id, homeAway2_team.score, homeAway1_team.score),
        ) %>%
        dplyr::arrange(.data$home_away)
      
      player_box_score <- players_df %>%
        dplyr::select(dplyr::any_of(c(
          "game_id",
          "season",
          "season_type",
          "game_date",
          "game_date_time",
          "athlete_id",
          "athlete_display_name",
          "team_id",
          "team_name",
          "team_location",
          "team_short_display_name",
          "minutes",
          "field_goals_made",
          "field_goals_attempted",
          "three_point_field_goals_made",
          "three_point_field_goals_attempted",
          "free_throws_made",
          "free_throws_attempted",
          "offensive_rebounds",
          "defensive_rebounds",
          "rebounds",
          "assists",
          "steals",
          "blocks",
          "turnovers",
          "fouls",
          "plus_minus",
          "points",
          "starter",
          "ejected",
          "did_not_play",
          "reason",
          "active",
          "athlete_jersey",
          "athlete_short_name",
          "athlete_headshot_href",
          "athlete_position_name",
          "athlete_position_abbreviation",
          "team_display_name",
          "team_uid",
          "team_slug",
          "team_logo",
          "team_abbreviation",
          "team_color",
          "team_alternate_color",
          "home_away",
          "team_winner",
          "team_score",
          "opponent_team_id",
          "opponent_team_name",
          "opponent_team_location",
          "opponent_team_display_name",
          "opponent_team_abbreviation",
          "opponent_team_logo",
          "opponent_team_color",
          "opponent_team_alternate_color",
          "opponent_team_score"
        ))) %>% 
        dplyr::mutate_at(c(
          "athlete_id",
          "team_id",
          "team_score",
          "opponent_team_score"
        ), as.integer) %>%
        make_wehoop_data("ESPN WNBA Player Box Information from ESPN.com", Sys.time())
      
      return(player_box_score)
    }
  }
}

Try the wehoop package in your browser

Any scripts or data that you put into this service are public.

wehoop documentation built on Aug. 25, 2026, 1:06 a.m.