const { HandlebarsApplicationMixin } = foundry.applications.api
import { ARMOR_TYPE_CHOICES, CLASS_RESTRICTION_CHOICES, SYSTEM, WEAPON_PROFICIENCY_GROUPS } from "../../config/system.mjs"
import { rollRarityCheck } from "../../rolls.mjs"

export default class OathHammerItemSheet extends HandlebarsApplicationMixin(foundry.applications.sheets.ItemSheetV2) {
  static SHEET_MODES = { EDIT: 0, PLAY: 1 }

  constructor(options = {}) {
    super(options)
    this.#dragDrop = this.#createDragDropHandlers()
  }

  #dragDrop

  /** @override */
  static DEFAULT_OPTIONS = {
    classes: ["oathhammer", "item"],
    position: {
      width: 600,
      height: "auto",
    },
    form: {
      submitOnChange: true,
    },
    window: {
      resizable: true,
    },
    dragDrop: [{ dragSelector: "[data-drag]", dropSelector: null }],
    actions: {
      toggleSheet: OathHammerItemSheet.#onToggleSheet,
      editImage: OathHammerItemSheet.#onEditImage,
      rollRarity: OathHammerItemSheet.#onRollRarity,
      removeRune: OathHammerItemSheet.#onRemoveRune,
      openRune:   OathHammerItemSheet.#onOpenRune,
    },
  }

  _sheetMode = this.constructor.SHEET_MODES.PLAY
  _lockedReadOnly = false

  /** @override — prevent form submissions when this sheet is opened in read-only mode */
  get isEditable() {
    if (this._lockedReadOnly) return false
    return super.isEditable
  }

  get isPlayMode() {
    return this._sheetMode === this.constructor.SHEET_MODES.PLAY
  }

  get isEditMode() {
    return this._sheetMode === this.constructor.SHEET_MODES.EDIT
  }

