const { HandlebarsApplicationMixin } = foundry.applications.api

import { MournbladeUtility } from "../../mournblade-utility.js"

export default class MournbladeActorSheet extends HandlebarsApplicationMixin(foundry.applications.sheets.ActorSheetV2) {
  /**
   * Different sheet modes.
   * @enum {number}
   */
  static SHEET_MODES = { EDIT: 0, PLAY: 1 }

  constructor(options = {}) {
    super(options)
    this.#dragDrop = this.#createDragDropHandlers()
    this._sheetMode = this.constructor.SHEET_MODES.PLAY // Commencer en mode visualisation
  }

  #dragDrop

  /** @override */
  static DEFAULT_OPTIONS = {
    classes: ["fvtt-mournblade", "sheet", "actor"],
    position: {
      width: 650,
      height: 720,
    },
    form: {
      submitOnChange: true,
      closeOnSubmit: false,
    },
    window: {
      resizable: true,
    },
    tabs: [
      {
        navSelector: 'nav[data-group="primary"]',
        contentSelector: "section.sheet-body",
        initial: "stats",
      },
    ],
    dragDrop: [{ dragSelector: ".item-list .item", dropSelector: null }],
    actions: {
      editImage: MournbladeActorSheet.#onEditImage,
      toggleSheet: MournbladeActorSheet.#onToggleSheet,
      editItem: MournbladeActorSheet.#onEditItem,
      deleteItem: MournbladeActorSheet.#onDeleteItem,
      createItem: MournbladeActorSheet.#onCreateItem,
      postItem: MournbladeActorSheet.#onPostItem,
      equipItem: MournbladeActorSheet.#onEquipItem,
      modifyQuantity: MournbladeActorSheet.#onModifyQuantity,
      modifySante: MournbladeActorSheet.#onModifySante,
      modifyAme: MournbladeActorSheet.#onModifyAme,
      rollAttribut: MournbladeActorSheet.#onRollAttribut,
      rollCompetence: MournbladeActorSheet.#onRollCompetence,
      rollRune: MournbladeActorSheet.#onRollRune,
      rollSortilege: MournbladeActorSheet.#onRollSortilege,
      preparePotion: MournbladeActorSheet.#onPreparePotion,
      invoquerElementaire: MournbladeActorSheet.#onInvoquerElementaire,
      bannirElementaire: MournbladeActorSheet.#onBannirElementaire,
      invoquerDemon: MournbladeActorSheet.#onInvoquerDemon,
      libererDemon: MournbladeActorSheet.#onLibererDemon,
      invoquerEsprit: MournbladeActorSheet.#onInvoquerEsprit,
      enchanter: MournbladeActorSheet.#onEnchanter,
      rollArmeOffensif: MournbladeActorSheet.#onRollArmeOffensif,
      rollArmeSpecial: MournbladeActorSheet.#onRollArmeSpecial,
      rollArmeDegats: MournbladeActorSheet.#onRollArmeDegats,
      rollAssommer: MournbladeActorSheet.#onRollAssommer,
      rollImmobiliser: MournbladeActorSheet.#onRollImmobiliser,
      rollFuir: MournbladeActorSheet.#onRollFuir,
    },
  }

  /**
   * Is the sheet currently in 'Play' mode?
   * @type {boolean}
   */
  get isPlayMode() {
    if (this._sheetMode === undefined) this._sheetMode = this.constructor.SHEET_MODES.PLAY
    return this._sheetMode === this.constructor.SHEET_MODES.PLAY
  }

  /**
   * Is the sheet currently in 'Edit' mode?
   * @type {boolean}
   */
  get isEditMode() {
    if (this._sheetMode === undefined) this._sheetMode = this.constructor.SHEET_MODES.PLAY
    return this._sheetMode === this.constructor.SHEET_MODES.EDIT
  }

  /**
   * Tab groups state
   * @type {object}
   */
  tabGroups = { primary: "stats" }

  /** @override */
  async _prepareContext() {
    const actor = this.document

    const context = {
      actor: actor,
      system: actor.system,
      source: actor.toObject(),
      fields: actor.schema.fields,
      systemFields: actor.system.schema.fields,
      isEditable: this.isEditable,
      isEditMode: this.isEditMode,
      isPlayMode: this.isPlayMode,
      isGM: game.user.isGM,
      config: game.system.mournblade.config,
      enrichedDescription: await foundry.applications.ux.TextEditor.implementation.enrichHTML(actor.system.biodata?.description || "", { async: true }),
    }
    return context
  }

