BeatLeader_PlayerScores.js

const {beatleader} = require('../api_urls.json').urls
const {parseSearchOptions} = require('./utility/parseSearchOptions')

/**
 * Interact with the BeatLeader Player Scores API endpoints. Get player score related info.
 * @namespace Scores
 * @memberof BeatLeader.Players 
 */

/**
 * Fetches a list of scores for a specific user based on filters
 * @function getById
 * @memberof BeatLeader.Players.Scores
 * @param {string} id ID of user to get data for
 * @param {Object} scoresSearchOptions Filters to narrow search results. See table below.
 * | Parameter           | Type                 | Description                           | Valid Values                                                                                       |
 * |---------------------|----------------------|---------------------------------------|-----------------------------------------------------------------------------------------------------|
 * | sortBy              | ScoresSortBy         | (Optional) Sorting criteria           | `0`, `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8`, `"date"`, `"pp"`, `"acc"`, `"pauses"`, `"rank"`, `"maxStreak"`, `"mistakes"`, `"weight"`, `"weightedPp"` |
 * | order               | Order                | (Optional) Sorting order              | `"asc"`, `"desc"`                                                                                       |
 * | page                | number               | (Optional) Page number                | N/A                                                                                                 |
 * | count               | number               | (Optional) Number of items per page   | N/A                                                                                                 |
 * | search              | string               | (Optional) Search query               | N/A                                                                                                 |
 * | diff                | string               | (Optional) Difficulty level           | `"Easy"`, `"Normal"`, `"Hard"`, `"Expert"`, `"ExpertPlus"`                                                     |
 * | mode                | string               | (Optional) Mode of item               | `"Standard"`, `"OneSaber"`, `"90Degree"`, `"360Degree"`                                                                                                |
 * | requirements        | Requirements         | (Optional) Requirements               | N/A                                                                                                 |
 * | scoreStatus         | ScoreFilterStatus    | (Optional) Score status               | N/A                                                                                                 |
 * | leaderboardContext  | LeaderboardContests  | (Optional) Context of the leaderboard | N/A                                                                                               |
 * | type                | DifficultyStatus     | (Optional) Type of difficulty         | N/A                                                                                                 |
 * | modifiers           | string               | (Optional) Modifiers                  | N/A                                                                                                 |
 * | stars_from          | number               | (Optional) Minimum stars              | N/A                                                                                                 |
 * | stars_to            | number               | (Optional) Maximum stars              | N/A                                                                                                 |
 * | time_from           | number               | (Optional) Start time                 | N/A                                                                                                 |
 * | time_to             | number               | (Optional) End time                   | N/A                                                                                                 |
 * | eventId             | number               | (Optional) Event ID                   | N/A                                                                                                 |                                                                                               |
 * @returns {Promise} Returns a promise with scores
 */
async function getById(id, scoresSearchOptions = {}) {
    try {
        let route = new URL(beatleader + 'player/' + id  + '/scores').href
        route += parseSearchOptions(scoresSearchOptions)

        let res = await fetch(encodeURI(route))
        if(res.status == 400) 
            return Promise.reject(new Error("Invalid request parameters"))
        if(res.status == 404)
            return Promise.reject(new Error("Scores not found for the given player ID"))
        if(res.status != 200)
            return Promise.reject(new Error("Undocumented error: " + res.status))

        return res.json()
    } catch (error) {
        throw new Error(error)
    }
}

