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

// Map temporaire pour stocker les données d'attaque en attente de défense
if (!globalThis.pendingDefenses) {
  globalThis.pendingDefenses = new Map()
}

export default class LethalFantasyUtils {

  /* -------------------------------------------- */
  static async loadCompendiumData(compendium) {
    const pack = game.packs.get(compendium)
    return await pack?.getDocuments() ?? []
  }

  /* -------------------------------------------- */
  static async loadCompendium(compendium, filter = item => true) {
    let compendiumData = await LethalFantasyUtils.loadCompendiumData(compendium)
    return compendiumData.filter(filter)
  }

  /* -------------------------------------------- */
  static pushCombatOptions(html, options) {
    options.push({ name: "Reset Progression", condition: true, icon: '<i class="fas fa-rotate-right"></i>', callback: target => { game.combat.resetProgression(target.data('combatant-id')); } })
  }

  /* -------------------------------------------- */
  static setHookListeners() {

    Hooks.on('renderTokenHUD', async (hud, html, token) => {
      // HP Loss Button (existing)
      const lossHPButton = await foundry.applications.handlebars.renderTemplate('systems/fvtt-lethal-fantasy/templates/loss-hp-hud.hbs', {})
      $(html).find('div.left').append(lossHPButton);
      $(html).find('img.lethal-hp-loss-hud').click((event) => {
        event.preventDefault();
        let hpMenu = $(html).find('.hp-loss-wrap')[0]
        if (hpMenu.classList.contains("hp-loss-hud-disabled")) {
          $(html).find('.hp-loss-wrap')[0].classList.add('hp-loss-hud-active');
          $(html).find('.hp-loss-wrap')[0].classList.remove('hp-loss-hud-disabled');
          $(html).find('.hp-loss-wrap')[1].classList.add('hp-loss-hud-active');
          $(html).find('.hp-loss-wrap')[1].classList.remove('hp-loss-hud-disabled');
          $(html).find('.hp-loss-wrap')[2].classList.add('hp-loss-hud-active');
          $(html).find('.hp-loss-wrap')[2].classList.remove('hp-loss-hud-disabled');
        } else {
          $(html).find('.hp-loss-wrap')[0].classList.remove('hp-loss-hud-active');
          $(html).find('.hp-loss-wrap')[0].classList.add('hp-loss-hud-disabled');
          $(html).find('.hp-loss-wrap')[1].classList.remove('hp-loss-hud-active');
          $(html).find('.hp-loss-wrap')[1].classList.add('hp-loss-hud-disabled');
          $(html).find('.hp-loss-wrap')[2].classList.remove('hp-loss-hud-active');
          $(html).find('.hp-loss-wrap')[2].classList.add('hp-loss-hud-disabled');
        }
      })
      $(html).find('.loss-hp-hud-click').click((event) => {
        event.preventDefault();
        let hpLoss = event.currentTarget.dataset.hpValue;
        if (token) {
          let tokenFull = canvas.tokens.placeables.find(t => t.id === token._id);
          console.log(tokenFull, token)
          let actor = tokenFull.actor;
          actor.applyDamage(Number(hpLoss));
          $(html).find('.hp-loss-wrap')[0].classList.remove('hp-loss-hud-active');
          $(html).find('.hp-loss-wrap')[0].classList.add('hp-loss-hud-disabled');
          $(html).find('.hp-loss-wrap')[1].classList.remove('hp-loss-hud-active');
          $(html).find('.hp-loss-wrap')[1].classList.add('hp-loss-hud-disabled');
          $(html).find('.hp-loss-wrap')[2].classList.remove('hp-loss-hud-active');
          $(html).find('.hp-loss-wrap')[2].classList.add('hp-loss-hud-disabled');
        }
      })

      // HP Gain Button (new)
      const gainHPButton = await foundry.applications.handlebars.renderTemplate('systems/fvtt-lethal-fantasy/templates/gain-hp-hud.hbs', {})
      $(html).find('div.left').append(gainHPButton);
      $(html).find('img.lethal-hp-gain-hud').click((event) => {
        event.preventDefault();
        let hpMenu = $(html).find('.hp-gain-wrap')[0]
        if (hpMenu.classList.contains("hp-gain-hud-disabled")) {
          $(html).find('.hp-gain-wrap')[0].classList.add('hp-gain-hud-active');
          $(html).find('.hp-gain-wrap')[0].classList.remove('hp-gain-hud-disabled');
          $(html).find('.hp-gain-wrap')[1].classList.add('hp-gain-hud-active');
          $(html).find('.hp-gain-wrap')[1].classList.remove('hp-gain-hud-disabled');
          $(html).find('.hp-gain-wrap')[2].classList.add('hp-gain-hud-active');
          $(html).find('.hp-gain-wrap')[2].classList.remove('hp-gain-hud-disabled');
        } else {
          $(html).find('.hp-gain-wrap')[0].classList.remove('hp-gain-hud-active');
          $(html).find('.hp-gain-wrap')[0].classList.add('hp-gain-hud-disabled');
          $(html).find('.hp-gain-wrap')[1].classList.remove('hp-gain-hud-active');
          $(html).find('.hp-gain-wrap')[1].classList.add('hp-gain-hud-disabled');
          $(html).find('.hp-gain-wrap')[2].classList.remove('hp-gain-hud-active');
          $(html).find('.hp-gain-wrap')[2].classList.add('hp-gain-hud-disabled');
        }
      })
      $(html).find('.gain-hp-hud-click').click((event) => {
        event.preventDefault();
        let hpGain = event.currentTarget.dataset.hpValue;
        if (token) {
          let tokenFull = canvas.tokens.placeables.find(t => t.id === token._id);
          console.log(tokenFull, token)
          let actor = tokenFull.actor;
          actor.applyDamage(Number(hpGain)); // Positive value to add HP
          $(html).find('.hp-gain-wrap')[0].classList.remove('hp-gain-hud-active');
          $(html).find('.hp-gain-wrap')[0].classList.add('hp-gain-hud-disabled');
          $(html).find('.hp-gain-wrap')[1].classList.remove('hp-gain-hud-active');
          $(html).find('.hp-gain-wrap')[1].classList.add('hp-gain-hud-disabled');
          $(html).find('.hp-gain-wrap')[2].classList.remove('hp-gain-hud-active');
          $(html).find('.hp-gain-wrap')[2].classList.add('hp-gain-hud-disabled');
        }
      })
    })
  }

