/* -------------------------------------------- */
import { WastelandCombat } from "./wasteland-combat.js";
import { WastelandCommands } from "./wasteland-commands.js";

/* -------------------------------------------- */
const __contrecouptCharme = {
  1 : {name: "Effet chromatique", description: "le corps du kobold prend des teintes aussi étranges que voyantes. L'effet s’estompe progressivement et 24 heures plus tard, le kobold retrouve ses couleurs d’origine." },
  3 : {name: "Enivrement Kobold", description: "très excité par son premier tour, le kobold doit immédiatement faire un autre tour, pour lequel il emploiera un dé plus gros." },
  5 : {name: "Mutisme superstitieux", description: "le kobold ne doit plus parler» pendant les prochaines 24 heures. S'il le fait malgré tout, les effets de son tour s’arrêtent." },
  7 : {name: "Agité!", description: "le kobold ne tient plus en place. Il ne peut se reposer pendant les prochaines 12 heures. Dès. que 12 heures se sont écoulées, il s'effondre comme une masse et dort 12 heures d'affilée d’un sommeil enchanté dont rien ne pourra le réveiller." },
  9 : {name: "Somnolence", description: "le kobold devient somnolent. Il n’arrive pas à se concentrer même sur une tâche simple, bäille sans arrêt, traîne les pieds et n’agit plus que de mauvaise grâce. Cela dure jusqu’à ce qu'il ait dormi au moins 12 heures." },
  11 : {name: "Manie incontrôlable", description: "le kobold est pris d’une manie incontrôlable. Dès qu'il voit un chapeau rouge, il doit suivre son porteur. Il ne mangera que si son voisin de gauche mange aussi, etc. Cela dure pendant une jour- née puis l’effet s’inverse pendant une heure : il ne suivra jamais un chapeau rouge, ne mangera jamais si son voi- sin de gauche mange, etc. Le contrecoup prend alors fin." },
  13 : {name: "Malédiction des Ternes", description: "le kobold perd cette qualité mystérieuse qui fait que les kobolds sont des kobolds et devient tout. Terne. Il perd 1d20 point(s) de Bonne Aventure (s’il doit en perdre plus qu'il n’en a, il tombe simplement à 0). Ces points perdus pourront cependant être regagnés normalement." },
  15 : {name: "La petite Mort", description: "le kobold s'endort pour 1420 heures. Rien ni personne ne pourra le tirer de ce sommeil enchanté avant que ce contrecoup ne prenne fin." },
  17 : {name: "Angoisse cauchemardesque", description: "le kobold a une brève vision de pure horreur. Il perd 1420 points de Psyché {s'il doit en perdre plus qu'il n’en a, il tombe à 0)." },
  19 : {name: "Anémie Kobold", description: "le kobold se met à saigner du nez, des oreilles et même d’autres endroits. Il perd 1420 point(s) de Santé." }
}

/* -------------------------------------------- */
export class WastelandUtility {


  /* -------------------------------------------- */
  static async init() {
    Hooks.on('renderChatLog', (log, html, data) => WastelandUtility.chatListeners(html))
    Hooks.on('renderChatMessageHTML', (message, html, data) => WastelandUtility.chatListeners(html))
    Hooks.on("getChatMessageContextOptions", (html, options) => WastelandUtility.chatRollMenu(html, options))

    Hooks.on("getCombatTrackerEntryContext", (html, options) => {
      WastelandUtility.pushInitiativeOptions(html, options);
    })

    this.rollDataStore = {}
    this.defenderStore = {}
    WastelandCommands.init();

    Handlebars.registerHelper('count', function (list) {
      return list.length;
    })
    Handlebars.registerHelper('includes', function (array, val) {
      return array.includes(val);
    })
    Handlebars.registerHelper('upper', function (text) {
      return text.toUpperCase();
    })
    Handlebars.registerHelper('lower', function (text) {
      return text.toLowerCase()
    })
    Handlebars.registerHelper('upperFirst', function (text) {
      if (typeof text !== 'string') return text
      return text.charAt(0).toUpperCase() + text.slice(1)
    })
    Handlebars.registerHelper('notEmpty', function (list) {
      return list.length > 0;
    })
    Handlebars.registerHelper('mul', function (a, b) {
      return parseInt(a) * parseInt(b);
    })

  }
  /* -------------------------------------------- */
  static getActorFromRollData(rollData) {
    let actor = game.actors.get(rollData.actorId)
    if (rollData.tokenId) {
      let token = canvas.tokens.placeables.find(t => t.id == rollData.tokenId)
      if (token) {
        actor = token.actor
      }
    }
    return actor
  }

