import { SYSTEM } from "../config/system.mjs"
import LethalFantasyUtils from "../utils.mjs"

export default class LethalFantasyRoll extends Roll {
  /**
   * The HTML template path used to render dice checks of this type
   * @type {string}
   */
  static CHAT_TEMPLATE = "systems/fvtt-lethal-fantasy/templates/chat-message.hbs"

  get type() {
    return this.options.type
  }

  get titleFormula() {
    return this.options.titleFormula
  }

  get rollName() {
    return this.options.rollName
  }

  get target() {
    return this.options.target
  }

  get value() {
    return this.options.value
  }

  get treshold() {
    return this.options.treshold
  }

  get actorId() {
    return this.options.actorId
  }

  get actorName() {
    return this.options.actorName
  }

  get actorImage() {
    return this.options.actorImage
  }

  get modifier() {
    return this.options.modifier
  }

  get resultType() {
    return this.options.resultType
  }

  get isFailure() {
    return this.resultType === "failure"
  }

  get hasTarget() {
    return this.options.hasTarget
  }

  get targetName() {
    return this.options.targetName
  }

  get targetArmor() {
    return this.options.targetArmor
  }

  get targetMalus() {
    return this.options.targetMalus
  }

  get realDamage() {
    return this.options.realDamage
  }

  get rollTotal() {
    return this.options.rollTotal
  }

  get diceResults() {
    return this.options.diceResults
  }

  get rollTarget() {
    return this.options.rollTarget
  }

  get D30result() {
    return this.options.D30result
  }

  get badResult() {
    return this.options.badResult
  }

  get rollData() {
    return this.options.rollData
  }