  /* -------------------------------------------- */
  static handleSocketEvent(msg = {}) {
    console.log(`handleSocketEvent !`, msg)
    let actor
    switch (msg.type) {
      case "applyDamage":
        if (game.user.isGM) {
          // Prefer the specific token actor (correct for unlinked monsters); fall back to world actor.
          actor = msg.tokenId
            ? canvas.tokens?.placeables?.find(t => t.id === msg.tokenId)?.actor
            : (game.combat?.combatants?.find(c => c.actorId === msg.actorId)?.actor
              ?? game.actors.get(msg.actorId))
          if (actor) actor.applyDamage(msg.damage)
        }
        break
      case "rollInitiative":
        if (msg.userId && msg.userId !== game.user.id) break
        actor = game.actors.get(msg.actorId)
        actor.system.rollInitiative(msg.combatId, msg.combatantId)
        break
      case "rollProgressionDice":
        if (msg.userId && msg.userId !== game.user.id) break
        actor = game.actors.get(msg.actorId)
        actor.system.rollProgressionDice(msg.combatId, msg.combatantId, msg.rollProgressionCount)
        break
      case "requestDefense":
        // Vérifier si le message est destiné à cet utilisateur
        if (msg.userId === game.user.id) {
          LethalFantasyUtils.showDefenseRequest(msg)
        }
        break
      case "offerAttackerGrit":
        // Vérifier si le message est destiné à cet utilisateur
        if (msg.userId === game.user.id) {
          LethalFantasyUtils.handleAttackerGritOffer(msg)
        }
        break
    }
  }

  /* -------------------------------------------- */
  static async handleAttackerGritOffer(msg) {
    const { attackerId, attackRoll, defenseRoll, attackerName, defenderName, attackWeaponId, attackRollType, attackRollKey, defenderId } = msg

    const attacker = game.actors.get(attackerId)
    if (!attacker) {
      console.warn("Attacker not found:", attackerId)
      return
    }

    const attackBonus = await LethalFantasyUtils.offerAttackerGritBonus(
      attacker,
      attackRoll,
      defenseRoll,
      attackerName,
      defenderName
    )

    const attackRollFinal = attackRoll + attackBonus

    // Maintenant créer le message de comparaison
    await LethalFantasyUtils.compareAttackDefense({
      attackerName,
      attackerId,
      attackRoll: attackRollFinal,
      attackWeaponId,
      attackRollType,
      attackRollKey,
      defenderName,
      defenderId,
      defenseRoll
    })
  }

