import { log } from "../utils.mjs"

/**
 * Classe pour gérer les résultats du D30 dans Lethal Fantasy
 */
export default class D30Roll {
  /**
   * Table des résultats D30 chargée depuis le fichier JSON
   * @type {Object}
   */
  static resultsTable = null

  /**
   * Définitions des conditions spéciales
   * @type {Object}
   */
  static definitions = null

  /**
   * Types de jets supportés
   * @type {Object}
   */
  static ROLL_TYPES = {
    MELEE_ATTACK: "melee_attack",
    RANGED_ATTACK: "ranged_attack",
    MELEE_DEFENSE: "melee_defense",
    RANGED_DEFENSE: "ranged_defense",
    ARCANE_SPELL_ATTACK: "arcane_spell_attack",
    ARCANE_SPELL_DEFENSE: "arcane_spell_defense",
    SKILL_ROLLS: "skill_rolls"
  }

  /**
   * Initialise la classe en chargeant la table des résultats
   * @returns {Promise<void>}
   */
  static async initialize() {
    try {
      const response = await fetch("systems/fvtt-lethal-fantasy/module/config/d30_results_tables.json")
      const data = await response.json()

      this.resultsTable = data.d30_dice_results
      this.definitions = data.definitions

      log("D30Roll | D30 results table loaded successfully")
    } catch (error) {
      console.error("D30Roll | Error loading D30 table:", error)
      ui.notifications.error("Unable to load D30 results table")
    }
  }

  /**
   * Récupère le résultat d'un jet de D30 sous forme d'objet structuré.
   * @param {number} diceValue La valeur du dé (1-30)
   * @param {string} rollType Le type de jet externe (ex: "weapon-attack", "spell-attack", etc.)
   * @param {Object} weapon L'arme ou l'objet utilisé (optionnel, nécessaire pour certains types)
   * @param {Object} options Options supplémentaires (optionnel)
   * @param {boolean} options.isRanged Si true, utilise ranged_defense au lieu de melee_defense
   * @returns {Object|null} L'objet effet `{ type, description, ...fields }` ou null si aucun effet
   */
  static getResult(diceValue, rollType, weapon = null, options = {}) {
    if (!this.resultsTable) {
      console.warn("D30Roll | Results table is not initialized. Call D30Roll.initialize() first.")
      return null
    }

    if (diceValue < 1 || diceValue > 30) {
      console.warn(`D30Roll | Invalid dice value: ${diceValue}. Must be between 1 and 30.`)
      return null
    }

    const internalType = this.convertToInternalType(rollType, weapon, options)

    if (!internalType) {
      console.warn(`D30Roll | Could not convert roll type: ${rollType}`)
      return null
    }

    if (!Object.values(this.ROLL_TYPES).includes(internalType)) {
      console.warn(`D30Roll | Invalid internal roll type: ${internalType}`)
      return null
    }

    const resultEntry = this.resultsTable[diceValue]
    if (!resultEntry) {
      console.warn(`D30Roll | No entry found for value ${diceValue}`)
      return null
    }

    const result = resultEntry[internalType]
    return result ?? null
  }

  /**
   * Retourne le type d'effet d'un résultat D30.
   * @param {Object|null} result L'objet retourné par getResult()
   * @returns {string|null} Le type d'effet ou null
   */
  static getEffectType(result) {
    return result?.type ?? null
  }