  /* -------------------------------------------- */
  static getModificateurOptions() {
    let opt = []
    for (let i = -15; i <= 15; i++) {
      opt.push(`<option value="${i}">${i}</option>`)
    }
    return opt.concat("\n")
  }

  /* -------------------------------------------- */
  static sortArrayObjectsByName(myArray) {
    myArray.sort((a, b) => {
      return a.name.localeCompare(b.name);
    })
  }

  /* -------------------------------------------- */
  static getPointAmeOptions() {
    let opt = []
    for (let i = 1; i <= 20; i++) {
      opt.push(`<option value="${i}">${i}</option>`)
    }
    return opt.concat("\n")
  }

  /* -------------------------------------------- */
  static getAttributs() {
    return { adr: "Adresse", pui: "Puissance", cla: "Clairvoyance", pre: "Présence", tre: "Trempe" }
  }
  /* -------------------------------------------- */
  static pushInitiativeOptions(html, options) {
  }

  /* -------------------------------------------- */
  static getSkills() {
    return this.skills
  }

  /* -------------------------------------------- */
  static async ready() {
    const skills = await WastelandUtility.loadCompendium("fvtt-wasteland.skills")
    this.skills = skills.map(i => i.toObject())

    // Note: listeNiveauSkill, listeNiveauCreature, etc. are now created in init hook
    // to be available immediately when sheets are opened
  }

  /* -------------------------------------------- */
  static createDirectOptionList(min, max) {
    let options = {};
    for (let i = min; i <= max; i++) {
      options[`${i}`] = `${i}`;
    }
    return options;
  }
  static createArrayOptionList(min, max) {
    let options = [];
    for (let i = min; i <= max; i++) {
      options.push({key:`${i}`, label:`${i}`});
    }
    return options;
  }

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

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

  /* -------------------------------------------- */
  static getOptionsStatusList() {
    return this.optionsStatusList;
  }
  /* -------------------------------------------- */
  static async chatListeners(html) {

    $(html).on("click", '.predilection-reroll', async event => {
      let predIdx = $(event.currentTarget).data("predilection-index")
      let messageId = WastelandUtility.findChatMessageId(event.currentTarget)
      let message = game.messages.get(messageId)
      let rollData = message.getFlag("world", "wasteland-roll")
      let actor = WastelandUtility.getActorFromRollData(rollData)
      
      // Sauvegarder le résultat précédent
      let previousResult = {
        diceResult: rollData.diceResult,
        finalResult: rollData.finalResult,
        diceFormula: rollData.diceFormula
      }
      
      // Récupérer la prédilection utilisée
      let predilectionUsed = foundry.utils.duplicate(rollData.competence.system.predilections[predIdx])
      
      // Marquer la prédilection comme utilisée
      await actor.setPredilectionUsed(rollData.competence._id, predIdx)
      
      // Mettre à jour la compétence dans rollData
      rollData.competence = foundry.utils.duplicate(actor.getCompetence(rollData.competence._id))
      
      // Lancer le reroll avec prédilection
      await WastelandUtility.rollWastelandPredilection(rollData, predilectionUsed, previousResult)
    })

    $(html).on("click", '.arme-roll-degats', async event => {
      let messageId = WastelandUtility.findChatMessageId(event.currentTarget)
      let message = game.messages.get(messageId)
      let rollData = message.getFlag("world", "wasteland-roll")
      if (rollData && rollData.arme) {
        let actor = WastelandUtility.getActorFromRollData(rollData)
        await actor.rollArmeDegats(rollData.arme._id)
      }
    })
  }

  /* -------------------------------------------- */
  static async preloadHandlebarsTemplates() {

    const templatePaths = [
      'systems/fvtt-wasteland/templates/editor-notes-gm.hbs',
      'systems/fvtt-wasteland/templates/partial-item-description.hbs',
      'systems/fvtt-wasteland/templates/partial-item-header.hbs',
      'systems/fvtt-wasteland/templates/partial-item-nav.hbs'
    ]
    return foundry.applications.handlebars.loadTemplates(templatePaths);
  }

  /* -------------------------------------------- */
  static removeChatMessageId(messageId) {
    if (messageId) {
      game.messages.get(messageId)?.delete();
    }
  }

  static findChatMessageId(current) {
    return WastelandUtility.getChatMessageId(WastelandUtility.findChatMessage(current));
  }

  static getChatMessageId(node) {
    return node?.attributes.getNamedItem('data-message-id')?.value;
  }

  static findChatMessage(current) {
    return WastelandUtility.findNodeMatching(current, it => it.classList.contains('chat-message') && it.attributes.getNamedItem('data-message-id'))
  }

  static findNodeMatching(current, predicate) {
    if (current) {
      if (predicate(current)) {
        return current;
      }
      return WastelandUtility.findNodeMatching(current.parentElement, predicate);
    }
    return undefined;
  }