  /* -------------------------------------------- */
  static async showDefenseRequest(msg) {
    const attackerName = msg.attackerName
    const attackerId = msg.attackerId
    const defenderName = msg.defenderName
    const weaponName = msg.weaponName || "attack"
    const attackRoll = msg.attackRoll
    const attackWeaponId = msg.attackWeaponId
    const attackRollType = msg.attackRollType
    const attackRollKey = msg.attackRollKey
    const attackD30result = msg.attackD30result
    const attackD30message = msg.attackD30message
    const attackRerollContext = msg.attackRerollContext
    const combatantId = msg.combatantId
    const tokenId = msg.tokenId

    // Récupérer le défenseur - essayer d'abord depuis le combat, puis depuis le token
    let defender = null

    if (game.combat && combatantId) {
      const combatant = game.combat.combatants.get(combatantId)
      if (combatant) {
        defender = combatant.actor
      }
    }

    // Si pas trouvé dans le combat, chercher le token directement
    if (!defender && tokenId) {
      const token = canvas.tokens.get(tokenId)
      if (token) {
        defender = token.actor
      }
    }

    if (!defender) {
      ui.notifications.error("Defender actor not found")
      return
    }

    // Resolve the specific token ID now while we still have combatant/token context.
    // This is passed through to the damage roll so the GM-side socket handler can find the
    // correct synthetic actor for unlinked tokens (avoids wrong-instance damage with multiple
    // unlinked copies of the same monster).
    const defenderTokenId = (() => {
      if (game.combat && combatantId) {
        const cbt = game.combat.combatants.get(combatantId)
        if (cbt?.token?.id) return cbt.token.id
      }
      return tokenId ?? canvas.tokens?.placeables?.find(t => t.actor?.id === defender.id)?.id ?? null
    })()

    const isMonster = defender.type === "monster"

    console.log(`[LF] showDefenseRequest | attackRollType=${attackRollType} isMonster=${isMonster} defender=${defender?.name}`)

    // Spell/miracle attacks use saving throws instead of weapon defense
    const isSpellAttack = attackRollType === "spell-attack" || attackRollType === "miracle-attack"
    if (isSpellAttack) {
      const savesConfig = isMonster ? SYSTEM.MONSTER_SAVES : SYSTEM.SAVES
      const combatSaves = ["will", "dodge", "toughness"]
      const savesHTML = Object.values(savesConfig)
        .filter(s => combatSaves.includes(s.id))
        .map(s => `<option value="${s.id}">${game.i18n.localize(s.label)}</option>`)
        .join("")

      const content = `
        <div class="defense-request-dialog">
          <div class="attack-info">
            <p><strong>${attackerName}</strong> targets <strong>${defenderName}</strong> with <strong>${weaponName}</strong>!</p>
            <p>Attack roll: <strong>${attackRoll}</strong></p>
          </div>
          <div class="weapon-selection">
            <label for="save-type">Choose saving throw:</label>
            <select id="save-type" name="saveKey" style="width: 100%; margin-top: 8px;">
              ${savesHTML}
            </select>
          </div>
        </div>
      `

      const result = await foundry.applications.api.DialogV2.wait({
        window: { title: "Saving Throw vs Spell" },
        classes: ["lethalfantasy"],
        content,
        buttons: [
          {
            action: "rollSave",
            label: "Roll Save",
            icon: "fa-solid fa-person-running",
            callback: (event, button) => button.form.elements.saveKey.value,
          },
        ],
        rejectClose: false
      })

      if (result) {
        game.lethalFantasy = game.lethalFantasy || {}
        game.lethalFantasy.nextDefenseData = {
          attackerId,
          attackRoll,
          attackerName,
          defenderName,
          attackWeaponId,
          attackRollType,
          attackRollKey,
          attackD30result,
          attackD30message,
          attackRerollContext,
          defenderId: defender.id,
          defenderTokenId
        }
        if (isMonster) {
          defender.system.prepareMonsterRoll("save", result)
        } else {
          defender.prepareRoll("save", result)
        }
      }
      return
    }

    // Pour les monstres, récupérer les attaques activées
    if (isMonster) {
      const attacksSet = defender.system.attackMode === "ranged" ? defender.system.rangedAttacks : defender.system.attacks
      const enabledAttacks = Object.entries(attacksSet).filter(([key, attack]) => attack.enabled)

      if (enabledAttacks.length === 0) {
        ui.notifications.warn("No enabled attacks available for defense")
        return
      }

      // Créer le contenu du dialogue pour monstre
      let attacksHTML = enabledAttacks.map(([key, attack]) =>
        `<option value="${key}">${attack.name}</option>`
      ).join("")

      const content = `
        <div class="defense-request-dialog">
          <div class="attack-info">
            <p><strong>${attackerName}</strong> attacks <strong>${defenderName}</strong> with <strong>${weaponName}</strong>!</p>
            <p>Attack roll: <strong>${attackRoll}</strong></p>
          </div>
          <div class="weapon-selection">
            <label for="defense-attack">Choose your defense weapon:</label>
            <select id="defense-attack" name="attackKey" style="width: 100%; margin-top: 8px;">
              ${attacksHTML}
            </select>
          </div>
        </div>
      `

      // Afficher le dialogue
      const result = await foundry.applications.api.DialogV2.wait({
        window: { title: msg.isRanged ? "Ranged Defense Roll" : "Defense Roll" },
        classes: ["lethalfantasy"],
        content,
        buttons: [
          {
            label: "Roll Defense",
            icon: "fa-solid fa-shield",
            callback: (event, button, dialog) => {
              const attackKey = button.form.elements.attackKey.value
              return attackKey
            },
          },
        ],
        rejectClose: false
      })

      // Si l'utilisateur a validé, lancer le jet de défense
      if (result) {
        // Stocker temporairement les données pour le hook preCreateChatMessage
        game.lethalFantasy = game.lethalFantasy || {}
        game.lethalFantasy.nextDefenseData = {
          attackerId,
          attackRoll,
          attackerName,
          defenderName,
          attackWeaponId,
          attackRollType,
          attackRollKey,
          attackD30result,
          attackD30message,
          attackRerollContext,
          defenderId: defender.id,
          defenderTokenId,
          isRanged: msg.isRanged
        }

        defender.system.prepareMonsterRoll("monster-defense", result)
      }
      return
    }

    // Pour les personnages, récupérer les armes équipées
    // Si l'attaque est une attaque à distance, utiliser le dialogue de défense à distance
    if (msg.isRanged) {
      const { default: LethalFantasyRoll } = await import("./documents/roll.mjs")
      const roll = await LethalFantasyRoll.promptRangedDefense({
        actorId: defender.id,
        actorName: defender.name,
        actorImage: defender.img,
      })
      if (roll) {
        game.lethalFantasy = game.lethalFantasy || {}
        game.lethalFantasy.nextDefenseData = {
          attackerId,
          attackRoll,
          attackerName,
          defenderName,
          attackWeaponId,
          attackRollType,
          attackRollKey,
          attackD30result,
          attackD30message,
          attackRerollContext,
          defenderId: defender.id,
          defenderTokenId,
          isRanged: true
        }
        await roll.toMessage({}, { messageMode: roll.options.rollMode })
      }
      return
    }

    // Pour les personnages, récupérer les armes équipées
    const equippedWeapons = defender.items.filter(i =>
      i.type === "weapon" && i.system.equipped === true
    )

    if (equippedWeapons.length === 0) {
      ui.notifications.warn("No equipped weapons for defense")
      return
    }

    // Créer le contenu du dialogue pour personnage
    let weaponsHTML = equippedWeapons.map(w =>
      `<option value="${w.id}">${w.name}</option>`
    ).join("")

    const content = `
      <div class="defense-request-dialog">
        <div class="attack-info">
          <p><strong>${attackerName}</strong> attacks <strong>${defenderName}</strong> with <strong>${weaponName}</strong>!</p>
          <p>Attack roll: <strong>${attackRoll}</strong></p>
        </div>
        <div class="weapon-selection">
          <label for="defense-weapon">Choose your defense weapon:</label>
          <select id="defense-weapon" name="weaponId" style="width: 100%; margin-top: 8px;">
            ${weaponsHTML}
          </select>
        </div>
      </div>
    `

    // Afficher le dialogue
    const result = await foundry.applications.api.DialogV2.wait({
      window: { title: "Defense Roll" },
      classes: ["lethalfantasy"],
      content,
      buttons: [
        {
          label: "Roll Defense",
          icon: "fa-solid fa-shield",
          callback: (event, button, dialog) => {
            const weaponId = button.form.elements.weaponId.value
            return weaponId
          },
        },
      ],
      rejectClose: false
    })

    // Si l'utilisateur a validé, lancer le jet de défense
    if (result) {
      // Stocker temporairement les données pour le hook preCreateChatMessage
      game.lethalFantasy = game.lethalFantasy || {}
      game.lethalFantasy.nextDefenseData = {
        attackerId,
        attackRoll,
        attackerName,
        defenderName,
        attackWeaponId,
        attackRollType,
        attackRollKey,
        attackD30result,
        attackD30message,
        attackRerollContext,
        defenderId: defender.id,
        defenderTokenId,
        isRanged: msg.isRanged
      }

      console.log("Storing defense data for character:", defender.id)

      defender.prepareRoll("weapon-defense", result)
    }
  }