  /**
   * Prompt the user with a dialog to configure and execute a roll.
   *
   * @param {Object} options Configuration options for the roll.
   * @param {string} options.rollType The type of roll being performed (e.g., RESOURCE, DAMAGE, ATTACK, SAVE).
   * @param {string} options.rollValue The initial value or formula for the roll.
   * @param {string} options.rollTarget The target of the roll.
   * @param {"="|"+"|"++"|"-"|"--"} options.rollAdvantage If there is an avantage (+), a disadvantage (-), a double advantage (++), a double disadvantage (--) or a normal roll (=).
   * @param {string} options.actorId The ID of the actor performing the roll.
   * @param {string} options.actorName The name of the actor performing the roll.
   * @param {string} options.actorImage The image of the actor performing the roll.
   * @param {boolean} options.hasTarget Whether the roll has a target.
   * @param {Object} options.target The target of the roll, if any.
   * @param {Object} options.data Additional data for the roll.
   *
   * @returns {Promise<Object|null>} The roll result or null if the dialog was cancelled.
   */
  static async prompt(options = {}) {
    let dice = "1D20"
    let maxValue = 20
    let baseFormula = "1D20"
    let modifierFormula = "1D0"
    let hasModifier = true
    let hasChangeDice = false
    let hasD30 = false
    let hasFavor = false
    let hasMaxValue = true
    let hasGrantedDice = false
    let pointBlank = false
    let letItFly = false
    let saveSpell = false
    let beyondSkill = false
    let hasStaticModifier = false
    let hasExplode = true
    let actor = game.actors.get(options.actorId)

    if (options.rollType === "challenge" || options.rollType === "save") {
      options.rollName = game.i18n.localize(`LETHALFANTASY.Label.${options.rollTarget.rollKey}`)
      hasD30 = options.rollType === "save"
      if (options.rollTarget.rollKey === "dying") {
        dice = options.rollTarget.value
        hasModifier = false
        hasChangeDice = true
        hasFavor = true
      } else {
        dice = "1D20"
        hasFavor = true
      }

    } else if (options.rollType === "granted") {
      hasD30 = false
      options.rollName = `Granted ${options.rollTarget.rollKey}`
      dice = options.rollTarget.formula
      baseFormula = options.rollTarget.formula
      hasModifier = false
      hasMaxValue = false
      hasChangeDice = false
      hasFavor = false

    } else if (options.rollType === "monster-attack" || options.rollType === "monster-defense") {
      hasD30 = true
      options.rollName = options.rollTarget.name
      dice = "1D20"
      baseFormula = "D20"
      hasModifier = true
      hasChangeDice = false
      hasFavor = true
      if (options.rollType === "monster-attack") {
        options.rollTarget.value = options.rollTarget.attackModifier
        options.rollTarget.charModifier = 0
      } else {
        options.rollTarget.value = options.rollTarget.defenseModifier
        options.rollTarget.charModifier = 0
      }

    } else if (options.rollType === "monster-skill") {
      options.rollName = game.i18n.localize(`LETHALFANTASY.Label.${options.rollTarget.rollKey}`)
      dice = "1D20"
      baseFormula = "D20"
      hasModifier = true
      hasFavor = true
      hasChangeDice = false

    } else if (options.rollType === "skill") {
      options.rollName = options.rollTarget.name
      hasD30 = true
      dice = "1D20"
      baseFormula = "D20"
      hasModifier = true
      hasFavor = true
      hasChangeDice = false
      options.rollTarget.value = Math.floor(options.rollTarget.system.skillTotal / 10)

    } else if (options.rollType === "weapon-attack" || options.rollType === "weapon-defense") {
      hasD30 = true
      options.rollName = options.rollTarget.name
      dice = "1D20"
      baseFormula = "D20"
      hasModifier = true
      hasChangeDice = false
      hasFavor = true
      if (options.rollType === "weapon-attack") {
        if (options.rollTarget.weapon.system.weaponType === "melee") {
          options.rollTarget.value = options.rollTarget.combat.attackModifier + options.rollTarget.weaponSkillModifier + options.rollTarget.weapon.system.bonuses.attackBonus
          options.rollTarget.charModifier = options.rollTarget.combat.attackModifier
        } else {
          options.rollTarget.value = options.rollTarget.combat.rangedAttackModifier + options.rollTarget.weaponSkillModifier + options.rollTarget.weapon.system.bonuses.attackBonus
          options.rollTarget.charModifier = options.rollTarget.combat.rangedAttackModifier
        }
      } else {
        options.rollTarget.value = options.rollTarget.combat.defenseModifier + options.rollTarget.weaponSkillModifier + options.rollTarget.weapon.system.bonuses.defenseBonus + options.rollTarget.armorDefense
        options.rollTarget.charModifier = options.rollTarget.combat.defenseModifier
      }

    } else if (options.rollType === "spell" || options.rollType === "spell-attack" || options.rollType === "spell-power") {
      hasD30 = true
      options.rollName = options.rollTarget.name
      dice = "1D20"
      baseFormula = "D20"
      hasModifier = true
      hasChangeDice = false
      options.rollTarget.value = options.rollTarget.actorModifiers.levelSpellModifier + options.rollTarget.actorModifiers.intSpellModifier
      options.rollTarget.charModifier = options.rollTarget.actorModifiers.intSpellModifier
      hasStaticModifier = options.rollType === "spell-power"
      //hasModifier = options.rollType !== "spell-attack"
      if (hasStaticModifier) {
        options.rollTarget.staticModifier = options.rollTarget.actorLevel
      } else {
        options.rollTarget.staticModifier = 0
      }

    } else if (options.rollType === "miracle" || options.rollType === "miracle-attack" || options.rollType === "miracle-power") {
      hasD30 = true
      options.rollName = options.rollTarget.name
      dice = "1D20"
      baseFormula = "D20"
      hasChangeDice = false
      options.rollTarget.value = options.rollTarget.actorModifiers.levelMiracleModifier + options.rollTarget.actorModifiers.chaMiracleModifier
      options.rollTarget.charModifier = options.rollTarget.actorModifiers.chaMiracleModifier
      hasStaticModifier = options.rollType === "miracle-power"
      //hasModifier = options.rollType !== "miracle-attack"
      if (hasStaticModifier) {
        options.rollTarget.staticModifier = options.rollTarget.actorLevel
      } else {
        options.rollTarget.staticModifier = 0
      }

    } else if (options.rollType === "shield-roll") {
      hasD30 = false
      options.rollName = "Shield Defense"
      dice = options.rollTarget.system.defense.toUpperCase()
      baseFormula = dice
      hasModifier = true
      hasChangeDice = false
      hasMaxValue = false
      hasExplode = false
      options.rollTarget.value = 0

    } else if (options.rollType.includes("weapon-damage")) {
      options.rollName = options.rollTarget.name
      options.isDamage = true
      hasModifier = true
      hasChangeDice = false
      let damageBonus = (options.rollTarget.weapon.system.applyStrengthDamageBonus) ? options.rollTarget.combat.damageModifier : 0
      options.rollTarget.value = damageBonus + options.rollTarget.weaponSkillModifier + options.rollTarget.weapon.system.bonuses.damageBonus
      options.rollTarget.charModifier = damageBonus
      if (options.rollType.includes("small")) {
        options.damageSmall = true
        dice = options.rollTarget.weapon.system.damage.damageS
      } else {
        options.damageMedium = true
        dice = options.rollTarget.weapon.system.damage.damageM
      }
      dice = dice.replace("E", "")
      baseFormula = dice

    } else if (options.rollType.includes("monster-damage")) {
      options.rollName = options.rollTarget.name
      options.isDamage = true
      hasModifier = true
      hasChangeDice = false
      options.rollTarget.value = options.rollTarget.damageModifier
      options.rollTarget.charModifier = 0
      dice = options.rollTarget.damageDice
      dice = dice.replace("E", "")
      baseFormula = dice
    }


    if (options.rollType === "save" && (options.rollTarget.rollKey === "pain" || options.rollTarget.rollKey === "paincourage")) {
      dice = options.rollTarget.rollDice
      baseFormula = options.rollTarget.rollDice
      hasModifier = false
    }

    const rollModes = foundry.utils.duplicate(CONFIG.Dice.rollModes); // v12 : Object.fromEntries(Object.entries(CONFIG.Dice.rollModes).map(([key, value]) => [key, game.i18n.localize(value)]))
    console.log("Roll mode", rollModes)

    const fieldRollMode = new foundry.data.fields.StringField({
      choices: rollModes,
      blank: false,
      default: "public",
    })

    const choiceModifier = SYSTEM.CHOICE_MODIFIERS
    const choiceDice = SYSTEM.CHOICE_DICE
    const choiceFavor = SYSTEM.FAVOR_CHOICES

    let modifier = "+0"
    let targetName

    let dialogContext = {
      rollType: options.rollType,
      rollTarget: options.rollTarget,
      rollName: options.rollName,
      actorName: options.actorName,
      rollModes,
      hasModifier,
      hasFavor,
      hasChangeDice,
      pointBlank,
      baseValue: options.rollTarget.value,
      attackerAimChoices: SYSTEM.ATTACKER_AIM_CHOICES,
      attackerAim: "0",
      changeDice: `${dice}`,
      fieldRollMode,
      choiceModifier,
      choiceDice,
      choiceFavor,
      baseFormula,
      dice,
      hasTarget: options.hasTarget,
      modifier,
      saveSpell: false,
      favor: "none",
      targetName
    }
    const content = await foundry.applications.handlebars.renderTemplate("systems/fvtt-lethal-fantasy/templates/roll-dialog.hbs", dialogContext)

    let position = game.user.getFlag(SYSTEM.id, "roll-dialog-pos") || { top: -1, left: -1 }
    const label = game.i18n.localize("LETHALFANTASY.Roll.roll")
    const rollContext = await foundry.applications.api.DialogV2.wait({
      window: { title: "Roll dialog" },
      classes: ["lethalfantasy"],
      content,
      position,
      buttons: [
        {
          label: label,
          callback: (event, button, dialog) => {
            console.log("Roll context", event, button, dialog)
            let position = dialog.position
            game.user.setFlag(SYSTEM.id, "roll-dialog-pos", foundry.utils.duplicate(position))
            const output = Array.from(button.form.elements).reduce((obj, input) => {
              if (input.name) obj[input.name] = input.value
              return obj
            }, {})
            return output
          },
        },
      ],
      actions: {
        "selectGranted": (event, button, dialog) => {
          hasGrantedDice = event.target.checked
        },
        "selectBeyondSkill": (event, button, dialog) => {
          beyondSkill = button.checked
        },
        "selectPointBlank": (event, button, dialog) => {
          pointBlank = button.checked
        },
        "selectLetItFly": (event, button, dialog) => {
          letItFly = button.checked
        },
        "saveSpellCheck": (event, button, dialog) => {
          saveSpell = button.checked
        },
        "gotoToken": (event, button, dialog) => {
          let tokenId = $(button).data("tokenId")
          let token = canvas.tokens?.get(tokenId)
          if (token) {
            canvas.animatePan({ x: token.x, y: token.y, duration: 200 })
            canvas.tokens.releaseAll();
            token.control({ releaseOthers: true });
          }
        }
      },
      rejectClose: false // Click on Close button will not launch an error
    })

    // If the user cancels the dialog, exit
    if (rollContext === null) return
    console.log("rollContext", rollContext, hasGrantedDice)
    rollContext.saveSpell = saveSpell // Update fucking flag

    let fullModifier = 0
    let titleFormula = ""
    dice = rollContext.changeDice || dice
    if (hasModifier) {
      let bonus = Number(options.rollTarget.value)
      fullModifier = rollContext.modifier === "" ? 0 : parseInt(rollContext.modifier, 10) + bonus
      fullModifier += (rollContext.saveSpell) ? options.rollTarget.actorModifiers.saveModifier : 0
      if (Number(rollContext.attackerAim) > 0) {
        fullModifier += Number(rollContext.attackerAim)
      }

      if (fullModifier === 0) {
        modifierFormula = "0"
      } else {
        let modAbs = Math.abs(fullModifier)
        modifierFormula = `D${modAbs + 1} - 1`
      }
      if (hasStaticModifier) {
        modifierFormula += ` + ${options.rollTarget.staticModifier}`
      }
      let sign = fullModifier < 0 ? "-" : "+"
      if (hasExplode) {
        titleFormula = `${dice}E ${sign} ${modifierFormula}`
      } else {
        titleFormula = `${dice} ${sign} ${modifierFormula}`
      }
    } else {
      modifierFormula = "0"
      fullModifier = 0
      baseFormula = `${dice}`
      if (hasExplode) {
        titleFormula = `${dice}E`
      } else {
        titleFormula = `${dice}`
      }
    }

    // Latest addition : favor choice at point blank range
    if (pointBlank) {
      rollContext.favor = "favor"
    }
    if (beyondSkill) {
      rollContext.favor = "disfavor"
    }

    // Specific pain case
    if (options.rollType === "save" && options.rollTarget.rollKey === "pain" || options.rollTarget.rollKey === "paincourage") {
      baseFormula = options.rollTarget.rollDice
      titleFormula = `${dice}`
      modifierFormula = "0"
      fullModifier = 0
    }

    // Specific pain/poison/contagion case
    if (options.rollType === "save" && (options.rollTarget.rollKey === "poison" || options.rollTarget.rollKey === "contagion")) {
      hasD30 = false
      hasStaticModifier = true
      modifierFormula = ` + ${Math.abs(fullModifier)}`
      titleFormula = `${dice}E + ${Math.abs(fullModifier)}`
    }

    if (letItFly) {
      baseFormula = "1D20"
      titleFormula = `1D20E`
      modifierFormula = "0"
      fullModifier = 0
      hasFavor = false
      hasExplode = true
      rollContext.favor = "none"
    }

    maxValue = Number(baseFormula.match(/\d+$/)[0]) // Update the max value agains

    const rollData = {
      type: options.rollType,
      rollType: options.rollType,
      target: options.rollTarget,
      rollName: options.rollName,
      actorId: options.actorId,
      actorName: options.actorName,
      actorImage: options.actorImage,
      rollMode: rollContext.visibility,
      hasTarget: options.hasTarget,
      isDamage: options.isDamage,
      damageSmall: options.damageSmall,
      damageMedium: options.damageMedium,
      pointBlank,
      beyondSkill,
      letItFly,
      hasGrantedDice,
      titleFormula,
      targetName,
      ...rollContext,
    }

    /**
     * A hook event that fires before the roll is made.
     * @function
     * @memberof hookEvents
     * @param {Object} options          Options for the roll.
     * @param {Object} rollData         All data related to the roll.
     * @returns {boolean}               Explicitly return `false` to prevent roll to be made.
     */
    if (Hooks.call("fvtt-lethal-fantasy.preRoll", options, rollData) === false) return

    let rollBase = new this(baseFormula, options.data, rollData)
    const rollModifier = new Roll(modifierFormula, options.data, rollData)
    await rollModifier.evaluate()
    await rollBase.evaluate()

    let rollFavor
    let badResult
    if (rollContext.favor === "favor") {
      rollFavor = new this(baseFormula, options.data, rollData)
      await rollFavor.evaluate()
      console.log("Rolling with favor", rollFavor)
      if (game?.dice3d) {
        game.dice3d.showForRoll(rollFavor, game.user, true)
      }
      if (Number(rollFavor.result) > Number(rollBase.result)) {
        badResult = rollBase.result
        rollBase = rollFavor
      } else {
        badResult = rollFavor.result
      }
      rollFavor = null
    }

    if (rollContext.favor === "disfavor") {
      rollFavor = new this(baseFormula, options.data, rollData)
      await rollFavor.evaluate()
      if (game?.dice3d) {
        game.dice3d.showForRoll(rollFavor, game.user, true)
      }
      if (Number(rollFavor.result) < Number(rollBase.result)) {
        badResult = rollBase.result
        rollBase = rollFavor
      } else {
        badResult = rollFavor.result
      }
      rollFavor = null
    }

    if (hasD30) {
      let rollD30 = await new Roll("1D30").evaluate()
      if (game?.dice3d) {
        game.dice3d.showForRoll(rollD30, game.user, true)
      }
      options.D30result = rollD30.total
    }

    let rollTotal = 0
    let diceResults = []
    let resultType
    let diceSum = 0

    let singleDice = `1D${maxValue}`
    for (let i = 0; i < rollBase.dice.length; i++) {
      for (let j = 0; j < rollBase.dice[i].results.length; j++) {
        let diceResult = rollBase.dice[i].results[j].result
        diceResults.push({ dice: `${singleDice.toUpperCase()}`, value: diceResult })
        diceSum += diceResult
        if (hasMaxValue) {
          while (diceResult === maxValue) {
            let r = await new Roll(baseFormula).evaluate()
            if (game?.dice3d) {
              await game.dice3d.showForRoll(r, game.user, true)
            }
            diceResult = r.dice[0].results[0].result
            diceResults.push({ dice: `${singleDice.toUpperCase()}-1`, value: diceResult - 1 })
            diceSum += (diceResult - 1)
          }
        }
      }
    }

    if (hasGrantedDice && options.rollTarget.grantedDice && options.rollTarget.grantedDice !== "") {
      titleFormula += ` + ${options.rollTarget.grantedDice.toUpperCase()}`
      let grantedRoll = new Roll(options.rollTarget.grantedDice)
      await grantedRoll.evaluate()
      if (game?.dice3d) {
        await game.dice3d.showForRoll(grantedRoll, game.user, true)
      }
      diceResults.push({ dice: `${options.rollTarget.grantedDice.toUpperCase()}`, value: grantedRoll.total })
      rollTotal += grantedRoll.total
    }

    if (fullModifier !== 0) {
      diceResults.push({ dice: `${rollModifier.formula.toUpperCase()}`, value: rollModifier.total })
      if (fullModifier < 0) {
        rollTotal += Math.max(diceSum - rollModifier.total, 0)
      } else {
        rollTotal += diceSum + rollModifier.total
      }
    } else {
      rollTotal += diceSum
    }

    rollBase.options.resultType = resultType
    rollBase.options.rollTotal = rollTotal
    rollBase.options.diceResults = diceResults
    rollBase.options.rollTarget = options.rollTarget
    rollBase.options.titleFormula = titleFormula
    rollBase.options.D30result = options.D30result
    rollBase.options.badResult = badResult
    rollBase.options.rollData = foundry.utils.duplicate(rollData)

    /**
     * A hook event that fires after the roll has been made.
     * @function
     * @memberof hookEvents
     * @param {Object} options          Options for the roll.
     * @param {Object} rollData         All data related to the roll.
      @param {LethalFantasyRoll} roll        The resulting roll.
     * @returns {boolean}               Explicitly return `false` to prevent roll to be made.
     */
    if (Hooks.call("fvtt-lethal-fantasy.Roll", options, rollData, rollBase) === false) return

    return rollBase
  }