  /** @override */
  _preSyncPartState(partId, newElement, priorElement, state) {
    super._preSyncPartState(partId, newElement, priorElement, state)
    // Save scrollable tab positions for deferred restoration in _onRender.
    // Tabs are hidden (display:none) at _syncPartState time, so scrollTop
    // assignments have no effect. We re-apply them after making tabs visible.
    const part = this.constructor.PARTS?.[partId]
    if (part?.scrollable) {
      this._pendingScrollRestores = part.scrollable.map(selector => {
        const el = selector ? priorElement.querySelector(selector) : priorElement
        return el ? { selector, scrollTop: el.scrollTop } : null
      }).filter(Boolean)
    }
  }

  /** @override */
  _onRender(context, options) {
    super._onRender(context, options)
    this.#dragDrop.forEach((d) => d.bind(this.element))

    // Handle edit-item-data changes
    this.element.querySelectorAll('.edit-item-data').forEach(element => {
      element.addEventListener('change', async (event) => {
        const target = event.currentTarget
        const itemElement = target.closest('[data-item-id]')
        if (!itemElement) return
        
        const itemId = itemElement.dataset.itemId
        const itemType = itemElement.dataset.itemType
        const itemField = target.dataset.itemField
        const dataType = target.dataset.dtype
        const value = target.value
        
        await this.document.editItemField(itemId, itemType, itemField, dataType, value)
      })
    })

    // Activate tab navigation manually
    const nav = this.element.querySelector('nav.tabs[data-group]')
    if (nav) {
      const group = nav.dataset.group
      const activeTab = this.tabGroups[group] || "stats"

      const switchTab = (tab) => {
        this.tabGroups[group] = tab
        nav.querySelectorAll('[data-tab]').forEach(link => {
          link.classList.toggle('active', link.dataset.tab === tab)
        })
        this.element.querySelectorAll('[data-group="' + group + '"][data-tab]').forEach(content => {
          content.classList.toggle('active', content.dataset.tab === tab)
        })
      }

      // Set initial state (makes active tab visible)
      switchTab(activeTab)

      // Restore scroll positions now that the active tab is visible
      if (this._pendingScrollRestores?.length) {
        for (const { selector, scrollTop } of this._pendingScrollRestores) {
          const el = selector ? this.element.querySelector(selector) : this.element
          if (el) el.scrollTop = scrollTop
        }
        this._pendingScrollRestores = null
      }

      // Tab clicks: DOM-only, no re-render (preserves scroll positions)
      nav.querySelectorAll('[data-tab]').forEach(link => {
        link.addEventListener('click', (event) => {
          event.preventDefault()
          switchTab(link.dataset.tab)
        })
      })
    }
  }