  /* -------------------------------------------- */
  static hasD30Reroll(d30Message) {
    return d30Message?.type === "mulligan"
  }

  /* -------------------------------------------- */
  static getCombatBonusDiceChoices() {
    return ["1d4", "1d6", "1d8", "1d10", "1d12", "1d20", "1d20e"]
  }

  /* -------------------------------------------- */
  static getShieldReactionData(actor) {
    if (!actor) return null
    if (actor.type === "monster") {
      const formula = actor.system.combat?.shieldDefenseDice
      const damageReduction = actor.getShieldDR()
      if (!formula || damageReduction <= 0) return null
      return {
        label: game.i18n.localize("LETHALFANTASY.Label.shieldDefenseDice"),
        formula,
        damageReduction
      }
    }

    const equippedShields = actor.items.filter(item => item.type === "shield" && item.system.equipped)
    if (equippedShields.length === 0) return null

    const shield = equippedShields[0]
    return {
      label: shield.name,
      formula: shield.system.defense,
      damageReduction: actor.getShieldDR(),
      shieldId: shield.id
    }
  }

  /* -------------------------------------------- */
  static async promptCombatBonusDie(actorName, sideLabel, currentRoll, opposingRoll) {
    const choices = this.getCombatBonusDiceChoices()
    const optionsHtml = choices.map(choice => `<option value="${choice}">${choice.toUpperCase()}</option>`).join("")
    const content = `
      <div class="grit-luck-dialog">
        <div class="combat-status">
          <p><strong>${actorName}</strong> currently has <strong>${currentRoll}</strong></p>
          <p>Opposing ${sideLabel} roll: <strong>${opposingRoll}</strong></p>
        </div>
        <div class="weapon-selection">
          <label for="bonus-die">Choose a bonus die:</label>
          <select id="bonus-die" name="bonusDie" style="width: 100%; margin-top: 8px;">
            ${optionsHtml}
          </select>
        </div>
      </div>
    `

    return await foundry.applications.api.DialogV2.wait({
      window: { title: "Add Bonus Die" },
      classes: ["lethalfantasy"],
      content,
      buttons: [
        {
          action: "roll",
          label: "Roll Bonus Die",
          icon: "fa-solid fa-dice",
          callback: (event, button) => {
            const sel = button.form?.elements?.bonusDie ?? button.closest("form")?.elements?.bonusDie
            return sel?.value ?? choices[0]
          }
        },
        {
          action: "cancel",
          label: "Cancel",
          icon: "fa-solid fa-xmark",
          callback: () => null
        }
      ],
      rejectClose: false
    })
  }