/**
 * Retrieves a paginated list of scores for a specific user
 * @function getCompactById
 * @memberof BeatLeader.Players.Scores
 * @param {string} id ID of user to get data for
 * @param {Object} scoresSearchOptions Filters to narrow search results
 * | Parameter           | Type                 | Description                           | Valid Values                                                                                       |
 * |---------------------|----------------------|---------------------------------------|-----------------------------------------------------------------------------------------------------|
 * | sortBy              | ScoresSortBy         | (Optional) Sorting criteria           | `0`, `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8`, `"date"`, `"pp"`, `"acc"`, `"pauses"`, `"rank"`, `"maxStreak"`, `"mistakes"`, `"weight"`, `"weightedPp"` |
 * | order               | Order                | (Optional) Sorting order              | `"asc"`, `"desc"`                                                                                       |
 * | page                | number               | (Optional) Page number                | N/A                                                                                                 |
 * | count               | number               | (Optional) Number of items per page   | N/A                                                                                                 |
 * | search              | string               | (Optional) Search query               | N/A                                                                                                 |
 * | diff                | string               | (Optional) Difficulty level           | `"Easy"`, `"Normal"`, `"Hard"`, `"Expert"`, `"ExpertPlus"`                                                     |
 * | mode                | string               | (Optional) Mode of item               | `"Standard"`, `"OneSaber"`, `"90Degree"`, `"360Degree"`                                                                                                |
 * | requirements        | Requirements         | (Optional) Requirements               | N/A                                                                                                 |
 * | scoreStatus         | ScoreFilterStatus    | (Optional) Score status               | N/A                                                                                                 |
 * | leaderboardContext  | LeaderboardContests  | (Optional) Context of the leaderboard | N/A                                                                                               |
 * | type                | DifficultyStatus     | (Optional) Type of difficulty         | N/A                                                                                                 |
 * | modifiers           | string               | (Optional) Modifiers                  | N/A                                                                                                 |
 * | stars_from          | number               | (Optional) Minimum stars              | N/A                                                                                                 |
 * | stars_to            | number               | (Optional) Maximum stars              | N/A                                                                                                 |
 * | time_from           | number               | (Optional) Start time                 | N/A                                                                                                 |
 * | time_to             | number               | (Optional) End time                   | N/A                                                                                                 |
 * | eventId             | number               | (Optional) Event ID                   | N/A                                                                                                 |                                                                                               |
 * @returns Returns a promise with compacted score data
 */
async function getCompactById(id, scoresSearchOptions = {}) {
    try {
        let route = new URL(beatleader + 'player/' + id  + '/scores/compact').href
        route += parseSearchOptions(scoresSearchOptions)

        let res = await fetch(encodeURI(route))
        if(res.status == 400) 
            return Promise.reject(new Error("Invalid request parameters"))
        if(res.status == 404)
            return Promise.reject(new Error("Scores not found for the given player ID"))
        if(res.status != 200)
            return Promise.reject(new Error("Undocumented error: " + res.status))

        return res.json()
    } catch (error) {
        throw new Error(error)
    }
}

/**
 * Gets a score made my a player on a specific map of a specific difficulty
 * @function getScore
 * @memberof BeatLeader.Players.Scores
 * @param {string} id ID of user to get data for
 * @param {string} hash Map hash
 * @param {string} difficulty Difficuly to get score from
 * @param {string} mode Maps characterists. `"Standard"`, `"OneSaber"`, `"90Degree"`, `"360Degree"`
 * @returns {Promise<number>} Returns a promise with the score
 */
async function getScore(id, hash, difficulty, mode) {
    try {
        let route = new URL(beatleader + 'player/' + id  + '/scorevalue/' + hash + '/' + difficulty + '/' + mode).href

        let res = await fetch(encodeURI(route))
        if(res.status == 400) 
            return Promise.reject(new Error("Invalid request parameters"))
        if(res.status == 404)
            return Promise.reject(new Error("Score not found for the given player ID"))
        if(res.status != 200)
            return Promise.reject(new Error("Undocumented error: " + res.status))

        return res.json()
    } catch (error) {
        throw new Error(error)
    }
}

/**
 * Retrieves accuracy graph data
 * @function getAccGraph
 * @memberof BeatLeader.Players.Scores
 * @param {string} id ID of user to get data for
 * @param {Object} accGraphOptions Filters to narrow search
 * Certainly! Here's the table:

| Parameter           | Type                  | Description                                  | Valid Values                                                      |
|---------------------|-----------------------|----------------------------------------------|-------------------------------------------------------------------|
| type                | string                | (Optional) Type of sorting                   | `"acc"`, `"rank"`, `"weight"`, `"pp"`                             |
| leaderboardContext  | LeaderboardContexts   | (Optional) Context of the leaderboard        | As defined in the LeaderboardContexts type                        |
| no_unranked_stars   | boolean               | (Optional) Whether to include unranked stars | `true` (include unranked stars), `false` (exclude unranked stars) |
 * @returns {Promise} Returns a promise with acc graph data
 */