  /* -------------------------------------------- */
  static buildListOptions(min, max) {
    let options = ""
    for (let i = min; i <= max; i++) {
      options += `<option value="${i}">${i}</option>`
    }
    return options;
  }

  /* -------------------------------------------- */
  static getTarget() {
    if (game.user.targets && game.user.targets.size == 1) {
      for (let target of game.user.targets) {
        return target;
      }
    }
    return undefined;
  }

  /* -------------------------------------------- */
  static updateRollData(rollData) {

    let id = rollData.rollId;
    let oldRollData = this.rollDataStore[id] || {};
    let newRollData = foundry.utils.mergeObject(oldRollData, rollData);
    this.rollDataStore[id] = newRollData;
  }
  /* -------------------------------------------- */
  static saveRollData(rollData) {
    game.socket.emit("system.fvtt-wasteland", {
      name: "msg_update_roll", data: rollData
    }); // Notify all other clients of the roll
    this.updateRollData(rollData);
  }

  /* -------------------------------------------- */
  static getRollData(id) {
    return this.rollDataStore[id];
  }

  /* -------------------------------------------- */
  static onSocketMesssage(msg) {
    if (msg.name == "msg_update_defense_state") {
      this.updateDefenseState(msg.data.defenderId, msg.data.rollId);
    }
    if (msg.name == "msg_update_roll") {
      this.updateRollData(msg.data);
    }
  }

  /* -------------------------------------------- */
  static chatDataSetup(content, modeOverride, isRoll = false, forceWhisper) {
    let chatData = {
      user: game.user.id,
      rollMode: modeOverride || game.settings.get("core", "rollMode"),
      content: content
    };

    if (["gmroll", "blindroll"].includes(chatData.rollMode)) chatData["whisper"] = ChatMessage.getWhisperRecipients("GM").map(u => u.id);
    if (chatData.rollMode === "blindroll") chatData["blind"] = true;
    else if (chatData.rollMode === "selfroll") chatData["whisper"] = [game.user];

    if (forceWhisper) { // Final force !
      chatData["speaker"] = ChatMessage.getSpeaker();
      chatData["whisper"] = ChatMessage.getWhisperRecipients(forceWhisper);
    }

    return chatData;
  }

  /* -------------------------------------------- */
  static async showDiceSoNice(roll, rollMode) {
    if (game.modules.get("dice-so-nice")?.active) {
      if (game.dice3d) {
        let whisper = null;
        let blind = false;
        rollMode = rollMode ?? game.settings.get("core", "rollMode");
        switch (rollMode) {
          case "blindroll": //GM only
            blind = true;
          case "gmroll": //GM + rolling player
            whisper = this.getUsers(user => user.isGM);
            break;
          case "roll": //everybody
            whisper = this.getUsers(user => user.active);
            break;
          case "selfroll":
            whisper = [game.user.id];
            break;
        }
        await game.dice3d.showForRoll(roll, game.user, true, whisper, blind);
      }
    }
  }

  /* -------------------------------------------- */
  static async computeResult(rollData, actor) {
    if (rollData.charme) {
      let resultIndex = false
      let resTab = foundry.utils.duplicate(rollData.charme.system.resultats)
      for(let id in resTab) {
        let res = resTab[id]
        if (!resultIndex && rollData.finalResult >= res.value) {
          resultIndex = id;
        }
      }
      if (resultIndex) {
        rollData.charmeDuree = rollData.charme.system.resultats[resultIndex].description
      }
      let effectRoll = await new Roll(rollData.charmeDice).roll()
      if (rollData.charme.system.charmetype == "tour") {
        rollData.contrecoupResult = effectRoll.total
        if (rollData.contrecoupResult % 2 == 1) {
          rollData.contrecoup = __contrecouptCharme[rollData.contrecoupResult]
        }
      }
      if (rollData.charme.system.charmetype == "charme") {
        rollData.charmeSante = effectRoll.total
        actor.incDecSante(rollData.charmeSante)
      }
    } else {
      if (rollData.mainDice == "1d20") {
        let diceValue = rollData.roll.terms[0].results[0].result
        diceValue *= (rollData.doubleD20) ? 2 : 1
        //console.log("PAIR/IMP", diceValue)
        if (diceValue % 2 == 1) {
          //console.log("PAIR/IMP2", diceValue)
          rollData.d20IsOdd = true
          rollData.finalResult -= rollData.roll.terms[0].results[0].result // Substract value
          if (diceValue == 1 || diceValue == 11) {
            rollData.isDramatique = true
            rollData.isSuccess = false
          }
        }
      }

      //console.log("Result : ", rollData)
      if (rollData.difficulte > 0 && !rollData.isDramatique) {
        rollData.isSuccess = (rollData.finalResult >= rollData.difficulte)
        rollData.isHeroique = ((rollData.finalResult - rollData.difficulte) >= 10)
        rollData.isDramatique = ((rollData.finalResult - rollData.difficulte) <= -10)
      }
    }
  }