  /* -------------------------------------------- */
  /**
   * Prompt the GM or player to choose an ad-hoc shield dice and DR value.
   * Used when the defender has no pre-configured shield equipment.
   * @param {string} defenderName
   * @param {number} attackRoll
   * @param {number} defenseRoll
   * @returns {Promise<{formula: string, damageReduction: number}|null>}
   */
  static async promptAdHocShield(defenderName, attackRoll, defenseRoll) {
    const choices = this.getCombatBonusDiceChoices()
    const optionsHtml = choices.map(c => `<option value="${c}">${c.toUpperCase()}</option>`).join("")
    const content = `
      <div class="grit-luck-dialog">
        <div class="combat-status">
          <p><strong>${defenderName}</strong> uses a shield (not equipped)</p>
          <p>Attack: <strong>${attackRoll}</strong> — Current defense: <strong>${defenseRoll}</strong></p>
        </div>
        <div class="weapon-selection" style="margin-top:8px;">
          <label for="shield-dice">Shield dice:</label>
          <select id="shield-dice" name="shieldDice" style="width: 100%; margin-top: 4px;">
            ${optionsHtml}
          </select>
        </div>
        <div class="weapon-selection" style="margin-top:8px;">
          <label for="shield-dr">Shield DR value:</label>
          <input id="shield-dr" name="shieldDR" type="number" min="0" value="0" style="width: 100%; margin-top: 4px;" />
        </div>
      </div>
    `

    const raw = await foundry.applications.api.DialogV2.wait({
      window: { title: "Ad-hoc Shield Roll" },
      classes: ["lethalfantasy"],
      content,
      buttons: [
        {
          action: "roll",
          label: "Roll Shield",
          icon: "fa-solid fa-shield",
          callback: (event, button) => {
            const shieldDice = button.form?.elements?.shieldDice ?? button.closest("form")?.elements?.shieldDice
            const shieldDR = button.form?.elements?.shieldDR ?? button.closest("form")?.elements?.shieldDR
            return {
              formula: shieldDice?.value ?? "1d6",
              damageReduction: Number(shieldDR?.value) || 0
            }
          }
        },
        {
          action: "cancel",
          label: "Cancel",
          icon: "fa-solid fa-xmark",
          callback: () => null
        }
      ],
      rejectClose: false
    })

    return raw ?? null
  }

  /* -------------------------------------------- */
  /**
   * Roll a bonus die formula, optionally showing Dice So Nice animation and posting a chat message.
   * @param {string} formula
   * @param {Actor} actor
   * @param {Function} [messageContent]
   * @returns {Promise<number>}
   */
  static async rollBonusDie(formula, actor, messageContent) {
    const roll = new Roll(formula)
    await roll.evaluate()
    if (game?.dice3d) {
      await game.dice3d.showForRoll(roll, game.user, true)
    }
    if (messageContent) {
      await ChatMessage.create({
        content: messageContent(roll.total, formula),
        speaker: ChatMessage.getSpeaker({ actor })
      })
    }
    return roll.total
  }

  /* -------------------------------------------- */
  static async rerollConfiguredRoll(rerollContext = {}) {
    const RollClass = CONFIG.Dice.rolls.find(r => r.name === "LethalFantasyRoll")
    if (typeof RollClass?.prompt !== "function") {
      ui.notifications.error("Lethal Fantasy roll class not available for reroll")
      return null
    }

    return await RollClass.prompt({
      ...foundry.utils.duplicate(rerollContext),
      rollContext: foundry.utils.duplicate(rerollContext.rollContext || {}),
      forceNoD30: true,
      hasTarget: false,
      target: false
    })
  }

  /* -------------------------------------------- */
  static async offerGritLuckBonus(defender, attackRoll, currentDefenseRoll, attackerName, defenderName) {
    let totalBonus = 0
    let keepOffering = true

    while (keepOffering && currentDefenseRoll + totalBonus < attackRoll) {
      const currentGrit = defender.system.grit.current
      const currentLuck = defender.system.luck.current

      // Si plus de points disponibles, sortir
      if (currentGrit <= 0 && currentLuck <= 0) {
        break
      }

      const buttons = []

      if (currentGrit > 0) {
        buttons.push({
          action: "grit",
          label: `Spend 1 Grit (+1D6) [${currentGrit} left]`,
          icon: "fa-solid fa-fist-raised",
          callback: () => "grit"
        })
      }

      if (currentLuck > 0) {
        buttons.push({
          action: "luck",
          label: `Spend 1 Luck (+1D6) [${currentLuck} left]`,
          icon: "fa-solid fa-clover",
          callback: () => "luck"
        })
      }

      buttons.push({
        action: "continue",
        label: "Continue (no bonus)",
        icon: "fa-solid fa-forward",
        callback: () => "continue"
      })

      const content = `
        <div class="grit-luck-dialog">
          <div class="combat-status">
            <p><strong>${attackerName}</strong> rolled <strong>${attackRoll}</strong></p>
            <p><strong>${defenderName}</strong> currently has <strong>${currentDefenseRoll + totalBonus}</strong></p>
            ${totalBonus > 0 ? `<p class="bonus-info">Bonus already added: +${totalBonus}</p>` : ''}
          </div>
          <p class="offer-text">You are losing! Spend Grit or Luck to add 1D6 to your defense?</p>
          <p class="shield-warning"><i class="fa-solid fa-triangle-exclamation"></i> If you intend to use a shield, you must spend Grit or Luck <strong>first</strong> — the shield roll comes after.</p>
        </div>
      `

      const choice = await foundry.applications.api.DialogV2.wait({
        window: { title: "Defend with Grit or Luck" },
        classes: ["lethalfantasy"],
        content,
        buttons,
        rejectClose: false
      })

      if (!choice || choice === "continue") {
        keepOffering = false
        break
      }

      // Lancer 1D6
      const bonusRoll = new Roll("1d6")
      await bonusRoll.evaluate()

      if (game?.dice3d) {
        await game.dice3d.showForRoll(bonusRoll, game.user, true)
      }

      totalBonus += bonusRoll.total

      // Déduire le point de Grit ou Luck
      if (choice === "grit") {
        await defender.update({ "system.grit.current": currentGrit - 1 })
        await ChatMessage.create({
          content: `<p><strong>${defenderName}</strong> spends 1 Grit and rolls <strong>${bonusRoll.total}</strong>! (Total defense bonus: +${totalBonus})</p>`,
          speaker: ChatMessage.getSpeaker({ actor: defender })
        })
      } else if (choice === "luck") {
        await defender.update({ "system.luck.current": currentLuck - 1 })
        await ChatMessage.create({
          content: `<p><strong>${defenderName}</strong> spends 1 Luck and rolls <strong>${bonusRoll.total}</strong>! (Total defense bonus: +${totalBonus})</p>`,
          speaker: ChatMessage.getSpeaker({ actor: defender })
        })
      }
    }

    return totalBonus
  }