  /* ***********************************************************/
  static async promptInitiative(options = {}) {
    const rollModes = foundry.utils.duplicate(CONFIG.Dice.rollModes); // v12 :  Object.fromEntries(Object.entries(CONFIG.Dice.rollModes).map(([key, value]) => [key, game.i18n.localize(value)]))
    const fieldRollMode = new foundry.data.fields.StringField({
      choices: rollModes,
      blank: false,
      default: "public",
    })

    if (SYSTEM.INITIATIVE_DICE_CHOICES_PER_CLASS[options.actorClass]) {
      options.initiativeDiceChoice = SYSTEM.INITIATIVE_DICE_CHOICES_PER_CLASS[options.actorClass]
    } else {
      options.initiativeDiceChoice = SYSTEM.INITIATIVE_DICE_CHOICES_PER_CLASS["untrained"]
    }

    let dialogContext = {
      actorClass: options.actorClass,
      initiativeDiceChoice: options.initiativeDiceChoice,
      initiativeDice: "1D20",
      maxInit: options.maxInit,
      fieldRollMode,
      rollModes
    }

    const content = await foundry.applications.handlebars.renderTemplate("systems/fvtt-lethal-fantasy/templates/roll-initiative-dialog.hbs", dialogContext)

    const label = game.i18n.localize("LETHALFANTASY.Label.initiative")
    const rollContext = await foundry.applications.api.DialogV2.wait({
      window: { title: "Initiative Roll" },
      classes: ["lethalfantasy"],
      content,
      buttons: [
        {
          label: label,
          callback: (event, button, dialog) => {
            const output = Array.from(button.form.elements).reduce((obj, input) => {
              if (input.name) obj[input.name] = input.value
              return obj
            }, {})
            return output
          },
        },
      ],
      rejectClose: false // Click on Close button will not launch an error
    })

    let initRoll = new Roll(`min(${rollContext.initiativeDice}, ${options.maxInit})`, options.data, rollContext)
    await initRoll.evaluate()
    let msg = await initRoll.toMessage({ flavor: `Initiative for ${options.actorName}` }, { rollMode: rollContext.visibility })
    if (game?.dice3d) {
      await game.dice3d.waitFor3DAnimationByMessageID(msg.id)
    }

    if (options.combatId && options.combatantId) {
      let combat = game.combats.get(options.combatId)
      combat.updateEmbeddedDocuments("Combatant", [{ _id: options.combatantId, initiative: initRoll.total, 'system.progressionCount': 0 }]);
    }
  }