  /* -------------------------------------------- */
  static async rollWasteland(rollData) {

    let actor = WastelandUtility.getActorFromRollData(rollData)
    if (rollData.attrKey == "tochoose") { // No attr selected, force address
      rollData.attrKey = "adr"
    }
    if (!rollData.attr && actor && actor.system.attributs && actor.system.attributs[rollData.attrKey]) {
      rollData.attr = foundry.utils.duplicate(actor.system.attributs[rollData.attrKey])
      if (rollData.attr.labelnorm) {
        rollData.actionImg = "systems/fvtt-wasteland/assets/icons/" + rollData.attr.labelnorm + ".webp"
      }
    }

    if (rollData.charme) {
      rollData.diceFormula = rollData.charmeDice
    } else {
      rollData.diceFormula = rollData.mainDice
      // Double D20 ne s'applique que si on lance un D20, pas un D10
      if (rollData.doubleD20 && rollData.mainDice === "1d20") {
        rollData.diceFormula += "*2"
        if (!rollData.isReroll) {
          actor.changeEclat(-1)
        }
      }
    }

    //console.log("BEFORE COMP", rollData)
    if (rollData.competence) {
      rollData.predilections = foundry.utils.duplicate(rollData.competence.system.predilections.filter(pred => !pred.used) || [])
      let compmod = (rollData.competence.system.niveau == 0) ? -3 : 0
      let attrValue = rollData.attr?.value || 0
      rollData.diceFormula += `+${attrValue}+${rollData.competence.system.niveau}+${rollData.modificateur}+${compmod}`
    } else {
      let attrValue = rollData.attr?.value || 0
      rollData.diceFormula += `+${attrValue}*2+${rollData.modificateur}`
    }
    if (rollData.arme && rollData.arme.type == "arme") {
      rollData.diceFormula += `+${rollData.arme.system.bonusmaniementoff}`
    }

    // Apply desavantages (tactical advantages)
    let desavantagesBonus = 0
    if (rollData.desavantages) {
      for (let desavantage in rollData.desavantages) {
        if (rollData.desavantages[desavantage]) {
          desavantagesBonus += 5
        }
      }
      desavantagesBonus = Math.min(15, desavantagesBonus)
      if (desavantagesBonus > 0) {
        rollData.diceFormula += `+${desavantagesBonus}`
      }
    }

    // Monté ?
    if (rollData.isMonte) {
      rollData.diceFormula += "+5"
    }

    // Specific modifiers for ranged combat
    if (rollData.arme?.system?.isDistance) {
      if (rollData.visee) {
        rollData.diceFormula += "+5"
      }
      if (rollData.cibleconsciente && rollData.defender) {
        rollData.diceFormula += `-${rollData.defender.system.attributs.adr.value}`
      }
      if (rollData.ciblecourt) {
        if (rollData.difficulte <= 15) { // Portée courte ou moins
          rollData.diceFormula += `-5`
        } else {
          rollData.diceFormula += `-10`
        }
      }
      if (rollData.typeCouvert && rollData.typeCouvert != "aucun" && rollData.config.couverts[rollData.typeCouvert]) {
        rollData.diceFormula += `+${rollData.config.couverts[rollData.typeCouvert].value}`
      }
    }

    let myRoll = await new Roll(rollData.diceFormula).roll()
    await this.showDiceSoNice(myRoll, game.settings.get("core", "rollMode"))
    rollData.roll = foundry.utils.duplicate(myRoll)
    rollData.diceResult = myRoll.terms[0].results[0].result
    console.log(">>>> ", myRoll)

    rollData.finalResult = myRoll.total
    await this.computeResult(rollData, actor)

    this.createChatWithRollMode(rollData.alias, {
      content: await foundry.applications.handlebars.renderTemplate(`systems/fvtt-wasteland/templates/chat-generic-result-v2.hbs`, rollData)
    }, rollData)

  }

  /* -------------------------------------------- */
  static async bonusRollWasteland(rollData) {
    rollData.bonusFormula = rollData.addedBonus

    let bonusRoll = await new Roll(rollData.bonusFormula).roll()
    await this.showDiceSoNice(bonusRoll, game.settings.get("core", "rollMode"));
    rollData.bonusRoll = foundry.utils.duplicate(bonusRoll)

    rollData.finalResult += rollData.bonusRoll.total

    await this.computeResult(rollData)

    this.createChatWithRollMode(rollData.alias, {
      content: await foundry.applications.handlebars.renderTemplate(`systems/fvtt-wasteland/templates/chat-generic-result-v2.hbs`, rollData)
    }, rollData)

  }

