
import CthulhuEternalRoll from "./documents/roll.mjs"
import { SystemManager } from './applications/hud/system-manager.js'
import { SYSTEM } from "./config/system.mjs"

export default class CthulhuEternalUtils {

  static registerSettings() {
    game.settings.register("fvtt-cthulhu-eternal", "settings-era", {
      name: game.i18n.localize("CTHULHUETERNAL.Settings.era"),
      hint: game.i18n.localize("CTHULHUETERNAL.Settings.eraHint"),
      default: "jazz",
      scope: "world",
      type: String,
      choices: SYSTEM.AVAILABLE_SETTINGS,
      config: true,
      onChange: _ => window.location.reload()
    });
    game.settings.register("fvtt-cthulhu-eternal", "settings-nudge", {
      name: game.i18n.localize("CTHULHUETERNAL.Settings.nudge"),
      hint: game.i18n.localize("CTHULHUETERNAL.Settings.nudgeHint"),
      default: true,
      scope: "world",
      type: Boolean,
      config: true
    });
    game.settings.register("fvtt-cthulhu-eternal", "roll-opposed-store", {
      name: "Roll Opposed Store",
      hint: "Whether to store opposed roll results for later use",
      default: { roll1: null, roll2: null },
      scope: "world",
      type: Object,
      config: false
    });
  }

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

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

