const { HandlebarsApplicationMixin } = foundry.applications.api

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

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

  #dragDrop

  /** @override */
  static DEFAULT_OPTIONS = {
    classes: ["fvtt-cthulhu-eternal", "actor"],
    position: {
      width: 1400,
      height: "auto",
    },
    form: {
      submitOnChange: true,
    },
    window: {
      resizable: true,
    },
    dragDrop: [{ dragSelector: '[data-drag="true"], .rollable', dropSelector: null }],
    actions: {
      editImage: CthulhuEternalActorSheet.#onEditImage,
      toggleSheet: CthulhuEternalActorSheet.#onToggleSheet,
      edit: CthulhuEternalActorSheet.#onItemEdit,
      delete: CthulhuEternalActorSheet.#onItemDelete,
      updateCheckboxArray: CthulhuEternalActorSheet.#onUpdateCheckboxArray,
    },
  }

  /**
   * The current sheet mode.
   * @type {number}
   */
  _sheetMode = this.constructor.SHEET_MODES.PLAY

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

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

  /** @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(),
      enrichedDescription: await TextEditor.enrichHTML(this.document.system.description, { async: true }),
      isEditMode: this.isEditMode,
      isPlayMode: this.isPlayMode,
      isEditable: this.isEditable,
    }
    return context
  }

  /** @override */
  _onRender(context, options) {
    this.#dragDrop.forEach((d) => d.bind(this.element))
    // Add listeners to rollable elements
    const rollables = this.element.querySelectorAll(".rollable")
    rollables.forEach((d) => d.addEventListener("click", this._onRoll.bind(this)))
  }

  // #region Drag-and-Drop Workflow
  /**
   * 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),
        dragover: this._onDragOver.bind(this),
        drop: this._onDrop.bind(this),
      }
      return new DragDrop(d)
    })
  }

  /**
   * Callback actions which occur when a dragged element is dropped on a target.
   * @param {DragEvent} event       The originating DragEvent
   * @protected
   */
  async _onDrop(event) {
  }


  /**
   * 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 true //this.isEditable && this.document.isOwner
  }

  /**
   * Callback actions which occur at the beginning of a drag start workflow.
   * @param {DragEvent} event       The originating DragEvent
   * @protected
   */
  _onDragStart(event) {
    if ("link" in event.target.dataset) return

    const el = event.currentTarget.closest('[data-drag="true"]')
    const dragType = el.dataset.dragType

    let dragData = {}

    let target
    switch (dragType) {
      case "save":
        target = event.currentTarget.querySelector("input")
        dragData = {
          actorId: this.document.id,
          type: "roll",
          rollType: target.dataset.rollType,
          rollTarget: target.dataset.rollTarget,
          value: target.value,
        }
        break
      case "resource":
        target = event.currentTarget.querySelector("select")
        dragData = {
          actorId: this.document.id,
          type: "roll",
          rollType: target.dataset.rollType,
          rollTarget: target.dataset.rollTarget,
          value: target.value,
        }
        break
      case "damage":
        dragData = {
          actorId: this.document.id,
          type: "rollDamage",
          rollType: el.dataset.dragType,
          rollTarget: el.dataset.itemId,
        }
        break
      case "attack":
        dragData = {
          actorId: this.document.id,
          type: "rollAttack",
          rollValue: el.dataset.rollValue,
          rollTarget: el.dataset.rollTarget,
        }
        break
      default:
        // Handle other cases or do nothing
        break
    }

    // Extract the data you need

    if (!dragData) return

    // Set data transfer
    event.dataTransfer.setData("text/plain", JSON.stringify(dragData))
  }

  /**
   * Callback actions which occur when a dragged element is over a drop target.
   * @param {DragEvent} event       The originating DragEvent
   * @protected
   */
  _onDragOver(event) {}

  async _onDropItem(item) {
    console.log("Dropped item", item) 
    let itemData = item.toObject()
    await this.document.createEmbeddedDocuments("Item", [itemData], { renderSheet: false })
  }

  // #endregion

  // #region Actions
  /**
   * Handle toggling between Edit and Play mode.
   * @param {Event} event             The initiating click event.
   * @param {HTMLElement} target      The current target of the event listener.
   */
  static #onToggleSheet(event, target) {
    const modes = this.constructor.SHEET_MODES
    this._sheetMode = this.isEditMode ? modes.PLAY : modes.EDIT
    this.render()
  }

  static #onUpdateCheckboxArray(event, target) {
    console.log("Update checkbox array", event, target)
    let arrayName = target.dataset.name
    let arrayIdx = Number(target.dataset.index)
    let dataPath = `system.san.${arrayName}`
    let tab = foundry.utils.duplicate(this.document.system.san[arrayName])
    tab[arrayIdx] = target.checked
    this.actor.update( { [dataPath]: tab } )
    // Dump
    console.log("Array name", arrayName, arrayIdx, target.checked, dataPath)
  }

  /**
   * Handle changing a Document's image.
   *
   * @this CthulhuEternalCharacterSheet
   * @param {PointerEvent} event   The originating click event
   * @param {HTMLElement} target   The capturing HTML element which defined a [data-action]
   * @returns {Promise}
   * @private
   */
  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()
  }

  /**
   * Edit an existing item within the Actor
   * Start with the uuid, if it's not found, fallback to the id (as Embedded item in the actor)
   * @this CthulhuEternalCharacterSheet
   * @param {PointerEvent} event The originating click event
   * @param {HTMLElement} target the capturing HTML element which defined a [data-action]
   */
  static async #onItemEdit(event, target) {
    const id = target.getAttribute("data-item-id")
    const uuid = target.getAttribute("data-item-uuid")
    let item
    item = await fromUuid(uuid)
    if (!item) item = this.document.items.get(id)
    if (!item) return
    item.sheet.render(true)
  }

  /**
   * Delete an existing talent within the Actor
   * Use the uuid to display the talent sheet
   * @param {PointerEvent} event The originating click event
   * @param {HTMLElement} target the capturing HTML element which defined a [data-action]
   */
  static async #onItemDelete(event, target) {
    const itemUuid = target.getAttribute("data-item-uuid")
    const item = await fromUuid(itemUuid)
    await item.deleteDialog()
  }

  // #endregion
}