  /* ***********************************************************/
  static async promptCombatAction(options = {}) {

    const rollModes = foundry.utils.duplicate(CONFIG.Dice.rollModes); // v12 : Object.fromEntries(Object.entries(CONFIG.Dice.rollModes).map(([key, value]) => [key, game.i18n.localize(value)]))
    const fieldRollMode = new foundry.data.fields.StringField({
      choices: rollModes,
      blank: false,
      default: "public",
    })

    let combatant = game.combats.get(options.combatId)?.combatants?.get(options.combatantId)
    if (!combatant) {
      console.error("No combatant found for this combat")
      return
    }
    let currentAction = combatant.getFlag(SYSTEM.id, "currentAction")

    let position = game.user.getFlag(SYSTEM.id, "combat-action-dialog-pos") || { top: -1, left: -1 }

    let dialogContext = {
      progressionDiceId: "",
      fieldRollMode,
      rollModes,
      currentAction,
      ...options
    }

    const content = await foundry.applications.handlebars.renderTemplate("systems/fvtt-lethal-fantasy/templates/combat-action-dialog.hbs", dialogContext)

    let buttons = []
    if (currentAction) {
      if (currentAction.type === "weapon") {
        buttons.push({
          action: "roll",
          label: "Roll progression dice",
          callback: (event, button, dialog) => {
            let pos = $('#combat-action-dialog').position()
            game.user.setFlag(SYSTEM.id, "combat-action-dialog-pos", pos)
            return "rollProgressionDice"
          },
        })
      } else if (currentAction.type === "spell" || currentAction.type === "miracle") {
        let label = ""
        if (currentAction.spellStatus === "castingTime") {
          let pos = $('#combat-action-dialog').position()
          game.user.setFlag(SYSTEM.id, "combat-action-dialog-pos", pos)
          label = "Wait casting time"
        }
        if (currentAction.spellStatus === "toBeCasted") {
          let pos = $('#combat-action-dialog').position()
          game.user.setFlag(SYSTEM.id, "combat-action-dialog-pos", pos)
          label = "Cast spell/miracle"
        }
        if (currentAction.spellStatus === "lethargy") {
          let pos = $('#combat-action-dialog').position()
          game.user.setFlag(SYSTEM.id, "combat-action-dialog-pos", pos)
          label = "Roll lethargy dice"
        }
        buttons.push({
          action: "roll",
          label: label,
          callback: (event, button, dialog) => {
            let pos = $('#combat-action-dialog').position()
            game.user.setFlag(SYSTEM.id, "combat-action-dialog-pos", foundry.utils.duplicate(pos))
            return "rollLethargyDice"
          },
        })
      }
    } else {
      buttons.push({
        action: "roll",
        label: "Select action",
        callback: (event, button, dialog) => {
          let pos = $('#combat-action-dialog').position()
          game.user.setFlag(SYSTEM.id, "combat-action-dialog-pos", foundry.utils.duplicate(pos))
          const output = Array.from(button.form.elements).reduce((obj, input) => {
            if (input.name) obj[input.name] = input.value
            return obj
          }, {})
          return output
        },
      },
      )
    }
    buttons.push({
      action: "cancel",
      label: "Other action, not listed here",
      callback: (event, button, dialog) => {
        let pos = $('#combat-action-dialog').position()
        game.user.setFlag(SYSTEM.id, "combat-action-dialog-pos", foundry.utils.duplicate(pos))
        return null;
      }
    })

    let rollContext = await foundry.applications.api.DialogV2.wait({
      window: { title: "Combat Action Dialog" },
      id: "combat-action-dialog",
      classes: ["lethalfantasy"],
      position,
      content,
      buttons,
      rejectClose: false // Click on Close button will not launch an error
    })

    console.log("RollContext", dialogContext, rollContext)
    // If action is cancelled, exit
    if (rollContext === null || rollContext === "cancel") {
      await combatant.setFlag(SYSTEM.id, "currentAction", "")
      let message = `${combatant.name} : Other action, progression reset`
      ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) })
      return
    }

    // Setup the current action
    if (!currentAction || currentAction === "") {
      // Get the item from the returned selectedChoice value
      let selectedChoice = rollContext.selectedChoice
      let rangedMode
      if (selectedChoice.match("simpleAim")) {
        selectedChoice = selectedChoice.replace("simpleAim", "")
        rangedMode = "simpleAim"
      }
      if (selectedChoice.match("carefulAim")) {
        selectedChoice = selectedChoice.replace("carefulAim", "")
        rangedMode = "carefulAim"
      }
      if (selectedChoice.match("focusedAim")) {
        selectedChoice = selectedChoice.replace("focusedAim", "")
        rangedMode = "focusedAim"
      }
      let selectedItem = combatant.actor.items.find(i => i.id === selectedChoice)
      // Setup flag for combat action usage
      let actionItem = foundry.utils.duplicate(selectedItem)
      actionItem.progressionCount = 1
      actionItem.rangedMode = rangedMode
      actionItem.castingTime = 1
      actionItem.spellStatus = "castingTime"
      // Set the flag on the combatant
      await combatant.setFlag(SYSTEM.id, "currentAction", actionItem)
      let message = `${combatant.name} action : ${selectedItem.name}, start rolling progression dice or casting time`
      ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) })
      rollContext = (actionItem.type === "weapon") ? "rollProgressionDice" : "rollLethargyDice" // Set the roll context to rollProgressionDice
      currentAction = actionItem
    }

    if (currentAction) {
      if (rollContext === "rollLethargyDice") {
        if (currentAction.spellStatus === "castingTime") {
          let time = currentAction.type === "spell" ? currentAction.system.castingTime : currentAction.system.prayerTime
          if (currentAction.castingTime < time) {
            let message = `Casting time : ${currentAction.name}, count : ${currentAction.castingTime}/${time}`
            ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) })
            currentAction.castingTime += 1
            await combatant.setFlag(SYSTEM.id, "currentAction", foundry.utils.duplicate(currentAction))
            return
          } else {
            let message = `Spell/Miracle ${currentAction.name} ready to be cast on next second !`
            ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) })
            currentAction.castingTime = 1
            currentAction.spellStatus = "toBeCasted"
            await combatant.setFlag(SYSTEM.id, "currentAction", foundry.utils.duplicate(currentAction))
            return
          }
        }
        if (currentAction.spellStatus === "toBeCasted") {
          combatant.actor.prepareRoll((currentAction.type === "spell") ? "spell-attack" : "miracle-attack", currentAction._id)
          if (currentAction.type === "spell") {
            currentAction.spellStatus = "lethargy"
            await combatant.setFlag(SYSTEM.id, "currentAction", foundry.utils.duplicate(currentAction))
          } else {
            // No lethargy for miracle
            await combatant.setFlag(SYSTEM.id, "currentAction", "")
          }
          return
        }
        if (currentAction.spellStatus === "lethargy") {
          // Roll lethargy dice
          let dice = LethalFantasyUtils.getLethargyDice(currentAction.system.level)
          let roll = new Roll(dice)
          await roll.evaluate()
          if (game?.dice3d) {
            await game.dice3d.showForRoll(roll)
          }
          let max = roll.dice[0].faces - 1
          let toCompare = Math.min(currentAction.progressionCount, max)
          if (roll.total <= toCompare) {
            // Notify that the player can act now with a chat message
            let message = game.i18n.format("LETHALFANTASY.Notifications.messageLethargyOK", { name: combatant.actor.name, spellName: currentAction.name, roll: roll.total })
            ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) })
            // Update the combatant progression count
            await combatant.setFlag(SYSTEM.id, "currentAction", "")
            // Display the action selection window again
            combatant.actor.system.rollProgressionDice(options.combatId, options.combatantId)
          } else {
            // Notify that the player cannot act now with a chat message
            currentAction.progressionCount += 1
            await combatant.setFlag(SYSTEM.id, "currentAction", foundry.utils.duplicate(currentAction))
            let message = game.i18n.format("LETHALFANTASY.Notifications.messageLethargyKO", { name: combatant.actor.name, spellName: currentAction.name, roll: roll.total })
            ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) })
          }
        }
      }

      if (rollContext === "rollProgressionDice") {
        let formula = currentAction.system.combatProgressionDice
        if (currentAction?.rangedMode) {
          let toSplit = currentAction.system.speed[currentAction.rangedMode]
          let split = toSplit.split("+")
          currentAction.rangedLoad = Number(split[0]) || 0
          formula = split[1]
          console.log("Ranged Mode", currentAction.rangedMode, currentAction.rangedLoad, formula)
        }
        // Range weapon loading
        if (!currentAction.weaponLoaded && currentAction.rangedLoad) {
          if (currentAction.progressionCount <= currentAction.rangedLoad) {
            let message = `Ranged weapon ${currentAction.name} is loading, loading count : ${currentAction.progressionCount}/${currentAction.rangedLoad}`
            ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) })
            currentAction.progressionCount += 1
            await combatant.setFlag(SYSTEM.id, "currentAction", foundry.utils.duplicate(currentAction))
          } else {
            let message = `Ranged weapon ${currentAction.name} is loaded !`
            ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) })
            currentAction.weaponLoaded = true
            currentAction.progressionCount = 1
            await combatant.setFlag(SYSTEM.id, "currentAction", foundry.utils.duplicate(currentAction))
          }
          return
        }

        // Melee mode
        let isMonster = combatant.actor.type === "monster"
        // Get the dice and roll it    if
        let roll = new Roll(formula)
        await roll.evaluate()

        let max = roll.dice[0].faces - 1
        max = Math.min(currentAction.progressionCount, max)
        let msg = await roll.toMessage({ flavor: `Progression Roll for ${currentAction.name}, progression count : ${currentAction.progressionCount}/${max}` }, { rollMode: rollContext.visibility })
        if (game?.dice3d) {
          await game.dice3d.waitFor3DAnimationByMessageID(msg.id)
        }

        if (roll.total <= max) {
          // Notify that the player can act now with a chat message
          let message = game.i18n.format("LETHALFANTASY.Notifications.messageProgressionOK", { isMonster, name: combatant.actor.name, weapon: currentAction.name, roll: roll.total })
          ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) })
          await combatant.setFlag(SYSTEM.id, "currentAction", "")
          combatant.actor.prepareRoll(currentAction.type === "weapon" ? "weapon-attack" : "spell-attack", currentAction._id)
        } else {
          // Notify that the player cannot act now with a chat message
          currentAction.progressionCount += 1
          combatant.setFlag(SYSTEM.id, "currentAction", foundry.utils.duplicate(currentAction))
          let message = game.i18n.format("LETHALFANTASY.Notifications.messageProgressionKO", { isMonster, name: combatant.actor.name, weapon: currentAction.name, roll: roll.total })
          ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) })
        }
      }
    }
  }

  /* ***********************************************************/
  static async promptRangedDefense(options = {}) {

    const rollModes = foundry.utils.duplicate(CONFIG.Dice.rollModes);
    const fieldRollMode = new foundry.data.fields.StringField({
      choices: rollModes,
      blank: false,
      default: "public",
    })

    let dialogContext = {
      movementChoices: SYSTEM.MOVEMENT_CHOICES,
      moveDirectionChoices: SYSTEM.MOVE_DIRECTION_CHOICES,
      sizeChoices: SYSTEM.SIZE_CHOICES,
      rangeChoices: SYSTEM.RANGE_CHOICES,
      attackerAimChoices: SYSTEM.ATTACKER_AIM_CHOICES,
      movement: "none",
      moveDirection: "none",
      size: "+5",
      range: "short",
      attackerAim: "simple",
      fieldRollMode,
      rollModes
    }

    const content = await foundry.applications.handlebars.renderTemplate("systems/fvtt-lethal-fantasy/templates/range-defense-dialog.hbs", dialogContext)

    const label = game.i18n.localize("LETHALFANTASY.Label.rangeDefenseRoll")
    const rollContext = await foundry.applications.api.DialogV2.wait({
      window: { title: "Range Defense" },
      classes: ["lethalfantasy"],
      content,
      buttons: [
        {
          label: label,
          callback: (event, button, dialog) => {
            const output = Array.from(button.form.elements).reduce((obj, input) => {
              if (input.name) obj[input.name] = input.value
              return obj
            }, {})
            return output
          },
        },
      ],
      rejectClose: false // Click on Close button will not launch an error
    })

    // If the user cancels the dialog, exit
    if (rollContext === null) return

    console.log("RollContext", rollContext)
    // Add disfavor/favor option if point blank range
    if (rollContext.range === "pointblank") {
      rollContext.movement = rollContext.movement.replace("kh", "")
      rollContext.movement = rollContext.movement.replace("kl", "")
      rollContext.movement += "kl" // Add the kl to the movement (disfavor for point blank range)
      rollContext.range = "0"
    }
    if (rollContext.range === "beyondskill") {
      rollContext.movement = rollContext.movement.replace("kh", "")
      rollContext.movement = rollContext.movement.replace("kl", "")
      rollContext.movement += "kh" // Add the kl to the movement (favor for point blank range)
      rollContext.range = "+11"
    }

    // Build the final modifier
    let fullModifier = Number(rollContext.moveDirection) +
      Number(rollContext.size) +
      Number(rollContext.range) +
      Number(rollContext?.attackerAim || 0)

    let modifierFormula
    if (fullModifier === 0) {
      modifierFormula = "0"
    } else {
      let modAbs = Math.abs(fullModifier)
      modifierFormula = `D${modAbs + 1} -1`
    }

    let rollData = { ...rollContext }
    // Merge rollContext object into options object
    options = { ...options, ...rollContext }
    options.rollName = "Ranged Defense"

    const rollBase = new this(rollContext.movement, options.data, rollData)
    const rollModifier = new Roll(modifierFormula, options.data, rollData)
    rollModifier.evaluate()
    await rollBase.evaluate()
    let rollD30 = await new Roll("1D30").evaluate()
    options.D30result = rollD30.total

    let badResult = 0
    if (rollContext.movement.includes("kh")) {
      rollData.favor = "favor"
      badResult = Math.min(rollBase.terms[0].results[0].result, rollBase.terms[0].results[1]?.result || 20)
    }
    if (rollContext.movement.includes("kl")) {
      rollData.favor = "disfavor"
      badResult = Math.max(rollBase.terms[0].results[0].result, rollBase.terms[0].results[1]?.result || 1)
    }
    let dice = rollContext.movement
    let maxValue = 20 // As per latest changes (was : Number(dice.match(/\d+$/)[0])
    let rollTotal = -1
    let diceResults = []
    let resultType

    let diceResult = rollBase.dice[0].results[0].result
    diceResults.push({ dice: `${dice.toUpperCase()}`, value: diceResult })
    let diceSum = diceResult
    while (diceResult === maxValue) {
      let r = await new Roll(dice).evaluate()
      diceResult = r.dice[0].results[0].result
      diceResults.push({ dice: `${dice.toUpperCase()}-1`, value: diceResult - 1 })
      diceSum += (diceResult - 1)
    }
    if (fullModifier !== 0) {
      diceResults.push({ dice: `${rollModifier.formula.toUpperCase()}`, value: rollModifier.total })
      if (fullModifier < 0) {
        rollTotal = Math.max(diceSum - rollModifier.total, 0)
      } else {
        rollTotal = diceSum + rollModifier.total
      }
    } else {
      rollTotal = diceSum
    }
    rollBase.options = { ...rollBase.options, ...options }
    rollBase.options.resultType = resultType
    rollBase.options.rollTotal = rollTotal
    rollBase.options.diceResults = diceResults
    rollBase.options.rollTarget = options.rollTarget
    rollBase.options.titleFormula = `1D20E + ${modifierFormula}`
    rollBase.options.D30result = options.D30result
    rollBase.options.rollName = "Ranged Defense"
    rollBase.options.badResult = badResult
    rollBase.options.rollData = foundry.utils.duplicate(rollData)
    /**
     * A hook event that fires after the roll has been made.
     * @function
     * @memberof hookEvents
     * @param {Object} options          Options for the roll.
     * @param {Object} rollData         All data related to the roll.
      @param {LethalFantasyRoll} roll        The resulting roll.
     * @returns {boolean}               Explicitly return `false` to prevent roll to be made.
     */

    return rollBase
  }

  /**
   * Creates a title based on the given type.
   *
   * @param {string} type The type of the roll.
   * @param {string} target The target of the roll.
   * @returns {string} The generated title.
   */
  static createTitle(type, target) {
    switch (type) {
      case "challenge":
        return `${game.i18n.localize("LETHALFANTASY.Label.titleChallenge")}`
      case "save":
        return `${game.i18n.localize("LETHALFANTASY.Label.titleSave")}`
      case "monster-skill":
      case "skill":
        return `${game.i18n.localize("LETHALFANTASY.Label.titleSkill")}`
      case "weapon-attack":
        return `${game.i18n.localize("LETHALFANTASY.Label.weapon-attack")}`
      case "weapon-defense":
        return `${game.i18n.localize("LETHALFANTASY.Label.weapon-defense")}`
      case "weapon-damage-small":
        return `${game.i18n.localize("LETHALFANTASY.Label.weapon-damage-small")}`
      case "weapon-damage-medium":
        return `${game.i18n.localize("LETHALFANTASY.Label.weapon-damage-medium")}`
      case "spell":
      case "spell-attack":
      case "spell-power":
        return `${game.i18n.localize("LETHALFANTASY.Label.spell")}`
      case "miracle":
      case "miracle-attack":
      case "miracle-power":
        return `${game.i18n.localize("LETHALFANTASY.Label.miracle")}`
      default:
        return game.i18n.localize("LETHALFANTASY.Label.titleStandard")
    }
  }

  /** @override */
  async render(chatOptions = {}) {
    let chatData = await this._getChatCardData(chatOptions.isPrivate)
    console.log("ChatData", chatData)
    return await foundry.applications.handlebars.renderTemplate(this.constructor.CHAT_TEMPLATE, chatData)
  }

  /*
   * Generates the data required for rendering a roll chat card.
   */
  async _getChatCardData(isPrivate) {
    // Générer la liste des combatants de la scène
    let combatants = []
    if (game?.combat?.combatants && this.rollData?.isDamage) {
      for (let c of game.combat.combatants) {
        if (c.actorId !== this.actorId) {
          combatants.push({ id: c.id, name: c.name })
        }
      }
    }

    // Récupérer les informations de l'arme pour les attaques réussies
    let weaponDamageOptions = null
    console.log("Roll type:", this.type, "rollTarget:", this.rollTarget, "Has weapon:", !!this.rollTarget?.weapon)
    if (this.type === "weapon-attack" && this.rollTarget?.weapon) {
      const weapon = this.rollTarget.weapon
      weaponDamageOptions = {
        weaponId: weapon._id || weapon.id,
        weaponName: weapon.name,
        damageS: weapon.system?.damage?.damageS,
        damageM: weapon.system?.damage?.damageM
      }
      console.log("Weapon damage options:", weaponDamageOptions)
    } else if (this.type === "monster-attack" && this.rollTarget) {
      weaponDamageOptions = {
        weaponId: this.rollTarget.rollKey,
        weaponName: this.rollTarget.name,
        damageFormula: this.rollTarget.damageDice,
        damageModifier: this.rollTarget.damageModifier,
        isMonster: true
      }
      console.log("Monster damage options:", weaponDamageOptions)
    }

    const cardData = {
      css: [SYSTEM.id, "dice-roll"],
      data: this.data,
      diceTotal: this.dice.reduce((t, d) => t + d.total, 0),
      isGM: game.user.isGM,
      formula: this.formula,
      titleFormula: this.titleFormula,
      rollName: this.rollName,
      rollType: this.type,
      rollTarget: this.rollTarget,
      total: this.rollTotal,
      isFailure: this.isFailure,
      actorId: this.actorId,
      diceResults: this.diceResults,
      actingCharName: this.actorName,
      actingCharImg: this.actorImage,
      resultType: this.resultType,
      hasTarget: this.hasTarget,
      targetName: this.targetName,
      targetArmor: this.targetArmor,
      D30result: this.D30result,
      badResult: this.badResult,
      rollData: this.rollData,
      isPrivate: isPrivate,
      combatants: combatants,
      weaponDamageOptions: weaponDamageOptions
    }
    cardData.cssClass = cardData.css.join(" ")
    cardData.tooltip = isPrivate ? "" : await this.getTooltip()
    return cardData
  }

  /**
   * Converts the roll result to a chat message.
   *
   * @param {Object} [messageData={}] Additional data to include in the message.
   * @param {Object} options Options for message creation.
   * @param {string} options.rollMode The mode of the roll (e.g., public, private).
   * @param {boolean} [options.create=true] Whether to create the message.
   * @returns {Promise} - A promise that resolves when the message is created.
   */
  async toMessage(messageData = {}, { rollMode, create = true } = {}) {
    super.toMessage(
      {
        isSave: this.isSave,
        isChallenge: this.isChallenge,
        isFailure: this.resultType === "failure",
        rollType: this.type,
        rollTarget: this.rollTarget,
        actingCharName: this.actorName,
        actingCharImg: this.actorImage,
        hasTarget: this.hasTarget,
        targetName: this.targetName,
        targetArmor: this.targetArmor,
        targetMalus: this.targetMalus,
        realDamage: this.realDamage,
        rollData: this.rollData,
        ...messageData,
      },
      { rollMode: rollMode },
    )
  }

}