  static registerHandlebarsHelpers() {

    Handlebars.registerHelper('isNull', function (val) {
      return val == null;
    });

    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('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) {
      return actor.isOwner || game.isGM;
    })
    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()
    })
    Handlebars.registerHelper('isCreature', function (key) {
      return key === "creature" || key === "daemon";
    })

    // 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 removeChatMessageId(messageId) {
    if (messageId) {
      game.messages.get(messageId)?.delete();
    }
  }

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

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

  static findChatMessage(current) {
    return HawkmoonUtility.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 HawkmoonUtility.findNodeMatching(current.parentElement, predicate);
    }
    return undefined;
  }

  /* -------------------------------------------- */
  static getWeaponSkill(actor, weapon, era) {
    let skill
    if (weapon.system.hasDirectSkill) {
      let skillName = weapon.name
      skill = { type: "skill", name: skillName, system: { base: 0, bonus: weapon.system.directSkillValue, skillTotal: weapon.system.directSkillValue } }
    } else {
      let skillName = game.i18n.localize(SYSTEM.WEAPON_SKILL_MAPPING[era][weapon.system.weaponType])
      skill = actor.items.find(i => i.type === "skill" && i.name.toLowerCase() === skillName.toLowerCase())
      if (!skill) {
        ui.notifications.error(game.i18n.localize("CTHULHUETERNAL.Notifications.NoWeaponSkill"))
        return
      }
    }
    return skill
  }

  /* -------------------------------------------- */
  static async applySANType(rollMessage, event) {
    let rollData = rollMessage.getFlag("fvtt-cthulhu-eternal", "rollData")
    if (!rollData) {
      ui.notifications.error(game.i18n.localize("CTHULHUETERNAL.Notifications.noRollDataFound"))
      return
    }
    let actor = game.actors.get(rollData.actorId)
    if (!actor) {
      ui.notifications.error(game.i18n.localize("CTHULHUETERNAL.Notifications.noActorFound"))
      return
    }
    let sanType = event.currentTarget.dataset.sanType;
    if (!sanType) {
      ui.notifications.error(game.i18n.localize("CTHULHUETERNAL.Notifications.noSanTypeFound"))
      return
    }
    rollData.sanType = sanType
    await actor.system.applySANConsequences(rollData)
    // Delete the roll message
    await rollMessage.delete()
  }

  static async applySANLoss(rollMessage, event) {
    let rollData = rollMessage.getFlag("fvtt-cthulhu-eternal", "rollData")
    if (!rollData) {
      ui.notifications.error(game.i18n.localize("CTHULHUETERNAL.Notifications.noRollDataFound"))
      return
    }
    let actor = game.actors.get(rollData.actorId)
    if (!actor) {
      ui.notifications.error(game.i18n.localize("CTHULHUETERNAL.Notifications.noActorFound"))
      return
    }
    // Get the san loss from the event : data-san-value
    let sanLoss = event.currentTarget.dataset.sanValue
    let r = new Roll(sanLoss.toString())
    await r.evaluate()
    rollData.sanLoss = -r.total

    await actor.system.modifySAN(rollData)
    // Delete the roll message
    await rollMessage.delete()
  }

  static async healingRoll(rollMessage) {
    let rollData = rollMessage.rolls[0]?.options?.rollData
    let healingFormula = rollData.rollItem.system.healingFormula
    let healingMsg = "CTHULHUETERNAL.Label.healingRoll"
    if (rollData.resultType === "successCritical") {
      healingFormula += " * 2"
    }
    if (rollData.resultType === "failureCritical") {
      healingMsg = "CTHULHUETERNAL.Label.healingRollFailure"
    }
    // Now display the result in chat message
    let roll = new Roll(healingFormula)
    await roll.evaluate()
    roll.toMessage({
      speaker: ChatMessage.getSpeaker({ actor: rollData.actorId }),
      flavor: `${game.i18n.localize(healingMsg)} :  ${roll.total}`,
      rolls: [roll],
      options: {
        rollData: rollData,
        resultType: rollData.resultType
      }
    })
  }

  static async opposedRollManagement(rollMessage, event) {
    let rollData = rollMessage.getFlag("fvtt-cthulhu-eternal", "rollData")
    if (!rollData) {
      ui.notifications.error(game.i18n.localize("CTHULHUETERNAL.Notifications.noRollDataFound"))
      return
    }

    // Get the store
    let store = game.settings.get("fvtt-cthulhu-eternal", "roll-opposed-store")
    if (!store.roll1) {
      store.roll1 = {
        rollData: rollData,
        messageId: rollMessage.id
      }
      await game.settings.set("fvtt-cthulhu-eternal", "roll-opposed-store", store)
      ui.notifications.info(game.i18n.localize("CTHULHUETERNAL.Notifications.opposedRollFirstStored"))
    }
    else if (!store.roll2) {
      store.roll2 = {
        rollData: rollData,
        messageId: rollMessage.id
      }
      await game.settings.set("fvtt-cthulhu-eternal", "roll-opposed-store", store)
      ui.notifications.info(game.i18n.localize("CTHULHUETERNAL.Notifications.opposedRollSecondStored"))
      // Now perform the opposed roll resolution
      await this.resolveOpposedRolls(store.roll1, store.roll2)
      // Clear the store
      store.roll1 = null
      store.roll2 = null
      await game.settings.set("fvtt-cthulhu-eternal", "roll-opposed-store", store)
    }
    else {
      ui.notifications.error(game.i18n.localize("CTHULHUETERNAL.Notifications.opposedRollStoreFull"))
    }
  }

  static async resolveOpposedRolls(roll1, roll2) {
    // Get actors
    let actor1 = game.actors.get(roll1.rollData.actorId)
    let actor2 = game.actors.get(roll2.rollData.actorId)
    if (!actor1 || !actor2) {
      ui.notifications.error(game.i18n.localize("CTHULHUETERNAL.Notifications.noActorFound"))
      return
    }
    // Determine winner
    let winner = null
    let loser = null
    // If there critical success/failure, apply them first (remark : this d100 results)
    roll1.rollData.rollCompare = roll1.rollData.rollResult
    roll2.rollData.rollCompare = roll2.rollData.rollResult
    if (roll1.rollData.resultType === "successCritical") {
      roll1.rollData.rollCompare = -roll1.rollData.rollResult
    }
    if (roll2.rollData.resultType === "failureCritical") {
      roll2.rollData.rollCompare = 100 + roll2.rollData.rollResult
    }
    if (roll2.rollData.resultType === "successCritical") {
      roll2.rollData.rollCompare = -roll2.rollData.rollResult
    }
    if (roll1.rollData.resultType === "failureCritical") {
      roll1.rollData.rollCompare = roll1.rollData.rollResult * 2
    }
    if (roll1.rollData.isSuccess && roll2.rollData.isFailure) {
      winner = { actor: actor1, rollData: roll1.rollData, messageId: roll1.messageId }
      loser = { actor: actor2, rollData: roll2.rollData, messageId: roll2.messageId }
    }
    else if (roll2.rollData.isSuccess && roll1.rollData.isFailure) {
      winner = { actor: actor2, rollData: roll2.rollData, messageId: roll2.messageId }
      loser = { actor: actor1, rollData: roll1.rollData, messageId: roll1.messageId }
    }
    else if (roll1.rollData.rollCompare < roll2.rollData.rollCompare) {
      winner = { actor: actor1, rollData: roll1.rollData, messageId: roll1.messageId }
      loser = { actor: actor2, rollData: roll2.rollData, messageId: roll2.messageId }
    }
    else {
      winner = { actor: actor2, rollData: roll2.rollData, messageId: roll2.messageId }
      loser = { actor: actor1, rollData: roll1.rollData, messageId: roll1.messageId }
    }

    // Check if winner was attacking with a weapon that can apply damage
    let canApplyDamage = winner && winner.rollData?.weapon && winner.rollData.weapon.system

    // Prepare data for the template
    let msgData = {
      winner: {
        actor: {
          name: winner.actor.name,
          img: winner.actor.img
        },
        rollData: {
          rollResult: winner.rollData.rollResult || winner.rollData.total,
          isCritical: winner.rollData.isCritical,
          weapon: winner.rollData.weapon
        }
      },
      loser: {
        actor: {
          name: loser.actor.name,
          img: loser.actor.img
        },
        rollData: {
          rollResult: loser.rollData.rollResult || loser.rollData.total,
          isCritical: loser.rollData.isCritical
        }
      },
      canApplyDamage: canApplyDamage
    }

    // Render the template
    let content = await foundry.applications.handlebars.renderTemplate(
      "systems/fvtt-cthulhu-eternal/templates/chat-opposed-result.hbs",
      msgData
    )

    // Display the result in chat
    let chatMsg = await ChatMessage.create({
      speaker: ChatMessage.getSpeaker({ actor: winner.actor.id }),
      content: content
    })

    // Store the winner's roll data for damage roll if applicable
    if (canApplyDamage) {
      await chatMsg.setFlag("fvtt-cthulhu-eternal", "rollData", winner.rollData)
    }
  }

  static translateRangeUnit(range) {
    if (typeof range === 'string') {
      return game.i18n.localize(`CTHULHUETERNAL.Label.${range}`)
    } else if (typeof range === 'number') {
      return range
    } else {
      console.warn("CTHULHU ETERNAL | translateRange called with an unknown type", range)
      return range
    }
  }

  static translateRange(range) {
    // If the range is a string, replace STR with FOR
    if (typeof range === 'string') {
      return range.replace(/STR/g, "FOR").replace(/str/g, "for")
    }
    return range
  }

  static async registerBabeleTranslations(babele) {
    babele.registerConverters({
      'translateRangeUnit': (originalValue) => {
        return CthulhuEternalUtils.translateRangeUnit(originalValue)
      },
      'translateRange': (originalValue) => {
        return CthulhuEternalUtils.translateRange(originalValue)
      }
    })
  }

  static async damageRoll(rollMessage, formula = null) {
    let rollData = rollMessage.rolls[0]?.options?.rollData
    if (!rollData) {
      rollData = rollMessage.getFlag("fvtt-cthulhu-eternal", "rollData")
    }
    if (!rollData) {
      ui.notifications.error(game.i18n.localize("CTHULHUETERNAL.Label.noRollDataFound"))
      return
    }
    let actor = game.actors.get(rollData.actorId)
    if (!actor) {
      ui.notifications.error(game.i18n.localize("CTHULHUETERNAL.Label.noActorFound"))
      return
    }

    rollData.weapon.resultType = rollData.resultType // Keep the result type from the roll message
    rollData.weapon.selectiveFireChoice = rollData.selectiveFireChoice // Keep the selected fire choice from the roll message
    rollData.weapon.damageFormula = formula || rollData.weapon.system.damage
    actor.system.roll("damage", rollData.weapon)
  }

  static async wornWeaponCheck(rollMessage) {
    let rollData = rollMessage.rolls[0]?.options?.rollData
    if (!rollData) {
      rollData = rollMessage.getFlag("fvtt-cthulhu-eternal", "rollData")
    }
    if (!rollData) {
      ui.notifications.error(game.i18n.localize("CTHULHUETERNAL.Label.noRollDataFound"))
      return
    }
    let actor = game.actors.get(rollData.actorId)
    if (!actor) {
      ui.notifications.error(game.i18n.localize("CTHULHUETERNAL.Label.noActorFound"))
      return
    }
    let weapon = rollData.weapon
    if (!weapon) {
      ui.notifications.error(game.i18n.localize("CTHULHUETERNAL.Label.noWeaponFound"))
      return
    }

    // Lance un jet de chance (50%)
    let luckRoll = new Roll("1d100")
    await luckRoll.evaluate()

    let isSuccess = luckRoll.total <= 50
    let resultMsg = ""

    if (isSuccess) {
      // Succès - l'arme reste worn
      resultMsg = game.i18n.format("CTHULHUETERNAL.Label.wornWeaponCheckSuccess", {
        weapon: weapon.name,
        roll: luckRoll.total
      })
    } else {
      // Échec - l'arme devient junk
      resultMsg = game.i18n.format("CTHULHUETERNAL.Label.wornWeaponCheckFailure", {
        weapon: weapon.name,
        roll: luckRoll.total
      })

      // Mettre à jour l'état de l'arme
      await actor.updateEmbeddedDocuments("Item", [{
        _id: weapon._id,
        "system.state": "junk"
      }])

      ui.notifications.warn(game.i18n.format("CTHULHUETERNAL.Notifications.WeaponBecameJunk", { weapon: weapon.name }))
    }

    // Créer un message de chat avec le résultat
    await ChatMessage.create({
      user: game.user.id,
      content: `<div class="cthulhu-eternal-roll">
        <h4>${game.i18n.localize("CTHULHUETERNAL.Label.wornWeaponCheckTitle")}</h4>
        <p>${resultMsg}</p>
      </div>`,
      speaker: ChatMessage.getSpeaker({ actor: actor }),
    })
  }

  static async nudgeToSuccess(rollMessage) {
    if (!game.settings.get("fvtt-cthulhu-eternal", "settings-nudge")) {
      ui.notifications.warn(game.i18n.localize("CTHULHUETERNAL.Settings.nudgeDisabled"))
      return
    }
    const rollOptions = rollMessage.rolls[0]?.options
    if (!rollOptions) return
    const actor = game.actors.get(rollOptions.actorId)
    if (!actor) return
    const rollTotal = rollMessage.rolls[0].total
    const targetScore = rollOptions.rollData?.targetScore ?? rollOptions.targetScore
    const wpCostToSucceed = Math.ceil((rollTotal - targetScore) / 5)

    if (actor.system.wp.value < wpCostToSucceed) {
      ui.notifications.warn(game.i18n.localize("CTHULHUETERNAL.Label.notEnoughWP"))
      return
    }

    const dialogContext = foundry.utils.duplicate(rollOptions)
    dialogContext.nudgedValue = targetScore
    dialogContext.wpCost = wpCostToSucceed
    dialogContext.isNudgedRoll = true
    dialogContext.isNudge = false

    const roll = new CthulhuEternalRoll(String(targetScore))
    await roll.evaluate()
    roll.options = dialogContext
    roll.displayRollResult(roll, dialogContext, dialogContext.rollData)
    roll.toMessage()

    actor.system.modifyWP(-wpCostToSucceed)

    // Original roll was a failure with skill progression — nudge succeeded, so unmark progression
    if (rollOptions.skillMarkedForProgress && rollOptions.rollItem?._id) {
      const skillItem = actor.items.get(rollOptions.rollItem._id)
      if (skillItem) await skillItem.update({ "system.rollFailed": false })
    }

    await rollMessage.delete()
  }

  static async nudgeRoll(rollMessage) {
    if (!game.settings.get("fvtt-cthulhu-eternal", "settings-nudge")) {
      ui.notifications.warn(game.i18n.localize("CTHULHUETERNAL.Settings.nudgeDisabled"))
      return
    }

    let dialogContext = rollMessage.rolls[0]?.options
    let actor = game.actors.get(dialogContext.actorId)
    const rollTotal = rollMessage.rolls[0].total
    dialogContext.wpValue = actor.system.wp.value
    // Rule: 1 WP can decrease the roll by 1–5. Only decrease is allowed.
    dialogContext.minValue = Math.max(rollTotal - (dialogContext.wpValue * 5), 1)
    dialogContext.maxValue = rollTotal
    dialogContext.rollResultIndex = rollTotal - dialogContext.minValue  // index of current value in nudgeOptions
    dialogContext.wpCost = 0
    dialogContext.nudgedValue = rollTotal  // default: no change for the select: values from minValue to maxValue (current roll)
    dialogContext.nudgeOptions = Array.from({ length: dialogContext.maxValue - dialogContext.minValue + 1 }, (_, i) => dialogContext.minValue + i)

    const content = await foundry.applications.handlebars.renderTemplate("systems/fvtt-cthulhu-eternal/templates/nudge-dialog.hbs", dialogContext)

    const title = game.i18n.localize("CTHULHUETERNAL.Roll.nudgeRoll")
    const rollContext = await foundry.applications.api.DialogV2.wait({
      window: { title: title },
      classes: ["fvtt-cthulhu-eternal"],
      content,
      buttons: [
        {
          action: "apply",
          label: game.i18n.localize("CTHULHUETERNAL.Roll.applyNudge"),
          callback: (event, button, dialog) => {
            const output = Array.from(button.form.elements).reduce((obj, input) => {
              if (input.name) obj[input.name] = input.value
              return obj
            }, {})
            // Compute nudgedValue from the selected index (selectOptions uses 0-based indices for arrays)
            dialogContext.nudgedValue = dialogContext.minValue + Number(output.modifiedValue)
            dialogContext.wpCost = Math.ceil(Math.abs(rollTotal - dialogContext.nudgedValue) / 5)
            return output
          },
        },
        {
          action: "cancel",
          label: game.i18n.localize("CTHULHUETERNAL.Roll.cancel"),
          callback: (event, button, dialog) => { }
        }
      ],
      actions: {
      },
      rejectClose: false, // Click on Close button will not launch an error
      render: (event, dialog) => {
        $(".nudged-score-select").change(event => {
          dialogContext.nudgedValue = dialogContext.minValue + Number(event.target.value)
          dialogContext.wpCost = Math.ceil(Math.abs(rollMessage.rolls[0].total - dialogContext.nudgedValue) / 5)
          $("#nudged-wp-cost").val(dialogContext.wpCost)
        })
      }
    })

    // If the user cancels the dialog, exit
    if (rollContext === null || dialogContext.wpCost === 0) {
      return
    }

    if (actor.system.wp.value < dialogContext.wpCost) {
      ui.notifications.warn(game.i18n.localize("CTHULHUETERNAL.Label.notEnoughWP"))
      return
    }

    const roll = new CthulhuEternalRoll(String(dialogContext.nudgedValue))
    await roll.evaluate()
    roll.options = dialogContext
    roll.options.isNudgedRoll = true
    roll.options.isNudge = false
    roll.displayRollResult(roll, dialogContext, dialogContext.rollData)
    roll.toMessage()

    actor.system.modifyWP(-dialogContext.wpCost)

    // If original roll had skill progression and nudge converts it to success, unmark progression
    const nudgedTargetScore = dialogContext.rollData?.targetScore ?? dialogContext.targetScore
    if (dialogContext.skillMarkedForProgress && dialogContext.nudgedValue <= nudgedTargetScore && dialogContext.rollItem?._id) {
      const skillItem = actor.items.get(dialogContext.rollItem._id)
      if (skillItem) await skillItem.update({ "system.rollFailed": false })
    }

    // Delete the initial roll message
    await rollMessage.delete()

  }

  static setupCSSRootVariables() {
    const era = game.settings.get("fvtt-cthulhu-eternal", "settings-era")

    let eraCSS = SYSTEM.ERA_CSS[era];
    if (!eraCSS) eraCSS = SYSTEM.ERA_CSS["jazz"];

    document.documentElement.style.setProperty('--font-size-standard', eraCSS.baseFontSize);
    document.documentElement.style.setProperty('--font-size-title', eraCSS.titleFontSize);
    document.documentElement.style.setProperty('--font-size-result', eraCSS.titleFontSize);
    document.documentElement.style.setProperty('--font-primary', eraCSS.primaryFont);
    document.documentElement.style.setProperty('--font-secondary', eraCSS.secondaryFont);
    document.documentElement.style.setProperty('--font-title', eraCSS.titleFont);
    document.documentElement.style.setProperty('--img-icon-color-filter', eraCSS.imgFilter);
    document.documentElement.style.setProperty('--background-image-base', `linear-gradient(rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0.8)), url("../assets/ui/${era}_background_main.webp")`);
  }

  static getTarget() {
    if (game.user.targets && game.user.targets.size === 1) {
      return game.user.targets.values().next().value;
    }
    return undefined;
  }

  static applyWounds(message, event) {
    let woundData = message.getFlag("fvtt-cthulhu-eternal", "woundData")
    if (!woundData) {
      ui.notifications.error(game.i18n.localize("CTHULHUETERNAL.Notifications.noRollDataFound"))
      return
    }
    let actor = game.actors.get(woundData.actorId)
    if (!actor) {
      ui.notifications.error(game.i18n.localize("CTHULHUETERNAL.Notifications.noActorFound"))
      return
    }
    this.removeChatMessageId(message.id)

    // Resolve the target actor: prefer canvas token (works for both combat and scene tokens)
    const targetTokenId = event.currentTarget.dataset.tokenId
    const targetActorId = event.currentTarget.dataset.actorId
    let targetActor
    if (targetTokenId) {
      const token = canvas.tokens.get(targetTokenId)
      targetActor = token?.actor
    }
    if (!targetActor && targetActorId) {
      targetActor = game.actors.get(targetActorId)
    }
    // Legacy fallback: combatant lookup
    if (!targetActor) {
      const combatantId = event.currentTarget.dataset.combatantId
      if (combatantId && game.combat) {
        const combatant = game.combat.combatants.get(combatantId)
        targetActor = combatant?.token?.actor || game.actors.get(combatant?.actorId)
      }
    }
    if (!targetActor) {
      ui.notifications.error(game.i18n.localize("CTHULHUETERNAL.Notifications.noTargetActorFound"))
      return
    }
    targetActor.applyWounds(woundData)
  }
}