  // #region Drag-and-Drop Workflow
  /**
   * Create drag-and-drop workflow handlers for this Application
   */
  #createDragDropHandlers() {
    return []
  }

  // #region Actions
  /** @override */
  static ACTIONS = {
    editImage: MournbladeActorSheet.#onEditImage,
    toggleSheet: MournbladeActorSheet.#onToggleSheet,
    editItem: MournbladeActorSheet.#onEditItem,
    deleteItem: MournbladeActorSheet.#onDeleteItem,
    createItem: MournbladeActorSheet.#onCreateItem,
    equipItem: MournbladeActorSheet.#onEquipItem,
    modifyQuantity: MournbladeActorSheet.#onModifyQuantity,
    rollAttribut: MournbladeActorSheet.#onRollAttribut,
    rollCompetence: MournbladeActorSheet.#onRollCompetence,
    rollArmeOffensif: MournbladeActorSheet.#onRollArmeOffensif,
    rollArmeDegats: MournbladeActorSheet.#onRollArmeDegats,
    rollAssommer: MournbladeActorSheet.#onRollAssommer,
    rollImmobiliser: MournbladeActorSheet.#onRollImmobiliser,
    rollFuir: MournbladeActorSheet.#onRollFuir,
  }

  /**
   * Handle editing the actor image
   * @param {Event} event - The triggering event
   */
  static async #onEditImage(event, target) {
    event.preventDefault()
    const attr = target.dataset.edit || "img"
    const current = foundry.utils.getProperty(this.document, attr) ?? this.document.img
    const filePicker = new FilePicker({
      type: "image",
      current,
      callback: (path) => {
        this.document.update({ [attr]: path })
      },
    })
    filePicker.browse()
  }

  /**
   * Handle toggling the sheet mode
   * @param {Event} event - The triggering event
   */
  static async #onToggleSheet(event) {
    event.preventDefault()
    const sheet = this
    sheet._sheetMode = sheet._sheetMode === sheet.constructor.SHEET_MODES.PLAY ? sheet.constructor.SHEET_MODES.EDIT : sheet.constructor.SHEET_MODES.PLAY
    sheet.render()
  }

  /**
   * Handle editing an item
   * @param {Event} event - The triggering event
   * @param {HTMLElement} target - The target element
   */
  static async #onEditItem(event, target) {
    const li = target.closest(".item")
    const itemId = li?.dataset.itemId
    if (!itemId) return
    const item = this.actor.items.get(itemId)
    if (item) item.sheet.render(true)
  }

  /**
   * Handle deleting an item
   * @param {Event} event - The triggering event
   * @param {HTMLElement} target - The target element
   */
  static async #onDeleteItem(event, target) {
    const li = target.closest(".item")
    await MournbladeUtility.confirmDelete(this, li)
  }

  /**
   * Handle creating an item
   * @param {Event} event - The triggering event
   * @param {HTMLElement} target - The target element
   */
  static async #onCreateItem(event, target) {
    const itemType = target.dataset.type
    await this.actor.createEmbeddedDocuments("Item", [{ name: `Nouveau ${itemType}`, type: itemType }], { renderSheet: true })
  }

  /**
   * Handle posting an item to chat
   */
  static async #onPostItem(event, target) {
    const li = target.closest(".item")
    const itemId = li?.dataset.itemId ?? target.dataset.itemId
    if (!itemId) return
    MournbladeUtility.postItemToChat(this.actor.id, itemId)
  }

  /**
   * Handle equipping an item
   * @param {Event} event - The triggering event
   * @param {HTMLElement} target - The target element
   */
  static async #onEquipItem(event, target) {
    const li = target.closest(".item")
    const itemId = li?.dataset.itemId
    if (!itemId) return
    const item = this.actor.items.get(itemId)
    if (item) {
      await item.update({ "system.equipped": !item.system.equipped })
    }
  }

  /**
   * Handle modifying the quantity of an item
   * @param {Event} event - The triggering event
   * @param {HTMLElement} target - The target element
   */
  static async #onModifyQuantity(event, target) {
    const li = target.closest('[data-item-id]')
    const itemId = li?.dataset.itemId
    const value = Number.parseInt(target.dataset.quantiteValue)
    const item = this.document.items.get(itemId)
    if (item) {
      const newQuantity = Math.max(0, (item.system.quantite || 0) + value)
      await item.update({ "system.quantite": newQuantity })
    }
  }

  /**
   * Handle modifying santé (health) values
   * @param {Event} event - The triggering event
   * @param {HTMLElement} target - The target element
   */
  static async #onModifySante(event, target) {
    const type = target.dataset.type
    const value = Number.parseInt(target.dataset.value)
    const actor = this.document
    const currentValue = actor.system.sante[type] || 0
    const newValue = Math.max(0, currentValue + value)
    await actor.update({ [`system.sante.${type}`]: newValue })
  }

  /**
   * Handle modifying âme (soul) value
   * @param {Event} event - The triggering event
   * @param {HTMLElement} target - The target element
   */
  static async #onModifyAme(event, target) {
    const value = Number.parseInt(target.dataset.value)
    const actor = this.document
    const currentValue = actor.system.ame.value || 0
    const newValue = Math.max(0, currentValue + value)
    await actor.update({ "system.ame.value": newValue })
  }

  /**
   * Handle rolling an attribut
   * @param {Event} event - The triggering event
   */
  static async #onRollAttribut(event, target) {
    event.preventDefault()
    const sheet = this
    const attrKey = target.dataset.attrKey
    const actor = sheet.document
    await actor.rollAttribut(attrKey)
  }

  /**
   * Handle rolling a competence
   * @param {Event} event - The triggering event
   */
  static async #onRollCompetence(event, target) {
    event.preventDefault()
    const sheet = this
    const attrKey = target.dataset.attrKey
    const li = target.closest('[data-item-id]')
    const compId = li?.dataset.itemId
    const actor = sheet.document
    await actor.rollCompetence(attrKey, compId)
  }

  /**
   * Handle rolling a rune
   * @param {Event} event - The triggering event
   */
  static async #onRollRune(event, target) {
    event.preventDefault()
    const sheet = this
    const li = target.closest('[data-item-id]')
    const runeId = li?.dataset.itemId
    const actor = sheet.document
    await actor.rollRune(runeId)
  }

  /**
   * Handle launching a sortilège (multi-rune spell)
   */
  static async #onRollSortilege(event, target) {
    event.preventDefault()
    await this.document.rollSortilege()
  }

  /**
   * Handle preparing a potion
   */
  static async #onPreparePotion(event, target) {
    event.preventDefault()
    await this.document.preparePotion()
  }

  /**
   * Handle invoking an elemental
   */
  static async #onInvoquerElementaire(event, target) {
    event.preventDefault()
    await this.document.invoquerElementaire()
  }

  /**
   * Handle banishing an elemental invocation
   */
  static async #onBannirElementaire(event, target) {
    event.preventDefault()
    const invocIndex = parseInt(target.dataset.invocIndex ?? "0")
    await this.document.bannirElementaire(invocIndex)
  }

  /**
   * Handle invoking a demon
   */
  static async #onInvoquerDemon(event, target) {
    event.preventDefault()
    await this.document.invoquerDemon()
  }

  /**
   * Handle releasing a demon invocation
   */
  static async #onLibererDemon(event, target) {
    event.preventDefault()
    const invocIndex = parseInt(target.dataset.invocIndex ?? "0")
    await this.document.libererDemon(invocIndex)
  }

  /**
   * Handle invoking a Law Spirit into an Automaton
   */
  static async #onInvoquerEsprit(event, target) {
    event.preventDefault()
    await this.document.invoquerEspritLoi()
  }

  /**
   * Handle enchanting an item with Loi power
   */
  static async #onEnchanter(event, target) {
    event.preventDefault()
    const itemId = target.dataset.itemId
    await this.document.enchanter(itemId)
  }

  /**
   * Handle rolling an arme offensif
   * @param {Event} event - The triggering event
   */
  static async #onRollArmeOffensif(event, target) {
    event.preventDefault()
    const sheet = this
    const armeId = target.dataset.armeId
    const actor = sheet.document
    await actor.rollArmeOffensif(armeId)
  }

  /**
   * Handle rolling an arme degats
   * @param {Event} event - The triggering event
   */
  static async #onRollArmeDegats(event, target) {
    event.preventDefault()
    const sheet = this
    const armeId = target.dataset.armeId
    const actor = sheet.document
    await actor.rollArmeDegats(armeId)
  }

  /**
   * Handle rolling an arme special
   * @param {Event} event - The triggering event
   */
  static async #onRollArmeSpecial(event, target) {
    event.preventDefault()
    const sheet = this
    const armeId = target.dataset.armeId
    const actor = sheet.document
    await actor.rollArmeSpecial(armeId)
  }

  /**
   * Handle rolling an assommer
   * @param {Event} event - The triggering event
   */
  static async #onRollAssommer(event, target) {
    event.preventDefault()
    const sheet = this
    const actor = sheet.document
    await actor.rollAssomer()
  }

  /**
   * Handle rolling an immobiliser
   * @param {Event} event - The triggering event
   */
  static async #onRollImmobiliser(event, target) {
    event.preventDefault()
    const sheet = this
    const actor = sheet.document
    await actor.rollImmobiliser()
  }

  /**
   * Handle rolling a fuir
   * @param {Event} event - The triggering event
   */
  static async #onRollFuir(event, target) {
    event.preventDefault()
    const sheet = this
    const actor = sheet.document
    await actor.rollFuir()
  }
}