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

export default class HellbornRoll extends Roll {
  /**
   * The HTML template path used to render dice checks of this type
   * @type {string}
   */
  static CHAT_TEMPLATE = "systems/fvtt-hellborn/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.numericModifier >= 0) {
      fullFormula = `${options.formula} + ${options.rollItem.value} + ${options.numericModifier}D`
    } else {
      fullFormula = `${options.formula} + ${options.rollItem.value} - ${Math.abs(options.numericModifier)}D`
    }
    $('#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"

    switch (options.rollType) {
      case "skill":
        break
      case "damage":
        let formula = options.rollItem.system.damage
        let damageRoll = new Roll(formula)
        await damageRoll.evaluate()
        await damageRoll.toMessage({
          flavor: `${options.rollItem.name} - Damage Roll`
        });
        return
      case "weapon":
        let actor = game.actors.get(options.actorId)
        options.weapon = foundry.utils.duplicate(options.rollItem)
        options.rollItem = actor.system.skills.combat
        break
      default:
        break
    }

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

    const choiceModifier = SYSTEM.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 }
        if (!rangeModifier && value) {
          rangeModifier = value
        }
      }
    }

    let modifier = "0"
    options.numericModifier = rangeModifier
    let fullFormula = `${formula} + ${options.rollItem.value}`
    if (options.isEncumbered) {
      options.numericModifier += -1
      fullFormula += ` - ${options.numericModifier}D`
    } else {
      options.numericModifier += 0
      fullFormula += ` + ${options.numericModifier}D`
    }
    options.fullFormula = fullFormula
    options.formula = formula

    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,
      choiceRangeModifier,
      rangeModifier,
      formula,
      hasTarget: options.hasTarget,
      modifier,
    }
    const content = await renderTemplate("systems/fvtt-hellborn/templates/roll-dialog.hbs", dialogContext)

    const title = HellbornRoll.createTitle(options.rollType, options.rollTarget)
    const label = game.i18n.localize("HELLBORN.Roll.roll")
    const rollContext = await foundry.applications.api.DialogV2.wait({
      window: { title: title },
      classes: ["fvtt-hellborn"],
      content,
      buttons: [
        {
          label: label,
          callback: (event, button, dialog) => {
            const output = Array.from(button.form.elements).reduce((obj, input) => {
              if (input.name) obj[input.name] = input.value
              return obj
            }, {})
            return output
          },
        },
      ],
      actions: {
      },
      rejectClose: false, // Click on Close button will not launch an error
      render: (event, dialog) => {
        $(".roll-skill-modifier").change(event => {
          options.numericModifier += Number(event.target.value)
          HellbornRoll.updateFullFormula(options)
        })
        $(".roll-skill-range-modifier").change(event => {
          options.numericModifier += Number(event.target.value)
          HellbornRoll.updateFullFormula(options)
        })
        $(".select-combat-option").change(event => {
          console.log(event)
          let field = $(event.target).data("field")
          let modifier = SYSTEM.ATTACK_MODIFIERS[field]
          if ( event.target.checked) {
            options.numericModifier += modifier
          } else {
            options.numericModifier -= modifier
          }
          HellbornRoll.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-hellborn.preRoll", options, rollData) === false) return

    let diceFormula = `${2+Math.abs(options.numericModifier)}D6`
    if ( options.numericModifier > 0 ) {
      diceFormula += `kh2 + ${options.rollItem.value}`
    } else {
      diceFormula += `kl2 + ${options.rollItem.value}`
    }

    const roll = new this(diceFormula, options.data, rollData)
    await roll.evaluate()

    roll.displayRollResult(roll, options, rollData)

    if (Hooks.call("fvtt-hellborn.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 "skill":
        return `${game.i18n.localize("HELLBORN.Label.titleSkill")}`
      case "weapon":
        return `${game.i18n.localize("HELLBORN.Label.titleWeapon")}`
      default:
        return game.i18n.localize("HELLBORN.Label.titleStandard")
    }
  }

  /** @override */
  async render(chatOptions = {}) {
    let chatData = await this._getChatCardData(chatOptions.isPrivate)
    return await 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 },
    )
  }

}