  /* -------------------------------------------- */
  static async offerAttackerGritBonus(attacker, currentAttackRoll, defenseRoll, attackerName, defenderName) {
    let totalBonus = 0
    let keepOffering = true

    while (keepOffering && currentAttackRoll + totalBonus <= defenseRoll) {
      const currentGrit = attacker.system.grit.current

      // Si plus de points de Grit disponibles, sortir
      if (currentGrit <= 0) {
        break
      }

      const buttons = [
        {
          action: "grit",
          label: `Spend 1 Grit (+1D6) [${currentGrit} left]`,
          icon: "fa-solid fa-fist-raised",
          callback: () => "grit"
        },
        {
          action: "continue",
          label: "Continue (no bonus)",
          icon: "fa-solid fa-forward",
          callback: () => "continue"
        }
      ]

      const content = `
        <div class="grit-luck-dialog">
          <div class="combat-status">
            <p><strong>${attackerName}</strong> currently has <strong>${currentAttackRoll + totalBonus}</strong></p>
            <p><strong>${defenderName}</strong> rolled <strong>${defenseRoll}</strong></p>
            ${totalBonus > 0 ? `<p class="bonus-info">Bonus already added: +${totalBonus}</p>` : ''}
          </div>
          <p class="offer-text">You are losing! Spend Grit to add 1D6 to your attack?</p>
        </div>
      `

      const choice = await foundry.applications.api.DialogV2.wait({
        window: { title: "Attack with Grit" },
        classes: ["lethalfantasy"],
        content,
        buttons,
        rejectClose: false
      })

      if (!choice || choice === "continue") {
        keepOffering = false
        break
      }

      // Lancer 1D6
      const bonusRoll = new Roll("1d6")
      await bonusRoll.evaluate()

      if (game?.dice3d) {
        await game.dice3d.showForRoll(bonusRoll, game.user, true)
      }

      totalBonus += bonusRoll.total

      // Déduire le point de Grit
      await attacker.update({ "system.grit.current": currentGrit - 1 })
      await ChatMessage.create({
        content: `<p><strong>${attackerName}</strong> spends 1 Grit and rolls <strong>${bonusRoll.total}</strong>! (Total attack bonus: +${totalBonus})</p>`,
        speaker: ChatMessage.getSpeaker({ actor: attacker })
      })
    }

    return totalBonus
  }

  /* -------------------------------------------- */
  static async compareAttackDefense(data) {
    console.log("compareAttackDefense called with:", data)
    const outcome = data.outcome || (data.attackRoll > data.defenseRoll ? "hit" : "miss")
    const isAttackWin = outcome !== "miss"
    console.log("isAttackWin:", isAttackWin, "attackRoll:", data.attackRoll, "defenseRoll:", data.defenseRoll)

    let damageButton = ""
    if (isAttackWin && (data.attackWeaponId || data.attackRollKey)) {
      console.log("Creating damage button. defenderId:", data.defenderId)
      // Déterminer le type de dégâts à lancer
      if (data.attackRollType === "weapon-attack") {
        damageButton = `
          <div class="attack-result-damage">
            <button class="roll-damage-btn" data-attacker-id="${data.attackerId}" data-defender-id="${data.defenderId}" data-defender-token-id="${data.defenderTokenId || ""}" data-extra-shield-dr="${data.shieldDamageReduction || 0}" data-weapon-id="${data.attackWeaponId}" data-damage-type="small">
              <i class="fa-solid fa-dice-d6"></i> Damage (Small)
            </button>
            <button class="roll-damage-btn" data-attacker-id="${data.attackerId}" data-defender-id="${data.defenderId}" data-defender-token-id="${data.defenderTokenId || ""}" data-extra-shield-dr="${data.shieldDamageReduction || 0}" data-weapon-id="${data.attackWeaponId}" data-damage-type="medium">
              <i class="fa-solid fa-dice-d20"></i> Damage (Medium)
            </button>
          </div>
        `
      } else if (data.attackRollType === "monster-attack") {
        damageButton = `
          <div class="attack-result-damage single-btn">
            <button class="roll-damage-btn" data-attacker-id="${data.attackerId}" data-defender-id="${data.defenderId}" data-defender-token-id="${data.defenderTokenId || ""}" data-extra-shield-dr="${data.shieldDamageReduction || 0}" data-attack-key="${data.attackRollKey}" data-damage-type="monster">
              <i class="fa-solid fa-burst"></i> Damage
            </button>
          </div>
        `
      } else if (data.attackRollType === "spell-attack" || data.attackRollType === "miracle-attack") {
        const attacker = game.actors.get(data.attackerId)
        const spell = attacker?.items.get(data.attackWeaponId || data.attackRollKey)
        const tiers = [
          { formula: spell?.system?.damageDice, label: "Standard" },
          { formula: spell?.system?.damageDiceOverpowered, label: "Overpowered" },
          { formula: spell?.system?.damageDiceOverpowered2, label: "Overpowered 2" },
        ].filter(t => t.formula)
        if (tiers.length) {
          const buttons = tiers.map(t => {
            const escapedFormula = Handlebars.escapeExpression(t.formula)
            return `
            <button class="roll-damage-btn"
              data-attacker-id="${data.attackerId}"
              data-defender-id="${data.defenderId}"
              data-defender-token-id="${data.defenderTokenId || ""}"
              data-damage-type="spell"
              data-damage-formula="${escapedFormula}">
              <i class="fa-solid fa-wand-magic-sparkles"></i> ${t.label} (${escapedFormula})
            </button>`
          }).join("")
          damageButton = `<div class="attack-result-damage spell-damage">${buttons}</div>`
        }
      }
    }

    const resultMessage = `
      <div class="attack-result ${isAttackWin ? 'attack-success' : 'attack-failure'}">
        <h3><i class="fa-solid ${isAttackWin ? 'fa-sword' : 'fa-shield'}"></i> Combat Result</h3>
        <div class="combat-comparison">
          <div class="combat-side attacker ${isAttackWin ? 'winner' : 'loser'}">
            <div class="side-label">Attacker</div>
            <div class="side-info">
              <div class="side-name">${data.attackerName}</div>
              <div class="side-roll">${data.attackRoll}</div>
            </div>
          </div>
          <div class="combat-vs">VS</div>
          <div class="combat-side defender ${isAttackWin ? 'loser' : 'winner'}">
            <div class="side-label">Defender</div>
            <div class="side-info">
              <div class="side-name">${data.defenderName}</div>
              <div class="side-roll">${data.defenseRoll}</div>
            </div>
          </div>
        </div>
        <div class="combat-result-text">
          ${outcome === "shielded-hit"
        ? `<i class="fa-solid fa-shield"></i> <strong>${data.defenderName}</strong> has blocked with shield — apply armor DR + shield DR <strong>${data.shieldDamageReduction || 0}</strong>.`
        : isAttackWin
          ? `<i class="fa-solid fa-circle-check"></i> <strong>${data.attackerName}</strong> hits <strong>${data.defenderName}</strong>!`
          : `<i class="fa-solid fa-shield-halved"></i> <strong>${data.defenderName}</strong> avoided the attack!`
      }
        </div>
        ${damageButton}
      </div>
    `

    console.log("Creating combat result message...")
    await ChatMessage.create({
      content: resultMessage,
      speaker: { alias: "Combat System" }
    })
    console.log("Combat result message created!")
  }

