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

export default class AwERoll extends Roll {
  /** @type {string} */
  static CHAT_TEMPLATE = "systems/fvtt-adventures-with-emmy/templates/chat-message.hbs"

  // --- Accessors for roll options ---

  get attributeKey()    { return this.options.attributeKey }
  get rollName()        { return this.options.rollName }
  get modifier()        { return this.options.modifier ?? 0 }
  get attributeBonus()  { return this.options.attributeBonus ?? 0 }
  get bonus()           { return this.options.bonus ?? 0 }
  get knowledgeBonus()  { return this.options.knowledgeBonus ?? 0 }
  get dc()              { return this.options.dc }
  get outcome()         { return this.options.outcome }
  get actorId()           { return this.options.actorId }
  get actorName()         { return this.options.actorName }
  get actorImage()        { return this.options.actorImage }
  get sourceItemName()    { return this.options.sourceItemName }
  get sourceItemImg()     { return this.options.sourceItemImg }

  // --- Outcome calculation ---

  /**
   * Compute the degree of success for a d20 check.
   * - Critical Success : total ≥ dc + 10, OR natural 20 upgrades result
   * - Success          : total ≥ dc
   * - Failure          : total < dc
   * - Critical Failure : total ≤ dc − 10, OR natural 1 downgrades result
   *
   * @param {number} total     The final roll total (d20 + modifiers)
   * @param {number} dc        The Difficulty Class to compare against
   * @param {number} d20Value  The raw d20 result (for nat-20 / nat-1 adjustment)
   * @returns {"criticalSuccess"|"success"|"failure"|"criticalFailure"}
   */
  static computeOutcome(total, dc, d20Value) {
    const DEGREES = ["criticalFailure", "failure", "success", "criticalSuccess"]
    let idx
    if (total >= dc + 10)      idx = 3
    else if (total >= dc)      idx = 2
    else if (total <= dc - 10) idx = 0
    else                       idx = 1

    if (d20Value === 20) idx = Math.min(3, idx + 1)
    if (d20Value === 1)  idx = Math.max(0, idx - 1)

    return DEGREES[idx]
  }

  // --- Prompt dialog ---