  /* -------------------------------------------- */
  static async rollWastelandPredilection(rollData, predilectionUsed, previousResult) {
    // Sauvegarder le premier résultat
    rollData.firstRoll = {
      diceResult: previousResult.diceResult,
      finalResult: previousResult.finalResult,
      diceFormula: previousResult.diceFormula
    }
    rollData.predilectionUsed = predilectionUsed
    rollData.isPredilectionReroll = true

    let actor = WastelandUtility.getActorFromRollData(rollData)
    if (rollData.attrKey == "tochoose") {
      rollData.attrKey = "adr"
    }
    if (!rollData.attr && actor && actor.system.attributs && actor.system.attributs[rollData.attrKey]) {
      rollData.attr = foundry.utils.duplicate(actor.system.attributs[rollData.attrKey])
      if (rollData.attr.labelnorm) {
        rollData.actionImg = "systems/fvtt-wasteland/assets/icons/" + rollData.attr.labelnorm + ".webp"
      }
    }

    if (rollData.charme) {
      rollData.diceFormula = rollData.charmeDice
    } else {
      rollData.diceFormula = rollData.mainDice
      // Double D20 ne s'applique que si on lance un D20, pas un D10
      if (rollData.doubleD20 && rollData.mainDice === "1d20") {
        rollData.diceFormula += "*2"
      }
    }

    if (rollData.competence) {
      rollData.predilections = foundry.utils.duplicate(rollData.competence.system.predilections.filter(pred => !pred.used) || [])
      let compmod = (rollData.competence.system.niveau == 0) ? -3 : 0
      let attrValue = rollData.attr?.value || 0
      rollData.diceFormula += `+${attrValue}+${rollData.competence.system.niveau}+${rollData.modificateur}+${compmod}`
    } else {
      let attrValue = rollData.attr?.value || 0
      rollData.diceFormula += `+${attrValue}*2+${rollData.modificateur}`
    }
    if (rollData.arme && rollData.arme.type == "arme") {
      rollData.diceFormula += `+${rollData.arme.system.bonusmaniementoff}`
    }

    // Apply desavantages (tactical advantages)
    if (rollData.desavantages) {
      let totalDesavantage = 0
      for (let key in rollData.desavantages) {
        if (rollData.desavantages[key]) {
          totalDesavantage += 5
        }
      }
      totalDesavantage = Math.min(totalDesavantage, 15)
      if (totalDesavantage > 0) {
        rollData.diceFormula += `+${totalDesavantage}`
      }
    }

    // Apply mounted bonus
    if (rollData.isMonte) {
      rollData.diceFormula += `+5`
    }

    // Apply ranged combat modifiers
    if (rollData.arme && rollData.arme.system && rollData.arme.system.isDistance) {
      if (rollData.visee) {
        rollData.diceFormula += `+5`
      }
      if (rollData.cibleconsciente && rollData.defender) {
        let adrMalus = -(rollData.defender.system?.attributs?.adr?.value || 0)
        rollData.diceFormula += `${adrMalus}`
      }
      if (rollData.ciblecourt) {
        if (rollData.portee === "portee-courte" || rollData.portee === "portee-moyenne") {
          rollData.diceFormula += `-5`
        } else {
          rollData.diceFormula += `-10`
        }
      }
      if (rollData.couvert && rollData.typeCouvertValue) {
        rollData.diceFormula += `${rollData.typeCouvertValue}`
      }
    }

    // Nouveau jet de dé
    let myRoll = await new Roll(rollData.diceFormula).roll()
    await this.showDiceSoNice(myRoll, game.settings.get("core", "rollMode"))
    rollData.roll = foundry.utils.duplicate(myRoll)
    rollData.diceResult = myRoll.total

    rollData.bonusFormula = "0"
    if (rollData.charme && rollData.charme.system.bonusmajeur > 0) {
      rollData.bonusFormula = `${rollData.charme.system.bonusmajeur}d6`
      rollData.textBonus = "Bonus de Charme"
    }

    rollData.finalResult = rollData.diceResult
    let bonusRoll = await new Roll(rollData.bonusFormula).roll()
    await this.showDiceSoNice(bonusRoll, game.settings.get("core", "rollMode"))
    rollData.bonusRoll = foundry.utils.duplicate(bonusRoll)
    rollData.finalResult += rollData.bonusRoll.total

    // Comparer avec le premier jet et garder le meilleur
    rollData.secondRoll = {
      diceResult: rollData.diceResult,
      finalResult: rollData.finalResult
    }

    if (rollData.firstRoll.finalResult > rollData.finalResult) {
      // Garder le premier résultat
      rollData.diceResult = rollData.firstRoll.diceResult
      rollData.finalResult = rollData.firstRoll.finalResult
      rollData.keptRoll = "first"
    } else {
      // Garder le second résultat
      rollData.keptRoll = "second"
    }

    await this.computeResult(rollData)

    this.createChatWithRollMode(rollData.alias, {
      content: await foundry.applications.handlebars.renderTemplate(`systems/fvtt-wasteland/templates/chat-generic-result-v2.hbs`, rollData)
    }, rollData)
  }

