/**
 * Règles d'apprentissage (phase d'Expérience).
 * Helpers purs de difficultés, handicaps et calculs de jets d'Expérience.
 */

export class VermineExperience {

  /**
   * Difficulté d'un jet d'apprentissage de Compétence selon le niveau visé.
   * @param {number} targetLevel niveau visé (1-5)
   * @returns {number}
   */
  static skillLearningDifficulty(targetLevel) {
    switch (parseInt(targetLevel, 10)) {
      case 1: return 5  // Débutant
      case 2: return 7  // Confirmé
      case 3: return 9  // Expert
      case 4: return 10 // Maître
      case 5: return 10 // Légende
      default: return 7
    }
  }

  /**
   * Handicap d'un jet d'apprentissage de Compétence.
   * Rareté (1-3) + handicap (I) supplémentaire pour le niveau Légende.
   * @param {number} targetLevel niveau visé (1-5)
   * @param {number} rarity rareté de la compétence (0-3)
   * @returns {number}
   */
  static skillLearningHandicap(targetLevel, rarity) {
    let handicap = parseInt(rarity, 10) || 0
    if (parseInt(targetLevel, 10) === 5) handicap += 1 // Légende : (I)
    return handicap
  }

  /**
   * Difficulté d'un jet d'apprentissage de Spécialité selon le niveau actuel
   * de la compétence associée. Domaine de prédilection : -2.
   * @param {number} skillLevel niveau actuel de la compétence (2-5)
   * @param {boolean} isDomain la compétence est dans le domaine de prédilection
   * @returns {number}
   */
  static specialtyLearningDifficulty(skillLevel, isDomain) {
    let difficulty
    switch (parseInt(skillLevel, 10)) {
      case 2: difficulty = 9  // Confirmé
        break
      case 3: difficulty = 7  // Expert
        break
      case 4: difficulty = 5  // Maître
        break
      case 5: difficulty = 5  // Légende (relance automatique)
        break
      default: difficulty = 9
    }
    return isDomain ? difficulty - 2 : difficulty
  }

  /**
   * Handicap d'un jet d'apprentissage de Spécialité : nombre de spécialités
   * déjà possédées (requiert au moins Confirmé dans la compétence associée).
   * @param {number} specialtyCount
   * @returns {number}
   */
  static specialtyLearningHandicap(specialtyCount) {
    return Math.max(0, parseInt(specialtyCount, 10) || 0)
  }

  /**
   * Difficulté d'un jet d'apprentissage de Caractéristique.
   * @returns {number}
   */
  static characteristicLearningDifficulty() {
    return 9
  }

  /**
   * Difficulté d'un jet d'apprentissage de Réserve : valeur actuelle.
   * @param {number} currentValue
   * @returns {number}
   */
  static reserveLearningDifficulty(currentValue) {
    return Math.max(0, parseInt(currentValue, 10) || 0)
  }

  /**
   * Handicap d'âge pour l'apprentissage des Réserves et autres jets liés à l'âge.
   * @param {number} ageType 1=Jeune, 2=Adulte, 3=Ancien
   * @returns {number}
   */
  static ageHandicap(ageType) {
    switch (parseInt(ageType, 10)) {
      case 1: return 1 // Jeune (I)
      case 3: return 3 // Ancien (III)
      default: return 2 // Adulte (II)
    }
  }

  /**
   * Difficulté d'un jet d'acquisition d'un Dé d'Évolution selon l'âge.
   * @param {number} ageType 1=Jeune, 2=Adulte, 3=Ancien
   * @returns {number}
   */
  static evolutionDifficulty(ageType) {
    switch (parseInt(ageType, 10)) {
      case 1: return 5
      case 3: return 9
      default: return 7
    }
  }

  /**
   * Bonus/Malus de Totem sur le jet de Dé d'Évolution.
   * Plus de Dés Adaptés que Humains : +1D. Inversement : -1D.
   * @param {number} human human totem dice
   * @param {number} adapted adapted totem dice
   * @returns {number}
   */
  static evolutionTotemModifier(human, adapted) {
    if (adapted > human) return 1
    if (human > adapted) return -1
    return 0
  }

  /**
   * Un Dé de Totem Adapté peut remplacer un Dé d'Expérience (compte ×2)
   * uniquement si le personnage possède strictement plus de Dés Adaptés
   * que Humains.
   * @param {number} human human totem dice
   * @param {number} adapted adapted totem dice
   * @returns {boolean}
   */
  static canUseAdaptedDie(human, adapted) {
    return adapted > human
  }

  /**
   * Difficulté d'un jet d'apprentissage de Capacité de Totem : valeur
   * d'Apprentissage de la capacité.
   * @param {object} abilityData system data de l'item ability
   * @returns {number}
   */
  static totemAbilityLearningDifficulty(abilityData) {
    return parseInt(abilityData?.learn?.threshold, 10) || 5
  }

  /**
   * Handicap d'un jet d'apprentissage de Capacité de Totem : valeur
   * d'Apprentissage de la capacité.
   * @param {object} abilityData system data de l'item ability
   * @returns {number}
   */
  static totemAbilityLearningHandicap(abilityData) {
    return parseInt(abilityData?.learn?.hindrance, 10) || 0
  }