  /** @override */
  async _prepareContext() {
    const context = await super._prepareContext()
    context.fields = this.document.schema.fields
    context.systemFields = this.document.system.schema.fields
    context.item = this.document
    context.system = this.document.system
    context.source = this.document.toObject()
    context.isEditMode = this.isEditMode
    context.isPlayMode = this.isPlayMode
    context.isEditable = this.isEditable
    if (this.document.system.description !== undefined) {
      context.enrichedDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.description ?? "", { async: true })
    }
    if (this.document.system.magicEffect !== undefined) {
      context.enrichedMagicEffect = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.magicEffect ?? "", { async: true })
    }
    context.classRestrictionChoices = CLASS_RESTRICTION_CHOICES
    // Armor-specific numeric selects
    context.armorValueChoices = Object.fromEntries(
      Array.from({ length: 13 }, (_, i) => [i, String(i)])
    )
    context.penaltyChoices = Object.fromEntries(
      Array.from({ length: 6 }, (_, i) => [-i, String(-i)])
    )
    // Weapon-specific numeric selects
    context.damageModChoices = Object.fromEntries(
      Array.from({ length: 20 }, (_, i) => [i - 4, i - 4 >= 0 ? `+${i - 4}` : String(i - 4)])
    )
    context.apChoices = Object.fromEntries(
      Array.from({ length: 7 }, (_, i) => [i, String(i)])
    )
    // Skill choices for weapon skill override (empty = auto-detect)
    context.skillChoices = {
      "": `— ${game.i18n.localize("OATHHAMMER.Weapon.SkillOverrideAuto")} —`,
      ...Object.fromEntries(
        Object.entries(SYSTEM.SKILLS).map(([k, v]) => [k, game.i18n.localize(v.label)])
      )
    }
    // Class proficiency choices (for class-sheet checkboxes)
    context.armorTypeChoices  = ARMOR_TYPE_CHOICES
    context.weaponGroupChoices = WEAPON_PROFICIENCY_GROUPS

    // Rune context — enrich effect HTML for each attached rune
    if (Array.isArray(this.document.system.runes)) {
      context.enrichedRunes = await Promise.all(
        this.document.system.runes.map(async (rune, idx) => ({
          ...rune,
          idx,
          enrichedEffect: await foundry.applications.ux.TextEditor.implementation.enrichHTML(rune.effect ?? "", { async: true }),
        }))
      )
    }
    context.acceptsRunes = this.#acceptsRunes()

    return context
  }

  /** Returns true if this item type can have runic spells attached. */
  #acceptsRunes() {
    const type = this.document.type
    if (type === "armor" || type === "weapon") return true
    if (type === "magic-item" && this.document.system.itemType === "talisman") return true
    return false
  }

  /** Map runeType expected for each item type. */
  static #EXPECTED_RUNE_TYPE = {
    armor:      "armor",
    weapon:     "weapon",
    "magic-item": "talisman",
  }

  /** @override */
  _onRender(context, options) {
    super._onRender(context, options)
    this.#dragDrop.forEach((d) => d.bind(this.element))
    if (this._lockedReadOnly) {
      this.element.querySelector('[data-action="toggleSheet"]')?.remove()
      for (const el of this.element.querySelectorAll(
        '.window-content input, .window-content select, .window-content textarea, .window-content button'
      )) {
        el.disabled = true
      }
    }
    for (const pm of this.element.querySelectorAll("prose-mirror[name]")) {
      pm.addEventListener("change", async (event) => {
        event.stopPropagation()
        await this.document.update({ [pm.name]: pm.value ?? "" })
      })
    }
  }

  #createDragDropHandlers() {
    return this.options.dragDrop.map((d) => {
      d.permissions = {
        dragstart: this._canDragStart.bind(this),
        drop: this._canDragDrop.bind(this),
      }
      d.callbacks = {
        dragstart: this._onDragStart.bind(this),
        dragover: this._onDragOver.bind(this),
        drop: this._onDrop.bind(this),
      }
      return new foundry.applications.ux.DragDrop.implementation(d)
    })
  }

  _canDragStart(selector) {
    return this.isEditable
  }

  _canDragDrop(selector) {
    return this.isEditable && this.document.isOwner
  }

  _onDragStart(event) {
    if ("link" in event.target.dataset) return
  }

  _onDragOver(event) {}

  async _onDrop(event) {
    if (!this.#acceptsRunes()) return
    let dragData
    try { dragData = JSON.parse(event.dataTransfer.getData("text/plain")) } catch { return }
    if (dragData?.type !== "Item") return

    let spell
    try { spell = await fromUuid(dragData.uuid) } catch { return }
    if (!spell || spell.type !== "spell") return

    const sys = spell.system
    if (sys.tradition !== "runic") {
      ui.notifications.warn(game.i18n.localize("OATHHAMMER.Rune.NotRunic"))
      return
    }

    const expectedType = OathHammerItemSheet.#EXPECTED_RUNE_TYPE[this.document.type]
    if (sys.runeType !== expectedType) {
      ui.notifications.warn(game.i18n.format("OATHHAMMER.Rune.WrongType", {
        expected: game.i18n.localize(`OATHHAMMER.RuneType.${expectedType.charAt(0).toUpperCase() + expectedType.slice(1)}`),
      }))
      return
    }

    const current = this.document.system.runes ?? []
    if (current.length >= 2) {
      ui.notifications.warn(game.i18n.localize("OATHHAMMER.Rune.MaxRunes"))
      return
    }
    if (current.some(r => r.name === spell.name)) {
      ui.notifications.warn(game.i18n.localize("OATHHAMMER.Rune.Duplicate"))
      return
    }
    if (sys.isExalted && current.some(r => r.isExalted)) {
      ui.notifications.warn(game.i18n.localize("OATHHAMMER.Rune.MaxExalted"))
      return
    }

    const snapshot = {
      sourceUuid:      spell.uuid,
      name:            spell.name,
      img:             spell.img,
      runeType:        sys.runeType,
      isExalted:       sys.isExalted,
      difficultyValue: sys.difficultyValue,
      effect:          sys.effect ?? "",
      duration:        sys.duration ?? "",
      range:           sys.range ?? "",
      spellSave:       sys.spellSave ?? "",
    }
    await this.document.update({ "system.runes": [...current, snapshot] })
    ui.notifications.info(game.i18n.format("OATHHAMMER.Rune.Attached", { name: spell.name }))
  }

  static async #onRemoveRune(event, target) {
    const idx = parseInt(target.dataset.runeIndex, 10)
    if (isNaN(idx)) return
    const runes = [...(this.document.system.runes ?? [])]
    runes.splice(idx, 1)
    await this.document.update({ "system.runes": runes })
  }

  static async #onOpenRune(event, target) {
    const uuid = target.dataset.sourceUuid
    if (!uuid) return
    try {
      const spell = await fromUuid(uuid)
      if (!spell) {
        ui.notifications.warn(game.i18n.localize("OATHHAMMER.Rune.SourceNotFound"))
        return
      }
      // Use a unique ID so this read-only instance never conflicts with the
      // document's normal sheet (which uses {ClassName}-{documentId} as its ID).
      const SheetClass = spell.sheet.constructor
      const sheet = new SheetClass({
        document: spell,
        id: `${SheetClass.name}-rune-view-${spell.id}`,
      })
      sheet._lockedReadOnly = true
      sheet.render(true)
    } catch {
      ui.notifications.warn(game.i18n.localize("OATHHAMMER.Rune.SourceNotFound"))
    }
  }

  static #onToggleSheet(event, target) {
    if (this._lockedReadOnly) return
    const modes = this.constructor.SHEET_MODES
    this._sheetMode = this.isEditMode ? modes.PLAY : modes.EDIT
    this.render()
  }

  static async #onEditImage(event, target) {
    const attr = target.dataset.edit
    const current = foundry.utils.getProperty(this.document, attr)
    const { img } = this.document.constructor.getDefaultArtwork?.(this.document.toObject()) ?? {}
    const fp = new FilePicker({
      current,
      type: "image",
      redirectToRoot: img ? [img] : [],
      callback: (path) => {
        this.document.update({ [attr]: path })
      },
      top: this.position.top + 40,
      left: this.position.left + 10,
    })
    return fp.browse()
  }

  static async #onRollRarity(event, target) {
    const rarity = this.document.system.rarity
    if (!rarity) return
    // Find the owning actor (embedded item) or prompt user to select a character
    let actor = this.document.parent
    if (!actor || actor.documentName !== "Actor") {
      // Item not embedded — use the user's selected character
      actor = game.user.character
      if (!actor) {
        ui.notifications.warn(game.i18n.localize("OATHHAMMER.Roll.NoActor"))
        return
      }
    }
    await rollRarityCheck(actor, rarity, this.document.name)
  }
}