  /* -------------------------------------------- */
  static getUsers(filter) {
    return game.users.filter(filter).map(user => user.id);
  }

  /* -------------------------------------------- */
  static getWhisperRecipients(rollMode, name) {
    switch (rollMode) {
      case "blindroll": return this.getUsers(user => user.isGM);
      case "gmroll": return this.getWhisperRecipientsAndGMs(name);
      case "selfroll": return [game.user.id];
    }
    return undefined;
  }
  /* -------------------------------------------- */
  static getWhisperRecipientsAndGMs(name) {
    let recep1 = ChatMessage.getWhisperRecipients(name) || [];
    return recep1.concat(ChatMessage.getWhisperRecipients('GM'));
  }

  /* -------------------------------------------- */
  static blindMessageToGM(chatOptions) {
    let chatGM = foundry.utils.duplicate(chatOptions);
    chatGM.whisper = this.getUsers(user => user.isGM);
    chatGM.content = "Blinde message of " + game.user.name + "<br>" + chatOptions.content;
    console.log("blindMessageToGM", chatGM);
    game.socket.emit("system.fvtt-weapons-of-the-gods", { msg: "msg_gm_chat_message", data: chatGM });
  }

  /* -------------------------------------------- */
  static async searchItem(dataItem) {
    let item;
    if (dataItem.pack) {
      item = await fromUuid("Compendium." + dataItem.pack + "." + dataItem.id);
    } else {
      item = game.items.get(dataItem.id)
    }
    return item
  }

  /* -------------------------------------------- */
  static split3Columns(data) {

    let array = [[], [], []];
    if (data == undefined) return array;

    let col = 0;
    for (let key in data) {
      let keyword = data[key];
      keyword.key = key; // Self-reference
      array[col].push(keyword);
      col++;
      if (col == 3) col = 0;
    }
    return array;
  }

  /* -------------------------------------------- */
  static async createChatMessage(name, rollMode, chatOptions, rollData = undefined) {
    switch (rollMode) {
      case "blindroll": // GM only
        if (!game.user.isGM) {
          this.blindMessageToGM(chatOptions);

          chatOptions.whisper = [game.user.id];
          chatOptions.content = "Message only to the GM";
        }
        else {
          chatOptions.whisper = this.getUsers(user => user.isGM);
        }
        break;
      default:
        chatOptions.whisper = this.getWhisperRecipients(rollMode, name);
        break;
    }
    chatOptions.alias = chatOptions.alias || name
    let msg = await ChatMessage.create(chatOptions)
    console.log("=======>", rollData)
    msg.setFlag("world", "wasteland-roll", rollData)
  }

  /* -------------------------------------------- */
  static getBasicRollData() {
    let rollData = {
      rollId: foundry.utils.randomID(16),
      rollMode: game.settings.get("core", "rollMode"),
      modificateursOptions: this.getModificateurOptions(),
      pointAmeOptions: this.getPointAmeOptions(),
      difficulte: 0,
      modificateur: 0,
      config: game.system.wasteland.config,
    }
    WastelandUtility.updateWithTarget(rollData)
    return rollData
  }

  /* -------------------------------------------- */
  static updateWithTarget(rollData) {
    let target = WastelandUtility.getTarget()
    if (target) {
      rollData.defenderTokenId = target.id
      let defender = game.canvas.tokens.get(rollData.defenderTokenId).actor
      rollData.defender = defender.toObject() // For template access
      rollData.armeDefense = defender.getBestDefenseValue()
      if (rollData.armeDefense) {
        rollData.difficulte = rollData.armeDefense.system.totalDefensif
      } else {
        ui.notifications.warn("Aucune arme de défense équipée, difficulté manuelle à positionner.")
      }
    }
  }

  /* -------------------------------------------- */
  static createChatWithRollMode(name, chatOptions, rollData = undefined) {
    this.createChatMessage(name, game.settings.get("core", "rollMode"), chatOptions, rollData)
  }

