Utils_ValRnkAchv.js

'user strict'

const { ErrorCodes } = require('../Errors/ErrorCodes')
const { capitalize } = require('./Capitalize')
const { RankBuilder } = require('../Structures/RankBuilder')
const { AchievementType } = require('../Interfaces')
const { AchievementBuilder } = require('../Structures/AchievementBuilder')
const { isNotPositiveInteger } = require('./ValidateInteger')
const { MissingValue, InvalidValue, AchievementInvalidProgress } = require('../Errors/LME')

/** @typedef {import('../../typings').Rank} Rank */
/** @typedef {import('../../typings').Achievement} Achievement */

/**
 * Validates RankBuilder/Rank and AchievementBuilder/Achievement instances and objects.
 */
class ValRnkAchv {
  /**
   * Validate a AchievementBuilder/Achievement instance or object.
   * @param {AchievementBuilder|Achievement} validate - The AchievementBuilder/Achievement instance or object to validate.
   * @returns {Boolean} - Returns true if the AchievementBuilder/Achievement instance or object is valid.
   *
   * @throws {MissingValue} - If the validate argument is missing.
   * @throws {InvalidValue} - If the validate argument is not a AchievementBuilder instance or an object.
   */
  static validateAchievement (validate) {
    if (!validate) throw new MissingValue(ErrorCodes.MissingArgument, 'validate')

    if (!(validate instanceof AchievementBuilder) && typeof validate !== 'object') throw new InvalidValue(ErrorCodes.InvalidValue, 'validate', 'a or AchievementBuilder instance or an object.')

    /** @type {import('../../typings').Achievement} */
    const data = validate instanceof AchievementBuilder ? validate.toJSON() : validate

    const achievementProperties = ['name', 'description', 'reward', 'type', 'progress']

    for (const key of achievementProperties) data[key] ??= undefined

    // We loop through the data object and call the corresponding setter method.
    for (const [key, value] of Object.entries(data)) this[`set${capitalize(key)}`](value)

    return true
  }

  /**
   * Validate a RankBuilder/Rank instance or object.
   * @param {RankBuilder|Rank} validate - The RankBuilder/Rank instance or object to validate.
   * @returns {Boolean} - Returns true if the RankBuilder/Rank instance or object is valid.
   *
   * @throws {MissingValue} - If the validate argument is missing.
   * @throws {InvalidValue} - If the validate argument is not a RankBuilder instance or an object.
   */
  static validateRank (validate) {
    if (!validate) throw new MissingValue(ErrorCodes.MissingArgument, 'validate')

    if (!(validate instanceof RankBuilder) && typeof validate !== 'object') throw new InvalidValue(ErrorCodes.InvalidValue, 'validate', 'a RankBuilder instance or an object.')

    /** @type {import('../../typings').Rank} */
    let data

    // Depending on the type of the validate argument, we do different things.
    if (validate instanceof RankBuilder) data = validate.toJSON()
    else data = validate

    const rankProperties = ['nameplate', 'color', 'priority', 'max', 'min']

    for (const key of rankProperties) data[key] ??= undefined

    // We loop through the data object and call the corresponding setter method.
    for (const [key, value] of Object.entries(data)) this[`set${capitalize(key)}`](value)

    return true
  }

  static setNameplate (nameplate) {
    if (!nameplate) throw new MissingValue(ErrorCodes.MissingProperty, 'nameplate')

    if (typeof nameplate !== 'string') throw new InvalidValue(ErrorCodes.InvalidValue, 'nameplate', 'a string')

    return true
  }

  static setColor (color) {
    if (!color) throw new MissingValue(ErrorCodes.MissingProperty, 'color')

    if (typeof color !== 'string') throw new InvalidValue(ErrorCodes.InvalidValue, 'color', 'a string')

    return true
  }

  static setPriority (priority) {
    if (!priority) throw new MissingValue(ErrorCodes.MissingProperty, 'priority')

    if (isNotPositiveInteger(priority)) throw new InvalidValue(ErrorCodes.InvalidValue, 'priority', 'a positive integer')

    return true
  }

  static setMax (max) {
    if (!max) throw new MissingValue(ErrorCodes.MissingProperty, 'max')

    if (isNotPositiveInteger(max)) throw new InvalidValue(ErrorCodes.InvalidValue, 'max', 'a positive integer')

    return true
  }

  static setMin (min) {
    if (!min) throw new MissingValue(ErrorCodes.MissingProperty, 'min')

    if (isNotPositiveInteger(min)) throw new InvalidValue(ErrorCodes.InvalidValue, 'min', 'a positive integer')

    return true
  }

  static setName (name) {
    if (!name) throw new MissingValue(ErrorCodes.MissingProperty, 'name')

    if (typeof name !== 'string') throw new InvalidValue(ErrorCodes.InvalidValue, 'name', 'a string')

    return true
  }

  static setDescription (description) {
    if (!description) throw new MissingValue(ErrorCodes.MissingProperty, 'description')

    if (typeof description !== 'string') throw new InvalidValue(ErrorCodes.InvalidValue, 'description', 'a string')

    return true
  }

  static setReward (reward) {
    if (!reward) throw new MissingValue(ErrorCodes.MissingProperty, 'reward')

    if (isNotPositiveInteger(reward)) throw new InvalidValue(ErrorCodes.InvalidValue, 'reward', 'a positive integer')

    return true
  }

  static setType (type) {
    if (!type) throw new MissingValue(ErrorCodes.MissingProperty, 'type')

    if (!Object.values(AchievementType).includes(type)) throw new InvalidValue(ErrorCodes.InvalidValue, 'type', 'a valid achievement type.')

    return true
  }

  static setThumbnail (thumbnail) {
    if (!thumbnail) return undefined

    if (thumbnail && typeof thumbnail !== 'string') throw new InvalidValue(ErrorCodes.InvalidValue, 'thumbnail', 'a string')

    return true
  }

  static setProgress (progress) {
    if (!progress) throw new MissingValue(ErrorCodes.MissingProperty, 'progress')

    if (!Array.isArray(progress) || progress.length !== 2) throw new AchievementInvalidProgress(ErrorCodes.AchievementInvalidProgress)

    if (progress.some(v => typeof v !== 'number' || v < 0 || isNaN(v) || !isFinite(v) || v % 1 !== 0)) throw new AchievementInvalidProgress(ErrorCodes.InvalidValue, 'progress', 'an array of two positive integers.')

    if (progress[0] > progress[1]) throw new AchievementInvalidProgress(ErrorCodes.AchievementGraterProgress)

    return true
  }
}

module.exports = { ValRnkAchv }