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

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

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

  get isDamage() {
    return this.type === ROLL_TYPE.DAMAGE
  }

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

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

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

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

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

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

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

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

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

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

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

  static updateFullFormula(options) {
    let fullFormula
    if (options.rollType === "damage") {
      fullFormula = `${options.formula} + ${options.damageModifier}D6 `
    } else {
      let mod = options.rollItem?.value || 0
      fullFormula = `${options.formula} + ${options.skillModifier}D + ${mod} + ${options.rangeModifier}D + ${options.numericModifier}D + ${options.numericModifierSelect}`
      if (options.vehicleBonus && options.useVehicleBonus) {
        fullFormula += ` + ${options.vehicleBonus}`
      }
    }
    // Replace all the "+ -" with "-"
    fullFormula = fullFormula.replace(/\+\s*-/g, "- ")
    $('#roll-dialog-full-formula').text(fullFormula)
    options.fullFormula = fullFormula
  }

  /**
   * Prompt the user with a dialog to configure and execute a roll.
   *
   * @param {Object} options Configuration options for the roll.
   * @param {string} options.rollType The type of roll being performed.
   * @param {string} options.rollTarget The target of the roll.
   * @param {string} options.actorId The ID of the actor performing the roll.
   * @param {string} options.actorName The name of the actor performing the roll.
   * @param {string} options.actorImage The image of the actor performing the roll.
   * @param {boolean} options.hasTarget Whether the roll has a target.
   * @param {Object} options.data Additional data for the roll.
   *
   * @returns {Promise<Object|null>} The roll result or null if the dialog was cancelled.
   */
  static async prompt(options = {}) {
    let formula = "2d6"
    let actor = game.actors.get(options.actorId)

    switch (options.rollType) {
      case "skill":
        break
      case "damage":
        options.weapon = foundry.utils.duplicate(options.rollItem)
        formula = options.weapon.system.damage
        break
      case "weapon":
        options.weapon = foundry.utils.duplicate(options.rollItem)
        options.rollItem = actor.system.skills.combat
        break
      default:
        break
    }

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

    const choiceModifier = SYSTEM.MODIFIER_CHOICES
    const choiceNumericModifier = SYSTEM.NUMERIC_MODIFIER_CHOICES
    let choiceRangeModifier = {}
    let rangeModifier = 0
    if (options.weapon) {
      // Build the range modifiers
      let range = SYSTEM.WEAPON_RANGE[options.weapon.system.rangeType]
      for (let [key, value] of Object.entries(range.range)) {
        choiceRangeModifier[key] = { label: `${key} (${value}D)`, value: value }
      }
    }
    const choiceDamageModifier = SYSTEM.DAMAGE_MODIFIER_CHOICES
    let damageModifier = "0"

    let modifier = "0"
    let numericModifierSelect = 0
    options.skillModifier = 0
    options.numericModifier = 0
    options.numericModifierSelect = 0
    options.rangeModifier = rangeModifier
    options.damageModifier = damageModifier
    options.vehicleBonus = options.vehicleBonus || 0
    options.useVehicleBonus = true // Par défaut, le bonus est activé
    let fullFormula = `${formula} + ${options.rollItem.value}`
    if (options.isEncumbered) {
      fullFormula += ` - 1D`
    }
    if (options.vehicleBonus && options.useVehicleBonus) {
      fullFormula += ` + ${options.vehicleBonus}`
    }
    options.fullFormula = fullFormula
    options.formula = formula

    // Prepare available skills if provided (for vehicle/starship piloting)
    let availableSkills = null
    if (options.availableSkills) {
      availableSkills = Object.entries(options.availableSkills).map(([id, skill]) => ({
        id: id,
        label: skill.label,
        value: skill.value,
        selected: id === options.selectedSkillId
      }))
    }

    let dialogContext = {
      actorId: options.actorId,
      actorName: options.actorName,
      rollType: options.rollType,
      rollItem: foundry.utils.duplicate(options.rollItem), // Object only, no class
      fullFormula,
      weapon: options?.weapon,
      isEncumbered: options.isEncumbered,
      talents: options.talents,
      rollModes,
      fieldRollMode,
      choiceModifier,
      choiceNumericModifier,
      choiceRangeModifier,
      choiceDamageModifier,
      rangeModifier,
      damageModifier,
      formula,
      hasTarget: options.hasTarget,
      modifier,
      numericModifierSelect,
      vehicleBonus: options.vehicleBonus,
      availableSkills: availableSkills,
    }
    const content = await foundry.applications.handlebars.renderTemplate("systems/fvtt-ftl-nomad/templates/roll-dialog.hbs", dialogContext)

    const title = FTLNomadRoll.createTitle(options.rollType, options.rollTarget)
    const label = game.i18n.localize("FTLNOMAD.Roll.roll")
    const rollContext = await foundry.applications.api.DialogV2.wait({
      window: { title: title },
      classes: ["fvtt-ftl-nomad"],
      content,
      buttons: [
        {
          label: label,
          callback: (event, button, dialog) => {
            const output = Array.from(button.form.elements).reduce((obj, input) => {
              if (input.name) {
                // For checkboxes, use checked property instead of value
                obj[input.name] = input.type === 'checkbox' ? input.checked : input.value
              }
              return obj
            }, {})
            return output
          },
        },
      ],
      actions: {
      },
      rejectClose: false, // Click on Close button will not launch an error
      render: (event, dialog) => {
        FTLNomadRoll.updateFullFormula(options)
        $(".roll-skill-selector").change(event => {
          const skillId = event.target.value
          if (options.availableSkills && options.availableSkills[skillId]) {
            options.rollItem = foundry.utils.duplicate(options.availableSkills[skillId])
            options.selectedSkillId = skillId
            FTLNomadRoll.updateFullFormula(options)
          }
        })
        $(".roll-skill-modifier").change(event => {
          options.skillModifier = Number(event.target.value)
          FTLNomadRoll.updateFullFormula(options)
        })
        $(".roll-numeric-modifier").change(event => {
          options.numericModifierSelect = Number(event.target.value)
          FTLNomadRoll.updateFullFormula(options)
        })
        $(".roll-damage-modifier").change(event => {
          options.damageModifier = Number(event.target.value)
          FTLNomadRoll.updateFullFormula(options)
        })
        $(".roll-skill-range-modifier").change(event => {
          options.rangeModifier = Number(event.target.value)
          FTLNomadRoll.updateFullFormula(options)
        })
        $(".select-combat-option").change(event => {
          let field = $(event.target).data("field")
          let modifier = SYSTEM.ATTACK_MODIFIERS[field]
          if (event.target.checked) {
            options.numericModifier += modifier
          } else {
            options.numericModifier -= modifier
          }
          FTLNomadRoll.updateFullFormula(options)
        })
        $(".use-vehicle-bonus").change(event => {
          options.useVehicleBonus = event.target.checked
          FTLNomadRoll.updateFullFormula(options)
        })
      }
    })

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

    let rollData = foundry.utils.mergeObject(foundry.utils.duplicate(options), rollContext)
    rollData.rollMode = rollContext.visibility
    // Update target score
    rollData.targetScore = 8

    if (Hooks.call("fvtt-ftl-nomad.preRoll", options, rollData) === false) return

    // Update rollItem if a skill was selected from the dropdown
    if (rollData.selectedSkill && options.availableSkills) {
      options.rollItem = foundry.utils.duplicate(options.availableSkills[rollData.selectedSkill])
    }

    options.numericModifier = Number(rollData.numericModifier) || 0
    options.skillModifier = Number(rollData.skillModifier) || 0
    options.rangeModifier = Number(rollData.rangeModifier) || 0
    options.numericModifierSelect = Number(rollData.numericModifierSelect) || 0
    options.useVehicleBonus = rollData.useVehicleBonus === true
    options.finalModifier = options.numericModifier + options.skillModifier + options.rangeModifier
    let mod = options.rollItem?.value || 0
    mod += options.numericModifierSelect
    if (options.useVehicleBonus) {
      mod += options.vehicleBonus || 0
    }

    // Build the dice formula
    let diceFormula = "2d6"
    if (options.rollType === "damage") {
      let damageFormula = options.weapon.system.damage.toUpperCase().replace(/D/g, "d")
      // Extract the mod (if present), like in 3d6+1
      let match = damageFormula.match(/([+-]\d+)$/)
      if (match) {
        mod += Number(match[1])
        damageFormula = damageFormula.replace(match[1], "")
      }
      // Replace the D6 by the correct number of D6
      damageFormula = damageFormula.replace(/(\d*)d6/gi, (match, p1) => {
        let numDice = Number(p1) || 1
        let damageModifier = Number(options.damageModifier)
        let postArg = ""
        if (damageModifier && damageModifier !== 0) {
          postArg = (damageModifier > 0) ? `kh${numDice}` : `kl${numDice}`
          numDice += Math.abs(damageModifier)
        }
        return `${numDice}d6${postArg} + ${mod}`
      })
      diceFormula = damageFormula
    } else {
      diceFormula = `${2 + Math.abs(options.finalModifier)}D6`
      if (options.finalModifier > 0) {
        diceFormula += `kh2 + ${mod}`
      } else {
        diceFormula += `kl2 + ${mod}`
      }
    }

    console.log("FTLNomadRoll | Rolling ", diceFormula, options, rollData)
    const roll = new this(diceFormula, options.data, rollData)
    await roll.evaluate()

    roll.displayRollResult(roll, options, rollData)

    if (Hooks.call("fvtt-ftl-nomad.Roll", options, rollData, roll) === false) return

    return roll
  }

  displayRollResult(formula, options, rollData) {

    // Compute the result quality
    let resultType = "failure"
    if (this.total >= 8) {
      resultType = "success"
      // Detect if decimal == unit in the dire total result
    }

    this.options.resultType = resultType
    this.options.isSuccess = resultType === "success"
    this.options.isFailure = resultType === "failure"
    this.options.isEncumbered = rollData.isEncumbered
    this.options.rollData = foundry.utils.duplicate(rollData)
  }

  /**
   * Creates a title based on the given type.
   *
   * @param {string} type The type of the roll.
   * @param {string} target The target of the roll.
   * @returns {string} The generated title.
   */
  static createTitle(type, target) {
    switch (type) {
      case "damage":
        return `${game.i18n.localize("FTLNOMAD.Label.titleDamage")}`
      case "skill":
        return `${game.i18n.localize("FTLNOMAD.Label.titleSkill")}`
      case "weapon":
        return `${game.i18n.localize("FTLNOMAD.Label.titleWeapon")}`
      default:
        return game.i18n.localize("FTLNOMAD.Label.titleStandard")
    }
  }

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

  /**
   * Generates the data required for rendering a roll chat card.
   *
   * @param {boolean} isPrivate Indicates if the chat card is private.
   * @returns {Promise<Object>} A promise that resolves to an object containing the chat card data.
   * @property {Array<string>} css - CSS classes for the chat card.
   * @property {Object} data - The data associated with the roll.
   * @property {number} diceTotal - The total value of the dice rolled.
   * @property {boolean} isGM - Indicates if the user is a Game Master.
   * @property {string} formula - The formula used for the roll.
   * @property {number} total - The total result of the roll.
   * @property {boolean} isFailure - Indicates if the roll is a failure.
   * @property {string} actorId - The ID of the actor performing the roll.
   * @property {string} actingCharName - The name of the character performing the roll.
   * @property {string} actingCharImg - The image of the character performing the roll.
   * @property {string} resultType - The type of result (e.g., success, failure).
   * @property {boolean} hasTarget - Indicates if the roll has a target.
   * @property {string} targetName - The name of the target.
   * @property {number} targetArmor - The armor value of the target.
   * @property {number} realDamage - The real damage dealt.
   * @property {boolean} isPrivate - Indicates if the chat card is private.
   * @property {string} cssClass - The combined CSS classes as a single string.
   * @property {string} tooltip - The tooltip text for the chat card.
   */
  async _getChatCardData(isPrivate) {
    let cardData = foundry.utils.duplicate(this.options)
    cardData.css = [SYSTEM.id, "dice-roll"]
    cardData.data = this.data
    cardData.diceTotal = this.dice.reduce((t, d) => t + d.total, 0)
    cardData.isGM = game.user.isGM
    cardData.formula = this.formula
    cardData.fullFormula = this.options.fullFormula
    cardData.numericModifier = this.options.numericModifier
    cardData.total = this.total
    cardData.actorId = this.actorId
    cardData.actingCharName = this.actorName
    cardData.actingCharImg = this.actorImage
    cardData.resultType = this.resultType
    cardData.hasTarget = this.hasTarget
    cardData.targetName = this.targetName
    cardData.targetArmor = this.targetArmor
    cardData.realDamage = this.realDamage
    cardData.isPrivate = isPrivate
    cardData.weapon = this.weapon
    cardData.isEncumbered = this.isEncumbered

    cardData.cssClass = cardData.css.join(" ")
    cardData.tooltip = isPrivate ? "" : await this.getTooltip()
    return cardData
  }

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

}