  /**
   * Convertit un rollType externe en rollType interne
   * @param {string} externalType Le type de jet externe (ex: "weapon-attack")
   * @param {Object} weapon L'arme ou l'objet utilisé (optionnel)
   * @param {Object} options Options supplémentaires (optionnel)
   * @param {boolean} options.isRanged Si true, utilise ranged_defense au lieu de melee_defense
   * @returns {string|null} Le type interne correspondant ou null
   */
  static convertToInternalType(externalType, weapon = null, options = {}) {
    // Attack types - need weapon to determine if melee or ranged
    if (externalType === "weapon-attack") {
      if (!weapon) {
        console.warn("D30Roll | Weapon object required for weapon-attack type")
        // Fall through to use options.isRanged if available, otherwise default melee
      }
      return (options.isRanged || weapon?.system?.weaponType === "ranged")
        ? this.ROLL_TYPES.RANGED_ATTACK
        : this.ROLL_TYPES.MELEE_ATTACK
    }

    // Monster attacks - check options.isRanged (set from rollTarget.attackMode) or weapon type
    if (externalType === "monster-attack") {
      if (options.isRanged || weapon?.system?.weaponType === "ranged") {
        return this.ROLL_TYPES.RANGED_ATTACK
      }
      return this.ROLL_TYPES.MELEE_ATTACK
    }

    // Defense types
    if (externalType === "weapon-defense" || externalType === "monster-defense") {
      return options.isRanged ? this.ROLL_TYPES.RANGED_DEFENSE : this.ROLL_TYPES.MELEE_DEFENSE
    }

    // Spell/Miracle types
    if (externalType === "spell-attack" || externalType === "spell" || externalType === "spell-power"
      || externalType === "miracle-attack" || externalType === "miracle" || externalType === "miracle-power") {
      return this.ROLL_TYPES.ARCANE_SPELL_ATTACK
    }

    // Skill types
    if (externalType === "skill" || externalType === "monster-skill" || externalType === "challenge") {
      return this.ROLL_TYPES.SKILL_ROLLS
    }

    // Saving throw types
    if (externalType === "save") {
      return this.ROLL_TYPES.ARCANE_SPELL_DEFENSE
    }

    // If no match, return null
    console.warn(`D30Roll | Unknown external roll type: ${externalType}`)
    return null
  }

  /**
   * Récupère toutes les informations pour une valeur de dé donnée
   * @param {number} diceValue La valeur du dé (1-30)
   * @returns {Object|null} Tous les résultats pour cette valeur ou null
   */
  static getAllResultsForValue(diceValue) {
    if (!this.resultsTable) {
      console.warn("D30Roll | Results table is not initialized.")
      return null
    }

    if (diceValue < 1 || diceValue > 30) {
      console.warn(`D30Roll | Invalid dice value: ${diceValue}`)
      return null
    }

    return this.resultsTable[diceValue]
  }

  /**
   * Récupère la définition d'une condition spéciale
   * @param {string} definitionKey La clé de la définition (ex: "flash_of_pain")
   * @returns {string|null} La définition ou null
   */
  static getDefinition(definitionKey) {
    if (!this.definitions) {
      console.warn("D30Roll | Definitions are not initialized.")
      return null
    }

    return this.definitions[definitionKey] || null
  }

  /**
   * Vérifie si un résultat est vide
   * @param {Object|null} result Le résultat à vérifier
   * @returns {boolean} True si le résultat est vide
   */
  static isEmptyResult(result) {
    return !result || !result.type
  }

  /**
   * Récupère un résultat formaté pour l'affichage
   * @param {number} diceValue La valeur du dé (1-30)
   * @param {string} rollType Le type de jet externe
   * @param {Object} weapon L'arme ou l'objet utilisé (optionnel)
   * @param {Object} options Options supplémentaires (optionnel)
   * @returns {Object} Un objet avec le résultat et des informations de formatage
   */
  static getFormattedResult(diceValue, rollType, weapon = null, options = {}) {
    const result = this.getResult(diceValue, rollType, weapon, options)
    const internalType = this.convertToInternalType(rollType, weapon, options)

    return {
      value: diceValue,
      rollType: rollType,
      internalType: internalType,
      result: result,
      isEmpty: this.isEmptyResult(result),
      hasResult: !this.isEmptyResult(result)
    }
  }

  /**
   * Vérifie si la table est chargée
   * @returns {boolean} True si la table est chargée
   */
  static isInitialized() {
    return this.resultsTable !== null && this.definitions !== null
  }
}
