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

export default class CthulhuEternalRoll extends Roll {
  /**
   * The HTML template path used to render dice checks of this type
   * @type {string}
   */
  static CHAT_TEMPLATE = "systems/fvtt-cthulhu-eternal/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 gene() {
    return this.options.gene
  }

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

  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
  }

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

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

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

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

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

  static updateResourceDialog(options) {
    let rating = 0
    if (options.rollItem.enableHand) {
      rating += options.rollItem.hand
    }
    if (options.rollItem.enableStowed) {
      rating += options.rollItem.stowed
    }
    if (options.rollItem.enableStorage) {
      rating += options.rollItem.storage
    }
    let multiplier = Number($(`.roll-skill-multiplier`).val())
    options.initialScore = rating
    options.percentScore = rating * multiplier
    $(".resource-score").text(`${rating} (${options.percentScore}%)`)
  }

  /**
   * 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 = "1d100"
    let hasModifier = true
    let hasMultiplier = false
    options.isNudge = true

    switch (options.rollType) {
      case "skill":
        console.log(options.rollItem)
        options.initialScore = options.rollItem.system.computeScore()
        break
      case "san":
      case "char":
        options.initialScore = options.rollItem.targetScore
        options.isNudge = (options.rollType !== "san")
        break
      case "resource":
        hasModifier = false
        hasMultiplier = true
        options.initialScore = options.rollItem.targetScore
        options.totalRating = options.rollItem.targetScore
        options.percentScore = options.rollItem.targetScore * 5
        options.rollItem.enableHand = true
        options.rollItem.enableStowed = true
        options.rollItem.enableStorage = true
        options.isNudge = false
        break
      case "damage":
        let formula = options.rollItem.system.damage
        if ( options.rollItem.system.weaponType === "melee" || options.rollItem.system.weaponType === "unarmed") {
          formula += ` + ${options.rollItem.damageBonus}`
        }
        let damageRoll = new Roll(formula)
        await damageRoll.evaluate()
        await damageRoll.toMessage({
          flavor: `${options.rollItem.name} - Damage Roll`
        });
        let isLethal = false
        options.isNudge = false
        if (options.rollItem.system.lethality > 0) {
          let lethalityRoll = new Roll("1d100")
          await lethalityRoll.evaluate()
          isLethal = (lethalityRoll.total <= options.rollItem.system.lethality)
          await lethalityRoll.toMessage({
            flavor: `${options.rollItem.name} - Lethality Roll : ${lethalityRoll.total} <= ${options.rollItem.system.lethality} => ${isLethal}`
          });
        }
        return
      case "weapon":
        let era = game.settings.get("fvtt-cthulhu-eternal", "settings-era")
        if (era !== options.rollItem.system.settings) {
          ui.notifications.error(game.i18n.localize("CTHULHUETERNAL.Notifications.WrongEra"))
          console.log("WP Wrong Era", era, options.rollItem.system.weaponType)
          return
        }
        if (!SYSTEM.WEAPON_SKILL_MAPPING[era] || !SYSTEM.WEAPON_SKILL_MAPPING[era][options.rollItem.system.weaponType]) {
          ui.notifications.error(game.i18n.localize("CTHULHUETERNAL.Notifications.NoWeaponType"))
          console.log("WP Not found", era, options.rollItem.system.weaponType)
          return
        }
        options.weapon = options.rollItem
        if (options.rollItem.system.hasDirectSkill) {
          let skillName = options.rollItem.name
          options.rollItem = {type: "skill", name: skillName, system: {base: 0, bonus:  options.weapon.system.directSkillValue} }
          options.initialScore = options.weapon.system.directSkillValue
        } else {
          let skillName = game.i18n.localize(SYSTEM.WEAPON_SKILL_MAPPING[era][options.rollItem.system.weaponType])
          let actor = game.actors.get(options.actorId)          
          options.rollItem = actor.items.find(i => i.type === "skill" && i.name.toLowerCase() === skillName.toLowerCase())
          if (!options.rollItem) {
            ui.notifications.error(game.i18n.localize("CTHULHUETERNAL.Notifications.NoWeaponSkill"))
            return
          }
          options.initialScore = options.rollItem.system.computeScore()
          console.log("WEAPON", skillName, era, options.rollItem)
        }
        break
      default:
        options.initialScore = 50
        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
    const choiceMultiplier = SYSTEM.MULTIPLIER_CHOICES

    let modifier = "+0"
    let multiplier = "5"

    let dialogContext = {
      rollType: options.rollType,
      rollItem: foundry.utils.duplicate(options.rollItem), // Object only, no class
      weapon: options?.weapon,
      initialScore: options.initialScore,
      targetScore: options.initialScore,
      isLowWP: options.isLowWP,
      isZeroWP: options.isZeroWP,
      isExhausted: options.isExhausted,
      enableHand: options.rollItem.enableHand,
      enableStowed: options.rollItem.enableStowed,
      enableStorage: options.rollItem.enableStorage,
      rollModes,
      fieldRollMode,
      choiceModifier,
      choiceMultiplier,
      formula,
      hasTarget: options.hasTarget,
      hasModifier,
      hasMultiplier,
      modifier,
      multiplier
    }
    const content = await renderTemplate("systems/fvtt-cthulhu-eternal/templates/roll-dialog.hbs", dialogContext)

    const title = CthulhuEternalRoll.createTitle(options.rollType, options.rollTarget)
    const label = game.i18n.localize("CTHULHUETERNAL.Roll.roll")
    const rollContext = await foundry.applications.api.DialogV2.wait({
      window: { title: title },
      classes: ["fvtt-cthulhu-eternal"],
      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: {
        "selectHand": (event, button, dialog) => {
          options.rollItem.enableHand = !options.rollItem.enableHand
          this.updateResourceDialog(options)
        },
        "selectStowed": (event, button, dialog) => {
          options.rollItem.enableStowed = !options.rollItem.enableStowed
          this.updateResourceDialog(options)
        },
        "selectStorage": (event, button, dialog) => {
          options.rollItem.enableStorage = !options.rollItem.enableStorage
          this.updateResourceDialog(options)
        }
      },
      rejectClose: false, // Click on Close button will not launch an error
      render: (event, dialog) => {
        $(".roll-skill-multiplier").change(event => {
          options.multiplier = Number(event.target.value)
          this.updateResourceDialog(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
    console.log("Rolldata", rollData, options)
    if (options.rollType === "resource") {
      rollData.targetScore = options.initialScore * Number(rollContext.multiplier)
    } else {
      rollData.targetScore = Math.min(Math.max(options.initialScore + Number(rollData.modifier), 0), 100)
      if (rollData.isLowWP || rollData.isExhausted) {
        rollData.targetScore -= 20
      }
      if (rollData.isZeroWP) {
        rollData.targetScore = 0
      }
      rollData.targetScore = Math.min(Math.max(rollData.targetScore, 0), 100)
    }

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

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

    roll.displayRollResult(roll, options, rollData)

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

    return roll
  }

  displayRollResult(formula, options, rollData) {

    // Compute the result quality
    let resultType = "failure"
    let dec = Math.floor(this.total / 10)
    let unit = this.total - (dec * 10)
    if (this.total <= rollData.targetScore) {
      resultType = "success"
      // Detect if decimal == unit in the dire total result 
      if (dec === unit || this.total === 1) {
        resultType = "successCritical"
      }
    } else {
      // Detect if decimal == unit in the dire total result 
      if (dec === unit || this.total === 100) {
        resultType = "failureCritical"
      }
    }

    this.options.resultType = resultType
    if (this.options.isNudgedRoll) { 
      this.options.isSuccess = resultType === "success" || resultType === "successCritical"
      this.options.isFailure = resultType === "failure" || resultType === "failureCritical"
      this.options.isCritical = false
    } else {
      this.options.isSuccess = resultType === "success" || resultType === "successCritical"
      this.options.isFailure = resultType === "failure" || resultType === "failureCritical"
      this.options.isCritical = resultType === "successCritical" || resultType === "failureCritical"
    }
    this.options.isLowWP = rollData.isLowWP
    this.options.isZeroWP = rollData.isZeroWP
    this.options.isExhausted = rollData.isExhausted
    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("CTHULHUETERNAL.Label.titleSkill")}`
      case "weapon":
        return `${game.i18n.localize("CTHULHUETERNAL.Label.titleWeapon")}`
      case "char":
        return `${game.i18n.localize("CTHULHUETERNAL.Label.titleCharacteristic")}`
      case "san":
        return `${game.i18n.localize("CTHULHUETERNAL.Label.titleSAN")}`
      default:
        return game.i18n.localize("CTHULHUETERNAL.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.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.isLowWP = this.isLowWP
    cardData.isZeroWP = this.isZeroWP
    cardData.isExhausted = this.isExhausted
    cardData.isNudgedRoll = this.isNudgedRoll
    cardData.wpCost = this.wpCost

    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 },
    )
  }

}