  /**
   * Open a dialog so the player can add a situational bonus and choose visibility,
   * then evaluate and post the roll to chat.
   *
   * @param {object}  options
   * @param {string}  options.attributeKey     Attribute id (agility | fitness | awareness | influence)
   * @param {number}  options.modifier         Base attribute modifier (level + boostLevel)
   * @param {number}  [options.attributeBonus] Persistent attribute bonus/penalty from the +/- column
   * @param {object[]} [options.knowledgeBonuses] Field knowledge bonuses [{label, bonus}]
   * @param {number}  [options.dc]             Pre-set DC
   * @param {string}  options.actorId
   * @param {string}  options.actorName
   * @param {string}  options.actorImage
   * @returns {Promise<AwERoll|null>}  Resolved roll, or null if dialog was cancelled
   */
  static async prompt(options = {}) {
    const attrLabel      = options.attributeKey
      ? game.i18n.localize(SYSTEM.ATTRIBUTES[options.attributeKey]?.label ?? options.attributeKey)
      : (options.rollName ?? game.i18n.localize("AWEMMY.Roll.Check"))
    const mod            = options.modifier ?? 0
    const attrBonus      = options.attributeBonus ?? 0
    const knowledgeBonuses = options.knowledgeBonuses ?? []

    const rollModes = Object.fromEntries(
      Object.entries(CONFIG.Dice.rollModes).map(([k, v]) => [k, game.i18n.localize(v.label ?? v)])
    )

    // Bonus choices: -5 to +5
    const bonusChoices = Array.from({length: 11}, (_, i) => i - 5)
      .map(v => ({ value: v, label: v > 0 ? `+${v}` : String(v), selected: v === 0 }))

    // DC choices: blank + 10..30
    const dcChoices = [{ value: "", label: "—", selected: !options.dc }]
      .concat(Array.from({length: 21}, (_, i) => i + 10)
        .map(v => ({ value: v, label: String(v), selected: v === (options.dc ?? null) })))

    const content = await foundry.applications.handlebars.renderTemplate(
      "systems/fvtt-adventures-with-emmy/templates/roll-dialog.hbs",
      {
        attrLabel,
        modifier:        mod,
        attributeBonus:  attrBonus,
        totalMod:        mod + attrBonus,
        knowledgeBonuses,
        bonusChoices,
        dcChoices,
        rollModes,
        visibility:      game.settings.get("core", "rollMode")
      }
    )

    const result = await foundry.applications.api.DialogV2.wait({
      window:      { title: game.i18n.format("AWEMMY.Roll.DialogTitle", { name: attrLabel }) },
      classes:     ["awemmy"],
      content,
      render: (event, dialog) => {
        const baseMod       = mod + attrBonus
        const el            = dialog.element
        const bonusSelect   = el.querySelector('#awe-bonus')
        const knowledgeSel  = el.querySelector('#awe-knowledge')
        const preview       = el.querySelector('#awe-formula-preview')
        function updatePreview() {
          const sit   = parseInt(bonusSelect?.value) || 0
          const kn    = parseInt(knowledgeSel?.value) || 0
          const total = baseMod + sit + kn
          const sign  = total >= 0 ? '+' : '−'
          const abs   = Math.abs(total)
          preview.textContent = total === 0 ? '1d20' : `1d20 ${sign} ${abs}`
        }
        bonusSelect?.addEventListener('change', updatePreview)
        knowledgeSel?.addEventListener('change', updatePreview)
        updatePreview()
      },
      buttons: [{
        label:    game.i18n.localize("AWEMMY.Roll.Roll"),
        callback: (_event, button) =>
          Array.from(button.form.elements).reduce((obj, input) => {
            if (input.name) obj[input.name] = input.value
            return obj
          }, {})
      }],
      rejectClose: false
    })

    if (!result) return null

    const bonus          = parseInt(result.bonus) || 0
    const knowledgeBonus = parseInt(result.knowledgeBonus) || 0
    const dc             = result.dc !== "" ? parseInt(result.dc) : undefined
    const rollMode       = result.visibility ?? game.settings.get("core", "rollMode")

    // Formula: 1d20 + (mod + attrBonus) [± bonus] [± knowledgeBonus]
    const totalMod = mod + attrBonus + bonus + knowledgeBonus
    let formula = `1d20`
    if (totalMod > 0)      formula += ` + ${totalMod}`
    else if (totalMod < 0) formula += ` - ${Math.abs(totalMod)}`

    const roll = new this(formula, {}, {
      attributeKey:   options.attributeKey,
      rollName:       attrLabel,
      modifier:       mod + attrBonus,
      attributeBonus: attrBonus,
      bonus,
      knowledgeBonus,
      dc,
      actorId:          options.actorId,
      actorName:        options.actorName,
      actorImage:       options.actorImage,
      sourceItemName:   options.sourceItemName,
      sourceItemImg:    options.sourceItemImg
    })

    await roll.evaluate()

    // Compute degree of success when a DC is known
    if (dc !== undefined) {
      const d20Value = roll.dice[0]?.results[0]?.result ?? 0
      roll.options.outcome = AwERoll.computeOutcome(roll.total, dc, d20Value)
    }

    await roll.toMessage({
      speaker:  ChatMessage.getSpeaker({ actor: game.actors.get(options.actorId) }),
      flavor:   attrLabel,
      rollMode
    })

    return roll
  }

  /** @override — render the custom chat message template */
  async render(chatOptions = {}) {
    const isPrivate = chatOptions.isPrivate ?? false
    return foundry.applications.handlebars.renderTemplate(
      AwERoll.CHAT_TEMPLATE,
      {
        flavor:        this.rollName,
        total:         this.total,
        modifier:      this.modifier,
        bonus:         this.bonus,
        knowledgeBonus: this.knowledgeBonus,
        dice:          this.dice,
        outcome:       isPrivate ? null : this.outcome,
        dc:            this.dc,
        actorName:      this.actorName,
        actorImage:     this.actorImage,
        sourceItemName: this.sourceItemName,
        sourceItemImg:  this.sourceItemImg,
        isPrivate
      }
    )
  }
}