  static registerHandlebarsHelpers() {

    Handlebars.registerHelper('isNull', function (val) {
      return val == null;
    });
    Handlebars.registerHelper('match', function (val, search) {
      if (val && search) {
        return val?.match(search);
      }
      return false
    });

    Handlebars.registerHelper('exists', function (val) {
      return val != null && val !== undefined;
    });

    Handlebars.registerHelper('isEmpty', function (list) {
      if (list) return list.length === 0;
      else return false;
    });

    Handlebars.registerHelper('notEmpty', function (list) {
      return list.length > 0;
    });

    Handlebars.registerHelper('isNegativeOrNull', function (val) {
      return val <= 0;
    });

    Handlebars.registerHelper('isNegative', function (val) {
      return val < 0;
    });

    Handlebars.registerHelper('isPositive', function (val) {
      return val > 0;
    });

    Handlebars.registerHelper('equals', function (val1, val2) {
      return val1 === val2;
    });

    Handlebars.registerHelper('neq', function (val1, val2) {
      return val1 !== val2;
    });

    Handlebars.registerHelper('gt', function (val1, val2) {
      return val1 > val2;
    })

    Handlebars.registerHelper('lt', function (val1, val2) {
      return val1 < val2;
    })

    Handlebars.registerHelper('gte', function (val1, val2) {
      return val1 >= val2;
    })

    Handlebars.registerHelper('lte', function (val1, val2) {
      return val1 <= val2;
    })
    Handlebars.registerHelper('and', function (val1, val2) {
      return val1 && val2;
    })
    Handlebars.registerHelper('or', function (val1, val2) {
      return val1 || val2;
    })

    Handlebars.registerHelper('or3', function (val1, val2, val3) {
      return val1 || val2 || val3;
    })

    Handlebars.registerHelper('for', function (from, to, incr, block) {
      let accum = '';
      for (let i = from; i < to; i += incr)
        accum += block.fn(i);
      return accum;
    })

    Handlebars.registerHelper('not', function (cond) {
      return !cond;
    })
    Handlebars.registerHelper('count', function (list) {
      return list.length;
    })
    Handlebars.registerHelper('countKeys', function (obj) {
      return Object.keys(obj).length;
    })

    Handlebars.registerHelper('isEnabled', function (configKey) {
      return game.settings.get("bol", configKey);
    })
    Handlebars.registerHelper('split', function (str, separator, keep) {
      return str.split(separator)[keep];
    })

    // If you need to add Handlebars helpers, here are a few useful examples:
    Handlebars.registerHelper('concat', function () {
      let outStr = '';
      for (let arg in arguments) {
        if (typeof arguments[arg] != 'object') {
          outStr += arguments[arg];
        }
      }
      return outStr;
    })

    Handlebars.registerHelper('add', function (a, b) {
      return parseInt(a) + parseInt(b);
    });
    Handlebars.registerHelper('mul', function (a, b) {
      return parseInt(a) * parseInt(b);
    })
    Handlebars.registerHelper('sub', function (a, b) {
      return parseInt(a) - parseInt(b);
    })
    Handlebars.registerHelper('abbrev2', function (a) {
      return a.substring(0, 2);
    })
    Handlebars.registerHelper('abbrev3', function (a) {
      return a.substring(0, 3);
    })
    Handlebars.registerHelper('valueAtIndex', function (arr, idx) {
      return arr[idx];
    })
    Handlebars.registerHelper('includesKey', function (items, type, key) {
      return items.filter(i => i.type === type).map(i => i.system.key).includes(key);
    })
    Handlebars.registerHelper('includes', function (array, val) {
      return array.includes(val);
    })
    Handlebars.registerHelper('eval', function (expr) {
      return eval(expr);
    })
    Handlebars.registerHelper('isOwnerOrGM', function (actor) {
      console.log("Testing actor", actor.isOwner, game.userId)
      return actor.isOwner || game.isGM;
    })
    Handlebars.registerHelper('upperCase', function (text) {
      if (typeof text !== 'string') return text
      return text.toUpperCase()
    })
    Handlebars.registerHelper('upperFirst', function (text) {
      if (typeof text !== 'string') return text
      return text.charAt(0).toUpperCase() + text.slice(1)
    })
    Handlebars.registerHelper('upperFirstOnly', function (text) {
      if (typeof text !== 'string') return text
      return text.charAt(0).toUpperCase()
    })

    // Handle v12 removal of this helper
    Handlebars.registerHelper('select', function (selected, options) {
      const escapedValue = RegExp.escape(Handlebars.escapeExpression(selected));
      const rgx = new RegExp(' value=[\"\']' + escapedValue + '[\"\']');
      const html = options.fn(this);
      return html.replace(rgx, "$& selected");
    });

  }

