const { HandlebarsApplicationMixin } = foundry.applications.api

import { YggdrasillUtility } from "../../yggdrasill-utility.js"

export default class YggdrasillActorSheet 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
  }

  #dragDrop

  /** @override */
  static DEFAULT_OPTIONS = {
    classes: ["fvtt-yggdrasill", "sheet", "actor"],
    position: {
      width: 750,
      height: 720,
    },
    form: {
      submitOnChange: true,
      closeOnSubmit: false,
    },
    window: {
      resizable: true,
    },
    tabs: [
      {
        navSelector: 'nav[data-group="primary"]',
        contentSelector: "section.sheet-body",
        initial: "principal",
      },
    ],
    dragDrop: [{ dragSelector: ".item-list .item", dropSelector: null }],
    actions: {
      editImage: YggdrasillActorSheet.#onEditImage,
      toggleSheet: YggdrasillActorSheet.#onToggleSheet,
      editItem: YggdrasillActorSheet.#onEditItem,
      deleteItem: YggdrasillActorSheet.#onDeleteItem,
      createItem: YggdrasillActorSheet.#onCreateItem,
      equipItem: YggdrasillActorSheet.#onEquipItem,
      rollCarac: YggdrasillActorSheet.#onRollCarac,
      rollCompetence: YggdrasillActorSheet.#onRollCompetence,
      rollArme: YggdrasillActorSheet.#onRollArme,
      rollSort: YggdrasillActorSheet.#onRollSort,
      rollProuesse: YggdrasillActorSheet.#onRollProuesse,
      rollDamage: YggdrasillActorSheet.#onRollDamage,
      lockUnlock: YggdrasillActorSheet.#onLockUnlock,
      incrementPV: YggdrasillActorSheet.#onIncrementPV,
      decrementPV: YggdrasillActorSheet.#onDecrementPV,
      updateCompetence: YggdrasillActorSheet.#onUpdateCompetence,
    },
  }

  /**
   * 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: "principal",
  }

  /** @override */
  async _prepareContext() {
    const context = {
      fields: this.document.schema.fields,
      systemFields: this.document.system.schema.fields,
      actor: this.document,
      system: this.document.system,
      source: this.document.toObject(),
      isEditMode: this.isEditMode,
      isPlayMode: this.isPlayMode,
      isEditable: this.isEditable,
      isGM: game.user.isGM,
      config: game.system.yggdrasill.config,
      editScore: this.isEditMode,
    }
    return context
  }

  /** @override */
  _onRender(context, options) {
    super._onRender(context, options)

    // Activate tab navigation manually
    const nav = this.element.querySelector('nav.tabs[data-group], nav.sheet-tabs[data-group]')
    if (nav) {
      const group = nav.dataset.group
      // Activate the current tab
      const activeTab = this.tabGroups[group] || "principal"
      nav.querySelectorAll('[data-tab]').forEach(link => {
        const tab = link.dataset.tab
        link.classList.toggle('active', tab === activeTab)
        link.addEventListener('click', (event) => {
          event.preventDefault()
          this.tabGroups[group] = tab
          this.render()
        })
      })

      // Show/hide tab content
      this.element.querySelectorAll('[data-group="' + group + '"][data-tab]').forEach(content => {
        content.classList.toggle('active', content.dataset.tab === activeTab)
      })
    }

    // Add change listener for competence niveau selects
    this.element.querySelectorAll('select.competence-niveau').forEach(select => {
      select.addEventListener('change', async (event) => {
        const itemId = event.target.dataset.itemId
        const item = this.document.items.get(itemId)
        if (item) {
          const newNiveau = parseInt(event.target.value)
          await item.update({ "system.niveau": newNiveau })
        }
      })
    })
  }

  /**
   * Creates drag-and-drop handlers for this application
   * @returns {DragDrop[]} An array of DragDrop handlers
   * @private
   */
  #createDragDropHandlers() {
    return []
  }

  /**
   * Handle changing a Document's image
   * @this {YggdrasillActorSheet}
   * @param {PointerEvent} event - The triggering event
   * @param {HTMLElement} target - The button element
   */
  static async #onEditImage(event, target) {
    const attr = target.dataset.edit
    const current = foundry.utils.getProperty(this.document, attr)
    const fp = new FilePicker({
      current,
      type: "image",
      callback: (path) => {
        this.document.update({ [attr]: path })
      },
    })
    return fp.browse()
  }

  /**
   * Toggle sheet mode between Edit and Play
   * @this {YggdrasillActorSheet}
   * @param {PointerEvent} event - The triggering event
   * @param {HTMLElement} target - The button element
   */
  static #onToggleSheet(event, target) {
    this._sheetMode = this.isEditMode
      ? this.constructor.SHEET_MODES.PLAY
      : this.constructor.SHEET_MODES.EDIT
    this.render()
  }

  /**
   * Handle item editing
   * @this {YggdrasillActorSheet}
   * @param {PointerEvent} event - The triggering event
   * @param {HTMLElement} target - The button element
   */
  static #onEditItem(event, target) {
    const itemId = target.closest("[data-item-id]").dataset.itemId
    const item = this.document.items.get(itemId)
    if (item) item.sheet.render(true)
  }

  /**
   * Handle item deletion
   * @this {YggdrasillActorSheet}
   * @param {PointerEvent} event - The triggering event
   * @param {HTMLElement} target - The button element
   */
  static async #onDeleteItem(event, target) {
    const itemId = target.closest("[data-item-id]").dataset.itemId
    const item = this.document.items.get(itemId)
    if (item) {
      await item.delete()
    }
  }

  /**
   * Handle item creation
   * @this {YggdrasillActorSheet}
   * @param {PointerEvent} event - The triggering event
   * @param {HTMLElement} target - The button element
   */
  static async #onCreateItem(event, target) {
    const itemType = target.dataset.itemType
    const itemData = {
      name: `Nouveau ${itemType}`,
      type: itemType,
    }
    await this.document.createEmbeddedDocuments("Item", [itemData])
  }

  /**
   * Handle item equip toggle
   * @this {YggdrasillActorSheet}
   * @param {PointerEvent} event - The triggering event
   * @param {HTMLElement} target - The button element
   */
  static async #onEquipItem(event, target) {
    const itemId = target.closest("[data-item-id]").dataset.itemId
    const item = this.document.items.get(itemId)
    if (item && item.system.equipe !== undefined) {
      await item.update({ "system.equipe": !item.system.equipe })
    }
  }

  /**
   * Handle characteristic roll
   * @this {YggdrasillActorSheet}
   * @param {PointerEvent} event - The triggering event
   * @param {HTMLElement} target - The button element
   */
  static #onRollCarac(event, target) {
    const caracCateg = target.dataset.caracCateg
    const caracKey = target.dataset.caracKey
    this.document.rollCarac(caracCateg, caracKey)
  }

  /**
   * Handle competence roll
   * @this {YggdrasillActorSheet}
   * @param {PointerEvent} event - The triggering event
   * @param {HTMLElement} target - The button element
   */
  static #onRollCompetence(event, target) {
    const itemId = target.closest("[data-item-id]").dataset.itemId
    this.document.rollCompetence(itemId)
  }

  /**
   * Handle weapon roll
   * @this {YggdrasillActorSheet}
   * @param {PointerEvent} event - The triggering event
   * @param {HTMLElement} target - The button element
   */
  static #onRollArme(event, target) {
    const itemId = target.closest("[data-item-id]").dataset.itemId
    this.document.rollArme(itemId)
  }

  /**
   * Handle lock/unlock toggle
   * @this {YggdrasillActorSheet}
   * @param {PointerEvent} event - The triggering event
   * @param {HTMLElement} target - The button element
   */
  static #onLockUnlock(event, target) {
    this._sheetMode = this.isEditMode
      ? this.constructor.SHEET_MODES.PLAY
      : this.constructor.SHEET_MODES.EDIT
    this.render()
  }

  /**
   * Handle incrementing PV
   * @this {YggdrasillActorSheet}
   * @param {PointerEvent} event - The triggering event
   * @param {HTMLElement} target - The button element
   */
  static async #onIncrementPV(event, target) {
    const currentPV = this.document.system.caracsecondaire.pv.value || 0
    const maxPV = this.document.system.caracsecondaire.pv.max || 0
    const newPV = Math.min(currentPV + 1, maxPV)
    await this.document.update({ "system.caracsecondaire.pv.value": newPV })
  }

  /**
   * Handle decrementing PV
   * @this {YggdrasillActorSheet}
   * @param {PointerEvent} event - The triggering event
   * @param {HTMLElement} target - The button element
   */
  static async #onDecrementPV(event, target) {
    const currentPV = this.document.system.caracsecondaire.pv.value || 0
    const newPV = Math.max(currentPV - 1, 0)
    await this.document.update({ "system.caracsecondaire.pv.value": newPV })
  }

  /**
   * Handle competence niveau update
   * @this {YggdrasillActorSheet}
   * @param {Event} event - The triggering event
   * @param {HTMLElement} target - The select element
   */
  static async #onUpdateCompetence(event, target) {
    const itemId = target.dataset.itemId
    const item = this.document.items.get(itemId)
    if (!item) return
    const newNiveau = parseInt(target.value)
    await item.update({ "system.niveau": newNiveau })
  }

  /**
   * Handle sort roll
   * @this {YggdrasillActorSheet}
   * @param {PointerEvent} event - The triggering event
   * @param {HTMLElement} target - The button element
   */
  static #onRollSort(event, target) {
    const itemId = target.closest("[data-item-id]").dataset.itemId
    const sortType = target.dataset.sortType || "sejdr"
    this.document.rollSort(itemId, sortType)
  }

  /**
   * Handle prouesse roll
   * @this {YggdrasillActorSheet}
   * @param {PointerEvent} event - The triggering event
   * @param {HTMLElement} target - The button element
   */
  static #onRollProuesse(event, target) {
    const itemId = target.closest("[data-item-id]").dataset.itemId
    this.document.rollProuesse(itemId)
  }

  /**
   * Handle damage roll
   * @this {YggdrasillActorSheet}
   * @param {PointerEvent} event - The triggering event
   * @param {HTMLElement} target - The button element
   */
  static #onRollDamage(event, target) {
    const itemId = target.closest("[data-item-id]").dataset.itemId
    const weapon = this.document.items.get(itemId)
    if (weapon) {
      this.document.rollDamage(weapon, 'damage')
    }
  }

  /**
   * Handle beginning of a drag operation
   * @param {DragEvent} event - The originating drag event
   * @protected
   */
  _onDragStart(event) {
    const li = event.currentTarget
    const itemId = li.dataset.itemId
    const item = this.document.items.get(itemId)
    if (!item) return

    const dragData = item.toDragData()
    event.dataTransfer.setData("text/plain", JSON.stringify(dragData))
  }

  /**
   * Handle a drop event
   * @param {DragEvent} event - The originating drop event
   * @protected
   */
  async _onDrop(event) {
    const data = TextEditor.getDragEventData(event)
    const actor = this.document

    // Handle different data types
    switch (data.type) {
      case "Item":
        return this._onDropItem(event, data)
      case "ActiveEffect":
        return this._onDropActiveEffect(event, data)
    }
  }

  /**
   * Handle dropping an Item on the sheet
   * @param {DragEvent} event - The originating drop event
   * @param {object} data - The dropped data
   * @protected
   */
  async _onDropItem(event, data) {
    if (!this.isEditable) return false
    const item = await Item.implementation.fromDropData(data)
    const itemData = item.toObject()

    // Handle item from same actor
    if (this.document.uuid === item.parent?.uuid) return this._onSortItem(event, itemData)

    // Create the item
    return this._onDropItemCreate(itemData)
  }

  /**
   * Handle creating an owned item from drop data
   * @param {object} itemData - The item data to create
   * @protected
   */
  async _onDropItemCreate(itemData) {
    itemData = itemData instanceof Array ? itemData : [itemData]
    return this.document.createEmbeddedDocuments("Item", itemData)
  }

  /**
   * Handle sorting items
   * @param {DragEvent} event - The originating drop event
   * @param {object} itemData - The item data being sorted
   * @protected
   */
  _onSortItem(event, itemData) {
    // Implement sorting logic if needed
    return Promise.resolve()
  }

  /**
   * Handle dropping an ActiveEffect on the sheet
   * @param {DragEvent} event - The originating drop event
   * @param {object} data - The dropped data
   * @protected
   */
  async _onDropActiveEffect(event, data) {
    const effect = await ActiveEffect.implementation.fromDropData(data)
    if (!this.isEditable || !effect) return false

    if (this.document.uuid === effect.parent?.uuid) return false
    return ActiveEffect.create(effect.toObject(), { parent: this.document })
  }
}