  /* -------------------------------------------- */
  static applyBonneAventureRoll(li, changed, addedBonus) {
    let msgId = $(li).data("message-id")
    let msg = game.messages.get(msgId)
    if (msg) {
      let rollData = msg.getFlag("world", "wasteland-roll")
      let actor = WastelandUtility.getActorFromRollData(rollData)
      actor.changeBonneAventure(changed)
      rollData.isReroll = true
      rollData.textBonus = "Bonus de Points d'Aventure"
      if (addedBonus == "reroll") {
        WastelandUtility.rollWasteland(rollData)
      } else {
        rollData.addedBonus = addedBonus
        WastelandUtility.bonusRollWasteland(rollData)
      }
    }
  }

  /* -------------------------------------------- */
  static applyEclatRoll(li, changed, addedBonus) {
    let msgId = $(li).data("message-id")
    let msg = game.messages.get(msgId)
    if (msg) {
      let rollData = msg.getFlag("world", "wasteland-roll")
      let actor = WastelandUtility.getActorFromRollData(rollData)
      actor.changeEclat(changed)
      rollData.isReroll = true
      rollData.textBonus = "Bonus d'Eclat"
      rollData.addedBonus = addedBonus
      WastelandUtility.bonusRollWasteland(rollData)
    }
  }

  /* -------------------------------------------- */
  static cancelBlessure(li) {
    let msgId = $(li).data("message-id")
    let msg = game.messages.get(msgId)
    if (msg) {
      let rollData = msg.getFlag("world", "wasteland-roll")
      let actor = WastelandUtility.getActorFromRollData(rollData)
      
      // Vérifier que l'acteur a au moins 1 point d'éclat
      if (actor.getEclat() < 1) {
        ui.notifications.warn("Pas assez de points d'Éclat")
        return
      }
      
      // Déduire 1 point d'éclat
      actor.changeEclat(-1)
      
      // Annuler la dernière blessure (incrémenter la santé)
      let currentSante = actor.system.sante.value
      let maxSante = actor.system.sante.max
      if (currentSante < maxSante) {
        actor.update({ 'system.sante.value': currentSante + 1 })
        ui.notifications.info("Blessure annulée - 1 Point d'Éclat dépensé")
      } else {
        ui.notifications.warn("Vous êtes déjà en pleine santé")
        // Rembourser le point d'éclat
        actor.changeEclat(1)
      }
    }
  }

  /* -------------------------------------------- */
  static reloadBA(li) {
    let msgId = $(li).data("message-id")
    let msg = game.messages.get(msgId)
    if (msg) {
      let rollData = msg.getFlag("world", "wasteland-roll")
      let actor = WastelandUtility.getActorFromRollData(rollData)
      
      // Vérifier que l'acteur a au moins 1 point d'éclat
      if (actor.getEclat() < 1) {
        ui.notifications.warn("Pas assez de points d'Éclat")
        return
      }
      
      // Déduire 1 point d'éclat
      actor.changeEclat(-1)
      
      // Recharger les points de Bonne Aventure au maximum
      let maxBA = actor.system.bonneaventure.base
      actor.update({ 'system.bonneaventure.actuelle': maxBA })
      ui.notifications.info(`Points de Bonne Aventure rechargés à ${maxBA} - 1 Point d'Éclat dépensé`)
    }
  }

  /* -------------------------------------------- */
  static incDecSante(li, value, costBA) {
    let msgId = $(li).data("message-id")
    let msg = game.messages.get(msgId)
    if (msg) {
      let rollData = msg.getFlag("world", "wasteland-roll")
      let actor = WastelandUtility.getActorFromRollData(rollData)
      
      // Vérifier que l'acteur a assez de points de Bonne Aventure
      if (actor.getBonneAventure() < costBA) {
        ui.notifications.warn("Pas assez de points de Bonne Aventure")
        return
      }
      
      // Déduire les points de Bonne Aventure
      actor.changeBonneAventure(-costBA)
      
      // Incrémenter la santé
      actor.incDecSante(value)
      
      ui.notifications.info(`+${value} Point(s) de Santé - ${costBA} Point(s) de Bonne Aventure dépensé(s)`)
    }
  }