  /**
   * Difficulté d'un jet d'obtention d'un Élément d'Historique.
   * @returns {number}
   */
  static backgroundLearningDifficulty() {
    return 9
  }

  /**
   * Handicap d'un jet d'obtention d'un Élément d'Historique : (I) si
   * l'Historique est listé avec un astérisque.
   * @param {boolean} asterisk
   * @returns {number}
   */
  static backgroundLearningHandicap(asterisk) {
    return asterisk ? 1 : 0
  }

  /**
   * Difficulté d'un jet visant à réduire le score de Réputation (devenir
   * plus célèbre) : 10 moins le score que le personnage souhaite atteindre.
   * @param {number} current réputation actuelle
   * @returns {number}
   */
  static reputationReduceDifficulty(current) {
    return 10 - Math.max(0, (parseInt(current, 10) || 0) - 1)
  }

  /**
   * Difficulté d'un jet visant à augmenter le score de Réputation (se faire
   * oublier) : égale à la Réputation actuelle.
   * @param {number} current réputation actuelle
   * @returns {number}
   */
  static reputationIncreaseDifficulty(current) {
    return parseInt(current, 10) || 0
  }

  /**
   * Clé i18n du libellé du niveau de compétence visé.
   * @param {number} level
   * @returns {string}
   */
  static skillLevelLabelKey(level) {
    return CONFIG.VERMINE.SkillLevels?.[level]?.label ?? "SKILL_LEVELS.incompetent"
  }

  /**
   * Détermine si une compétence appartient au domaine de prédilection.
   * @param {Actor} actor
   * @param {string} skillKey
   * @returns {boolean}
   */
  static isPreferredDomain(actor, skillKey) {
    const category = actor.system?.skills?.[skillKey]?.category
    const preferred = actor.system?.skill_categories?.preferred
    return Boolean(category && preferred && category === preferred)
  }

  /**
   * Réinitialise les limites d'apprentissage (learned.*) de la phase
   * courante, pour un personnage ou un Groupe, sans toucher au compteur
   * de phase (avancé uniquement par le dialogue d'attribution du MJ).
   * @param {foundry.abstract.Document} actor personnage ou Groupe
   * @returns {Promise<void>}
   */
  static async resetPhaseLimits(actor) {
    const ex = actor.system?.experience
    if (!ex) return
    const update = {}
    if (ex.learned && typeof ex.learned === "object") {
      for (const key of Object.keys(ex.learned)) {
        if (key === "phase") continue
        update[`system.experience.learned.${key}`] = false
      }
    }
    if (Object.keys(update).length) await actor.update(update)
  }

  /**
   * Mode de jeu actif (1=Survie, 2=Cauchemar, 3=Apocalypse).
   * @returns {number}
   */
  static gameMode() {
    return parseInt(game.settings.get("vermine2047", "game-mode"), 10) || 1
  }

  /**
   * Total de Dés d'Évolution maximum selon le Mode de jeu.
   * @param {number} mode 1=Survie, 2=Cauchemar, 3=Apocalypse
   * @returns {number}
   */
  static evolutionMaxForMode(mode) {
    switch (parseInt(mode, 10)) {
      case 2: return 6
      case 3: return 7
      default: return 5
    }
  }

  /**
   * Somme des niveaux des Adaptations/Mutations déjà possédées.
   * @param {foundry.abstract.Document} actor personnage
   * @returns {number}
   */
  static evolutionTotalLevels(actor) {
    return (actor?.itemTypes?.evolution || []).reduce((sum, item) => sum + (item.system?.level?.value || 0), 0)
  }

  /**
   * Valide l'achat d'une Adaptation/Mutation contre les Dés d'Évolution
   * accumulés. Retourne une clé i18n d'erreur, ou null si l'achat est permis.
   * Règles p. 116-117 : coût = niveau ; total limité par le Mode ; Mutations
   * 3D/4D interdites en Mode Survie ; une seule Mutation à 4D.
   * @param {foundry.abstract.Document} actor personnage
   * @param {number} cost niveau de l'Adaptation/Mutation (1-4)
   * @param {boolean} mutation vrai si c'est une Mutation
   * @returns {string|null}
   */
  static validateEvolutionPurchase(actor, cost, mutation) {
    const level = parseInt(cost, 10) || 0
    const accumulated = actor.system?.experience?.evolution || 0
    const mode = this.gameMode()
    if (accumulated < level) return "VERMINE.error_evolution_not_enough_dice"
    if (level < 1 || level > 4) return "VERMINE.error_evolution_invalid_level"
    const total = this.evolutionTotalLevels(actor) + level
    if (total > this.evolutionMaxForMode(mode)) return "VERMINE.error_evolution_mode_limit"
    if (mutation && level >= 3 && mode < 2) return "VERMINE.error_evolution_mutation_mode"
    if (mutation && level === 4) {
      const hasFourDMutation = (actor?.itemTypes?.evolution || []).some(
        (item) => item.system?.mutation && (item.system?.level?.value || 0) === 4
      )
      if (hasFourDMutation) return "VERMINE.error_evolution_mutation_4d"
    }
    return null
  }
}