  static getLethargyDice(level) {
    for (let s of SYSTEM.SPELL_LETHARGY_DICE) {
      if (Number(level) <= s.maxLevel) {
        return s.dice
      }
    }
  }

  /* -------------------------------------------- */
  static async applyDamage(message, event) {
    // Récupérer les données du message
    let combatantId = event.currentTarget.dataset.combatantId
    if (!combatantId) {
      ui.notifications.error("No combatant selected")
      return
    }

    // Try to find the target: first as a combat combatant, then as a scene token
    let targetActor = null
    if (game.combat) {
      const combatant = game.combat.combatants.get(combatantId)
      if (combatant) {
        targetActor = combatant.token?.actor || game.actors.get(combatant.actorId)
      }
    }
    if (!targetActor) {
      // Fall back to scene token lookup (non-combat tokens use tokenId as their combatantId)
      const token = canvas.tokens?.placeables?.find(t => t.id === combatantId)
      targetActor = token?.actor
    }
    if (!targetActor) {
      ui.notifications.error("Target actor not found")
      return
    }

    // Récupérer les données de dégâts du message
    // Use options.rollTotal (includes weapon modifier bonus) rather than roll.total (dice formula only)
    let damageTotal = message.rolls[0]?.options?.rollTotal ?? message.rolls[0]?.total ?? 0
    let weaponName = message.rolls[0]?.options?.rollName || "Unknown Weapon"

    // Calculer les DR
    let armorDR = targetActor.computeDamageReduction() || 0
    let shieldDR = targetActor.getShieldDR() || 0
    let totalDR = armorDR + shieldDR

    // Créer le dialogue
    const content = await foundry.applications.handlebars.renderTemplate(
      "systems/fvtt-lethal-fantasy/templates/apply-damage-dialog.hbs",
      {
        targetName: targetActor.name,
        weaponName: weaponName,
        damageTotal: damageTotal,
        armorDR: armorDR,
        shieldDR: shieldDR,
        totalDR: totalDR,
        damageNoDR: damageTotal,
        damageWithArmor: Math.max(0, damageTotal - armorDR),
        damageWithAll: Math.max(0, damageTotal - totalDR)
      }
    )

    const result = await foundry.applications.api.DialogV2.wait({
      window: { title: "Apply Damage" },
      classes: ["lethalfantasy"],
      position: { width: 280 },
      content,
      buttons: [
        {
          action: "noDR",
          label: "No DR",
          callback: () => ({ drType: "none", damage: damageTotal })
        },
        {
          action: "armorDR",
          label: "With Armor DR",
          callback: () => ({ drType: "armor", damage: Math.max(0, damageTotal - armorDR) })
        },
        {
          action: "allDR",
          label: "With Armor + Shield DR",
          callback: () => ({ drType: "all", damage: Math.max(0, damageTotal - totalDR) })
        },
        {
          action: "cancel",
          label: "Cancel",
          callback: () => null
        }
      ],
      rejectClose: false
    })

    if (result && result.damage !== undefined) {
      await targetActor.applyDamage(-result.damage)

      // Message de confirmation
      let drText = ""
      if (result.drType === "armor") {
        drText = `Armor DR: ${armorDR}`
      } else if (result.drType === "all") {
        drText = `Total DR: ${totalDR}`
      }

      const messageContent = await foundry.applications.handlebars.renderTemplate(
        "systems/fvtt-lethal-fantasy/templates/damage-applied-message.hbs",
        {
          targetName: targetActor.name,
          damage: result.damage,
          drText: drText,
          weaponName: weaponName
        }
      )

      ChatMessage.create({
        user: game.user.id,
        speaker: { alias: targetActor.name },
        mode: "gm",
        content: messageContent
      })
    }
  }

}