async function getAccGraph(id, accGraphOptions = {}) {
    try {
        let route = new URL(beatleader + 'player/' + id  + '/accgraph').href
        route += parseSearchOptions(accGraphOptions)

        let res = await fetch(encodeURI(route))
        if(res.status == 400) 
            return Promise.reject(new Error("Invalid request parameters"))
        if(res.status == 404)
            return Promise.reject(new Error("No accuracy graph available for the given player ID"))
        if(res.status != 200)
            return Promise.reject(new Error("Undocumented error: " + res.status))

        return res.json()
    } catch (error) {
        throw new Error(error)
    }
}

/**
 * Retrieves a lkist of players performance metrics and statistics 
 * @function getHistory
 * @memberof BeatLeader.Players.Scores
 * @param {string} id ID of user to get data for
 * @param {string} leaderboardContext Filter scores by leaderboard context, default is 'General'. Available values : `"none"`, `"general"`, `"noMods"`, `"noPause"`, `"golf"`, `"sCPM"`, `"speedrun"`, `"speedrunBackup"`
 * @param {number} count Amount of days to include. Default is 50
 * @returns {Promise} Returns a promise with metrics and statistics
 */
async function getHistory(id, leaderboardContext = "general", count = 50) {
    try {
        let route = new URL(beatleader + 'player/' + id  + '/history').href
        route += `?leaderboardContext=${leaderboardContext}&count=${count}`

        let res = await fetch(encodeURI(route))
        if(res.status == 400) 
            return Promise.reject(new Error("Invalid request parameters"))
        if(res.status == 404)
            return Promise.reject(new Error("No history saved for the given player ID"))
        if(res.status != 200)
            return Promise.reject(new Error("Undocumented error: " + res.status))

        return res.json()
    } catch (error) {
        throw new Error(error)
    }
}

/**
 * 
 * @function getCompactHistory
 * @memberof BeatLeader.Players.Scores
 * @param {string} id ID of user to get data for
 * @param {string} leaderboardContext Filter scores by leaderboard context, default is 'General'. Available values : `"none"`, `"general"`, `"noMods"`, `"noPause"`, `"golf"`, `"sCPM"`, `"speedrun"`, `"speedrunBackup"`
 * @param {number} count Amount of days to include. Default is 50
 * @returns {Promise} Returns a promise with compacted history
 */
async function getCompactHistory(id, leaderboardContext = "general", count = 50) {
    try {
        let route = new URL(beatleader + 'player/' + id  + '/history/compact').href
        route += `?leaderboardContext=${leaderboardContext}&count=${count}`

        let res = await fetch(encodeURI(route))
        if(res.status == 400) 
            return Promise.reject(new Error("Invalid request parameters"))
        if(res.status == 404)
            return Promise.reject(new Error("No history saved for the given player ID"))
        if(res.status != 200)
            return Promise.reject(new Error("Undocumented error: " + res.status))

        return res.json()
    } catch (error) {
        throw new Error(error)
    }
}

/**
 * Retrieves a paginated list of pinned scores for a specific user
 * @function getPinnedScores
 * @memberof BeatLeader.Players.Scores
 * @param {string} id ID of user to get data for
 * @param {string} leaderboardContext Filter scores by leaderboard context, default is 'General'. Available values : `"none"`, `"general"`, `"noMods"`, `"noPause"`, `"golf"`, `"sCPM"`, `"speedrun"`, `"speedrunBackup"`
 * @returns {Promise} Returns a promise with a list of pinned score data
 */
async function getPinnedScores(id, leaderboardContext = "general") {
    try {
        let route = new URL(beatleader + 'player/' + id  + '/pinnedScores').href
        route += `?leaderboardContext=${leaderboardContext}`

        let res = await fetch(encodeURI(route))
        if(res.status == 400) 
            return Promise.reject(new Error("Invalid request parameters"))
        if(res.status == 404)
            return Promise.reject(new Error("Scores not found for the given player ID"))
        if(res.status != 200)
            return Promise.reject(new Error("Undocumented error: " + res.status))

        return res.json()
    } catch (error) {
        throw new Error(error)
    }
}


exports.BeatLeaderPlayerScores = {
    getById: getById,
    getCompactById: getCompactById,
    getScore: getScore,
    getAccGraph: getAccGraph,
    getHistory: getHistory,
    getCompactHistory: getCompactHistory,
    getPinnedScores: getPinnedScores
}