  /* -------------------------------------------- */
  static chatRollMenu(html, options) {
    let canApply = li => canvas.tokens.controlled.length && li.find(".wasteland-roll").length
    let hasBA = function (li) {
      let message = game.messages.get($(li).attr("data-message-id"))
      let rollData = message.getFlag("world", "wasteland-roll")
      if (rollData?.actorId) {
        let actor = game.actors.get(rollData.actorId)
        return actor.getBonneAventure() > 0

      }
      return false
    }
    let hasBA2 = function (li) {
      let message = game.messages.get($(li).attr("data-message-id"))
      let rollData = message.getFlag("world", "wasteland-roll")
      if (rollData?.actorId) {
        let actor = game.actors.get(rollData.actorId)
        return actor.getBonneAventure() >= 2

      }
      return false
    }
    let hasBA3 = function (li) {
      let message = game.messages.get($(li).attr("data-message-id"))
      let rollData = message.getFlag("world", "wasteland-roll")
      if (rollData?.actorId) {
        let actor = game.actors.get(rollData.actorId)
        return actor.getBonneAventure() >= 3

      }
      return false
    }
    let hasPE = function (li) {
      let message = game.messages.get($(li).attr("data-message-id"))
      let rollData = message.getFlag("world", "wasteland-roll")
      if (rollData?.actorId) {
        let actor = game.actors.get(rollData.actorId)
        return actor.getEclat() >= 1

      }
      return false
    }
    let hasPredilection = function (li) {
      let message = game.messages.get($(li).attr("data-message-id"))
      let rollData = message.getFlag("world", "wasteland-roll")
      if (rollData.competence) {
        let nbPred = rollData.competence.system.predilections.filter(pred => !pred.used).length
        return (!rollData.isReroll && rollData.competence && nbPred > 0)
      }
      return false
    }
    let canCompetenceDouble = function (li) {
      let message = game.messages.get($(li).attr("data-message-id"))
      let rollData = message.getFlag("world", "wasteland-roll")
      if (rollData.competence) {
        return rollData.competence.system.doublebonus
      }
      return false
    }
    options.push(
      {
        name: "Ajouter +3 (1 point de Bonne Aventure)",
        icon: "<i class='fas fa-user-plus'></i>",
        condition: canApply && hasBA,
        callback: li => WastelandUtility.applyBonneAventureRoll(li, -1, "+3")
      }
    )
    options.push(
      {
        name: "Gain de 1 Point de Santé / 24 heure (1 point de Bonne Aventure)",
        icon: "<i class='fas fa-user-plus'></i>",
        condition: canApply && hasBA,
        callback: li => WastelandUtility.incDecSante(li, 1, 1)
      }
    )
    options.push(
      {
        name: "Gain de 2 Points de Santé / 24 heure (2 points de Bonne Aventure)",
        icon: "<i class='fas fa-user-plus'></i>",
        condition: canApply && hasBA2,
        callback: li => WastelandUtility.incDecSante(li, 2, 2)
      }
    )
    options.push(
      {
        name: "Relancer le jet (3 points de Bonne Aventure)",
        icon: "<i class='fas fa-user-plus'></i>",
        condition: canApply && hasBA3,
        callback: li => WastelandUtility.applyBonneAventureRoll(li, -3, "reroll")
      }
    )
    options.push(
      {
        name: "Bénéficier d'1 action supplémentaire (3 points de Bonne Aventure)",
        icon: "<i class='fas fa-user-plus'></i>",
        condition: canApply && hasBA3,
        callback: li => WastelandUtility.applyBonneAventureRoll(li, -3, "newaction")
      }
    )
    options.push(
      {
        name: "Ajouter +10 (1 Point d'Eclat)",
        icon: "<i class='fas fa-user-plus'></i>",
        condition: canApply && hasPE,
        callback: li => WastelandUtility.applyEclatRoll(li, -1, "+10")
      }
    )
    options.push(
      {
        name: "Double le résultat du d20 (1 Point d'Eclat)",
        icon: "<i class='fas fa-user-plus'></i>",
        condition: canApply && hasPE,
        callback: li => WastelandUtility.applyEclatRoll(li, -1, "double20")
      }
    )
    options.push(
      {
        name: "Annuler une blessure (1 Point d'Eclat)",
        icon: "<i class='fas fa-user-plus'></i>",
        condition: canApply && hasPE,
        callback: li => WastelandUtility.cancelBlessure(li)
      }
    )
    options.push(
      {
        name: "Recharger ses points de BA (1 Point d'Eclat)",
        icon: "<i class='fas fa-user-plus'></i>",
        condition: canApply && hasPE,
        callback: li => WastelandUtility.reloadBA(li)
      }
    )
    return options
  }

  /* -------------------------------------------- */
  static async confirmDelete(actorSheet, li) {
    const itemId = li.dataset.itemId
    if (!itemId) return
    
    const item = actorSheet.document.items.get(itemId)
    if (!item) return
    
    const confirmed = await foundry.applications.api.DialogV2.confirm({
      window: { title: "Confirmer la suppression" },
      content: `<p>Êtes-vous sûr de vouloir supprimer <strong>${item.name}</strong> ?</p>`,
      rejectClose: false,
      modal: true,
      yes: { label: "Supprimer", icon: "fa-trash" },
      no: { label: "Annuler", icon: "fa-times" }
    })
    
    if (confirmed) {
      await actorSheet.document.deleteEmbeddedDocuments("Item", [itemId])
    }
  }

}