const { HandlebarsApplicationMixin } = foundry.applications.api

import { HeritiersUtility } from "../../heritiers-utility.js"

export default class HeritiersActorSheet 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-les-heritiers", "sheet", "actor"],
    position: {
      width: 780,
      height: 840,
    },
    window: {
      resizable: true,
    },
    form: {
      submitOnChange: true,
      closeOnSubmit: false,
    },
    dragDrop: [{ dragSelector: ".item-list .item", dropSelector: "form" }],
    actions: {
      editImage: HeritiersActorSheet.#onEditImage,
      toggleSheet: HeritiersActorSheet.#onToggleSheet,
      editItem: HeritiersActorSheet.#onEditItem,
      deleteItem: HeritiersActorSheet.#onDeleteItem,
      createItem: HeritiersActorSheet.#onCreateItem,
      equipItem: HeritiersActorSheet.#onEquipItem,
      modifyQuantity: HeritiersActorSheet.#onModifyQuantity,
      quantityIncrease: HeritiersActorSheet.#onQuantityIncrease,
      quantityDecrease: HeritiersActorSheet.#onQuantityDecrease,
      pvIncrease: HeritiersActorSheet.#onPvIncrease,
      pvDecrease: HeritiersActorSheet.#onPvDecrease,
      rollInitiative: HeritiersActorSheet.#onRollInitiative,
      rollCarac: HeritiersActorSheet.#onRollCarac,
      rollRang: HeritiersActorSheet.#onRollRang,
      rollRootCompetence: HeritiersActorSheet.#onRollRootCompetence,
      rollCompetence: HeritiersActorSheet.#onRollCompetence,
      rollSort: HeritiersActorSheet.#onRollSort,
      rollAttaqueArme: HeritiersActorSheet.#onRollAttaqueArme,
      rollAttaqueBrutaleArme: HeritiersActorSheet.#onRollAttaqueBrutaleArme,
      rollAttaqueChargeArme: HeritiersActorSheet.#onRollAttaqueChargeArme,
      rollAssomerArme: HeritiersActorSheet.#onRollAssomerArme,
      rollPouvoir: HeritiersActorSheet.#onRollPouvoir,
      toggleMasque: HeritiersActorSheet.#onToggleMasque,
      dialogRecupUsage: HeritiersActorSheet.#onDialogRecupUsage,
    },
  }

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

  /** @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: CONFIG.HERITIERS,
      enrichedDescription: await foundry.applications.ux.TextEditor.implementation.enrichHTML(actor.system.biodata?.description || "", { async: true }),
      enrichedHabitat: await foundry.applications.ux.TextEditor.implementation.enrichHTML(actor.system.biodata?.habitat || "", { async: true }),
      enrichedRevesetranges: await foundry.applications.ux.TextEditor.implementation.enrichHTML(actor.system.biodata?.revesetranges || "", { async: true }),
      enrichedSecretsdecouverts: await foundry.applications.ux.TextEditor.implementation.enrichHTML(actor.system.biodata?.secretsdecouverts || "", { async: true }),
      enrichedQuestions: await foundry.applications.ux.TextEditor.implementation.enrichHTML(actor.system.biodata?.questions || "", { async: true }),
      enrichedPlayernotes: await foundry.applications.ux.TextEditor.implementation.enrichHTML(actor.system.biodata?.playernotes || "", { async: true }),
    }

    return context
  }

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

    // Activate drag & drop handlers
    this.#dragDrop.forEach(d => d.bind(this.element))

    // Manual tab navigation
    const html = this.element
    const tabLinks = html.querySelectorAll('.sheet-tabs a.item[data-tab]')
    const tabContents = html.querySelectorAll('.sheet-body .tab[data-group="primary"]')

    // Hide all tabs initially
    tabContents.forEach(tab => {
      tab.classList.remove('active')
      tab.style.display = 'none'
    })

    // Show active tab
    const activeTab = this.tabGroups.primary
    const activeTabContent = html.querySelector(`.tab[data-group="primary"][data-tab="${activeTab}"]`)
    if (activeTabContent) {
      activeTabContent.classList.add('active')
      activeTabContent.style.display = 'block'
    }

    // Activate the corresponding nav link
    tabLinks.forEach(link => {
      if (link.dataset.tab === activeTab) {
        link.classList.add('active')
      } else {
        link.classList.remove('active')
      }
    })

    // Tab click handler
    tabLinks.forEach(link => {
      link.addEventListener('click', (event) => {
        event.preventDefault()
        const tab = link.dataset.tab

        // Update state
        this.tabGroups.primary = tab

        // Hide all tabs
        tabContents.forEach(t => {
          t.classList.remove('active')
          t.style.display = 'none'
        })

        // Show selected tab
        const selectedTab = html.querySelector(`.tab[data-group="primary"][data-tab="${tab}"]`)
        if (selectedTab) {
          selectedTab.classList.add('active')
          selectedTab.style.display = 'block'
        }

        // Update nav links
        tabLinks.forEach(l => {
          if (l.dataset.tab === tab) {
            l.classList.add('active')
          } else {
            l.classList.remove('active')
          }
        })
      })
    })

    // Inline item editing
    html.querySelectorAll('.edit-item-data').forEach(input => {
      input.addEventListener('change', (event) => {
        const li = event.target.closest('.item')
        const itemId = li?.dataset.itemId
        const itemType = li?.dataset.itemType
        const itemField = event.target.dataset.itemField
        const dataType = event.target.dataset.dtype
        // Pour les checkboxes, utiliser checked au lieu de value
        const value = event.target.type === 'checkbox' ? event.target.checked : event.target.value
        if (itemId && itemType && itemField) {
          this.actor.editItemField(itemId, itemType, itemField, dataType, value)
        }
      })
    })
  }

  // #region Drag & Drop

  /**
   * Create drag-and-drop workflow handlers for this Application
   * @returns {DragDrop[]}     An array of DragDrop handlers
   * @private
   */
  #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),
        drop: this._onDrop.bind(this),
      }
      return new foundry.applications.ux.DragDrop(d)
    })
  }

  /**
   * Define whether a user is able to begin a dragstart workflow for a given drag selector
   * @param {string} selector       The candidate HTML selector for dragging
   * @returns {boolean}             Can the current user drag this selector?
   * @protected
   */
  _canDragStart(selector) {
    return this.isEditable
  }

  /**
   * Define whether a user is able to conclude a drag-and-drop workflow for a given drop selector
   * @param {string} selector       The candidate HTML selector for the drop target
   * @returns {boolean}             Can the current user drop on this selector?
   * @protected
   */
  _canDragDrop(selector) {
    return this.isEditable
  }

  /**
   * Callback actions which occur at the beginning of a drag start workflow.
   * @param {DragEvent} event       The originating DragEvent
   * @protected
   */
  _onDragStart(event) {
    const li = event.currentTarget.closest(".item")
    if (!li?.dataset.itemId) return
    const item = this.actor.items.get(li.dataset.itemId)
    if (!item) return

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

  /**
   * Callback actions which occur when a dragged element is dropped on a target.
   * @param {DragEvent} event       The originating DragEvent
   * @protected
   */
  async _onDrop(event) {
    const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event)
    const actor = this.actor

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

  /**
   * Handle dropping an Item on the actor sheet
   * @param {DragEvent} event
   * @param {object} data
   * @private
   */
  async _onDropItem(event, data) {
    if (!this.actor.isOwner) return false

    let item = await fromUuid(data.uuid)
    if (item.pack) {
      item = await HeritiersUtility.searchItem(item)
    }

    const itemData = item.toObject ? item.toObject() : item
    const createdItems = await this.actor.createEmbeddedDocuments("Item", [itemData])

    for (const createdItem of createdItems) {
      this.#notifyDroppedItemVisibility(createdItem)
    }

    return createdItems
  }

  /**
   * Notify the user when a dropped magic item will not be visible in the Magie tab.
   * @param {Item} item
   */
  #notifyDroppedItemVisibility(item) {
    if (!item) return

    const magieActivee = this.actor.system?.biodata?.magie !== false
    if ((item.type === "sort" || this.#isMagicCompetence(item)) && !magieActivee) {
      ui.notifications.info("Élément de magie ajouté, mais l'onglet Magie reste masqué tant que « Fiche de Magie ? » n'est pas activé dans la fiche.")
    }

    if (item.type !== "sort") return

    const competenceName = item.system?.competence?.trim()
    if (!competenceName) {
      ui.notifications.warn("Sort ajouté, mais il n'apparaîtra pas dans l'onglet Magie tant que sa compétence de magie n'est pas renseignée.")
      return
    }

    const competence = this.actor.items.find(actorItem =>
      actorItem.type === "competence"
      && actorItem.system?.profil === "magie"
      && actorItem.name === competenceName)

    if (!competence) {
      ui.notifications.warn(`Sort ajouté, mais il n'apparaîtra pas dans l'onglet Magie tant que la compétence magique « ${competenceName} » n'existe pas sur l'acteur.`)
      return
    }

    if (competenceName === "Magie du Clan" && !["soufflecombat", "soufflemouvement", "souffleesprit"].includes(item.system?.souffle)) {
      ui.notifications.warn("Sort ajouté, mais il n'apparaîtra pas dans l'onglet Magie tant que son souffle n'est pas renseigné dans sa fiche.")
    }
  }

  /**
   * @param {Item} item
   * @returns {boolean}
   */
  #isMagicCompetence(item) {
    return item?.type === "competence" && item.system?.profil === "magie"
  }

  /**
   * Handle dropping an Actor on the sheet
   * @param {DragEvent} event
   * @param {object} data
   * @private
   */
  async _onDropActor(event, data) {
    return false
  }

  /**
   * Handle dropping an ActiveEffect on the sheet
   * @param {DragEvent} event
   * @param {object} data
   * @private
   */
  async _onDropActiveEffect(event, data) {
    return false
  }

  // #endregion

  // #region Action Handlers

  /**
   * Toggle between edit and play mode
   * @param {Event} event
   * @param {HTMLElement} target
   * @private
   */
  static #onToggleSheet(event, target) {
    const wasEditMode = this.isEditMode
    this._sheetMode = wasEditMode ? this.constructor.SHEET_MODES.PLAY : this.constructor.SHEET_MODES.EDIT
    this.render({ force: true })
  }

  /**
   * Edit the actor image
   * @param {Event} event
   * @param {HTMLElement} target
   * @private
   */
  static async #onEditImage(event, target) {
    const attr = target.dataset.edit || "img"
    const current = foundry.utils.getProperty(this.actor, attr) ?? this.actor.img
    const fp = new FilePicker({
      type: "image",
      current,
      callback: (path) => {
        this.actor.update({ [attr]: path })
      },
    })
    return fp.browse()
  }

  /**
   * Edit an item
   * @param {Event} event
   * @param {HTMLElement} target
   * @private
   */
  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)
  }

  /**
   * Delete an item
   * @param {Event} event
   * @param {HTMLElement} target
   * @private
   */
  static async #onDeleteItem(event, target) {
    const li = target.closest(".item")
    await HeritiersUtility.confirmDelete(this, li)
  }

  /**
   * Create a new item
   * @param {Event} event
   * @param {HTMLElement} target
   * @private
   */
  static async #onCreateItem(event, target) {
    const itemType = target.dataset.type

    // Cas spécial pour les sorts avec une compétence spécifique
    if (itemType === "sort" && target.dataset.sortCompetence) {
      const sortCompetence = target.dataset.sortCompetence
      await this.actor.createEmbeddedDocuments('Item', [{
        name: `Nouveau ${itemType} de ${sortCompetence}`,
        type: itemType,
        system: { competence: sortCompetence }
      }], { renderSheet: true })
      return
    }

    await this.actor.createEmbeddedDocuments("Item", [{ name: `Nouveau ${itemType}`, type: itemType }], { renderSheet: true })
  }

  /**
   * Equip/unequip an item
   * @param {Event} event
   * @param {HTMLElement} target
   * @private
   */
  static async #onEquipItem(event, target) {
    const li = target.closest(".item")
    const itemId = li?.dataset.itemId
    if (itemId) {
      await this.actor.equipItem(itemId)
      this.render()
    }
  }

  /**
   * Modify item quantity
   * @param {Event} event
   * @param {HTMLElement} target
   * @private
   */
  static async #onModifyQuantity(event, target) {
    const li = target.closest(".item")
    const itemId = li?.dataset.itemId
    const value = Number(target.dataset.quantiteValue)
    if (itemId) {
      await this.actor.incDecQuantity(itemId, value)
    }
  }

  /**
   * Increase item quantity
   * @param {Event} event
   * @param {HTMLElement} target
   * @private
   */
  static async #onQuantityIncrease(event, target) {
    const li = target.closest(".item")
    const itemId = li?.dataset.itemId
    if (itemId) {
      await this.actor.incDecQuantity(itemId, 1)
    }
  }

  /**
   * Decrease item quantity
   * @param {Event} event
   * @param {HTMLElement} target
   * @private
   */
  static async #onQuantityDecrease(event, target) {
    const li = target.closest(".item")
    const itemId = li?.dataset.itemId
    if (itemId) {
      await this.actor.incDecQuantity(itemId, -1)
    }
  }

  /**
   * Increase PV
   * @param {Event} event
   * @param {HTMLElement} target
   * @private
   */
  static async #onPvIncrease(event, target) {
    const currentPv = this.actor.system.pv.value || 0
    const maxPv = this.actor.system.pv.max || 0
    const newPv = Math.min(currentPv + 1, maxPv)
    await this.actor.update({ 'system.pv.value': newPv })
  }

  /**
   * Decrease PV
   * @param {Event} event
   * @param {HTMLElement} target
   * @private
   */
  static async #onPvDecrease(event, target) {
    const currentPv = this.actor.system.pv.value || 0
    const newPv = Math.max(currentPv - 1, 0)
    await this.actor.update({ 'system.pv.value': newPv })
  }

  /**
   * Roll initiative
   * @param {Event} event
   * @param {HTMLElement} target
   * @private
   */
  static async #onRollInitiative(event, target) {
    await this.actor.rollInitiative()
  }

  /**
   * Roll caractéristique
   * @param {Event} event
   * @param {HTMLElement} target
   * @private
   */
  static async #onRollCarac(event, target) {
    const key = target.dataset.key
    if (key) {
      await this.actor.rollCarac(key, false)
    }
  }

  /**
   * Roll rang
   * @param {Event} event
   * @param {HTMLElement} target
   * @private
   */
  static async #onRollRang(event, target) {
    const key = target.dataset.rangKey
    if (key) {
      await this.actor.rollRang(key)
    }
  }

  /**
   * Roll root competence
   * @param {Event} event
   * @param {HTMLElement} target
   * @private
   */
  static async #onRollRootCompetence(event, target) {
    const compKey = target.dataset.attrKey
    if (compKey) {
      await this.actor.rollRootCompetence(compKey)
    }
  }

  /**
   * Roll competence
   * @param {Event} event
   * @param {HTMLElement} target
   * @private
   */
  static async #onRollCompetence(event, target) {
    const li = target.closest(".item")
    const compId = li?.dataset.itemId
    if (compId) {
      await this.actor.rollCompetence(compId)
    }
  }

  /**
   * Roll sort
   * @param {Event} event
   * @param {HTMLElement} target
   * @private
   */
  static async #onRollSort(event, target) {
    const li = target.closest(".item")
    const sortId = li?.dataset.itemId
    if (sortId) {
      await this.actor.rollSort(sortId)
    }
  }

  /**
   * Roll attaque arme
   * @param {Event} event
   * @param {HTMLElement} target
   * @private
   */
  static async #onRollAttaqueArme(event, target) {
    const li = target.closest(".item")
    const armeId = li?.dataset.itemId
    if (armeId) {
      await this.actor.rollAttaqueArme(armeId)
    }
  }

  /**
   * Roll attaque brutale arme
   * @param {Event} event
   * @param {HTMLElement} target
   * @private
   */
  static async #onRollAttaqueBrutaleArme(event, target) {
    const li = target.closest(".item")
    const armeId = li?.dataset.itemId
    if (armeId) {
      await this.actor.rollAttaqueBrutaleArme(armeId)
    }
  }

  /**
   * Roll attaque charge arme
   * @param {Event} event
   * @param {HTMLElement} target
   * @private
   */
  static async #onRollAttaqueChargeArme(event, target) {
    const li = target.closest(".item")
    const armeId = li?.dataset.itemId
    if (armeId) {
      await this.actor.rollAttaqueChargeArme(armeId)
    }
  }

  /**
   * Roll assomer arme
   * @param {Event} event
   * @param {HTMLElement} target
   * @private
   */
  static async #onRollAssomerArme(event, target) {
    const li = target.closest(".item")
    const armeId = li?.dataset.itemId
    if (armeId) {
      await this.actor.rollAssomerArme(armeId)
    }
  }

  /**
   * Roll pouvoir
   * @param {Event} event
   * @param {HTMLElement} target
   * @private
   */
  static async #onRollPouvoir(event, target) {
    const li = target.closest(".item")
    const pouvoirId = li?.dataset.itemId
    if (pouvoirId) {
      await this.actor.rollPouvoir(pouvoirId)
    }
  }

  /**
   * Toggle masque
   * @param {Event} event
   * @param {HTMLElement} target
   * @private
   */
  static async #onToggleMasque(event, target) {
    await this.actor.toggleMasqueStatut()
    this.render()
  }

  /**
   * Dialog récupération usage
   * @param {Event} event
   * @param {HTMLElement} target
   * @private
   */
  static async #onDialogRecupUsage(event, target) {
    new Dialog({
      title: "Récupération des Points d'Usage",
      content: "<p>Combien de Points d'Usage souhaitez-vous récupérer ?</p>",
      buttons: {
        one: {
          icon: '<i class="fas fa-check"></i>',
          label: "1 Point",
          callback: () => {
            this.actor.recupUsage(1)
          }
        },
        two: {
          icon: '<i class="fas fa-check"></i>',
          label: "2 Points",
          callback: () => {
            this.actor.recupUsage(2)
          }
        },
        three: {
          icon: '<i class="fas fa-check"></i>',
          label: "3 Points",
          callback: () => {
            this.actor.recupUsage(3)
          }
        },
        cancel: {
          icon: '<i class="fas fa-times"></i>',
          label: "Annuler"
        }
      },
      default: "one"
    }).render(true)
  }

  // #endregion
}
