const { HandlebarsApplicationMixin } = foundry.applications.api

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

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

  #dragDrop

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

  _sheetMode = this.constructor.SHEET_MODES.PLAY

  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 = {
      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,
    }
    return context
  }

  /** @override */
  _onRender(context, options) {
    this.#dragDrop.forEach((d) => d.bind(this.element))
    // ProseMirror "Save" dispatches a change event before committing its .value
    // to the element, so FormDataExtended may read stale HTML.  Instead we
    // intercept the event here, stop it from bubbling to the submitOnChange
    // handler, and update the document directly with the current editor value.
    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)
    })
  }

  async _onDrop(event) {}

  _canDragStart(selector) {
    return this.isEditable
  }

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

  _onDragStart(event) {
    if ("link" in event.target.dataset) return
    const li = event.target.closest("[data-item-uuid]")
    if (!li) return
    const dragData = { type: "Item", uuid: li.dataset.itemUuid }
    event.dataTransfer.setData("text/plain", JSON.stringify(dragData))
  }

  _onDragOver(event) {}

  async _onDropItem(item) {
    // Ignore drops of items already owned by this actor (internal drag = no-op)
    if (item.parent?.id === this.document.id) return

    const itemData = item.toObject()
    // Class is unique: replace any existing item of the same type
    if (item.type === "class") {
      const existing = this.document.itemTypes[item.type]
      if (existing.length > 0) {
        await this.document.deleteEmbeddedDocuments("Item", existing.map(i => i.id))
      }
    }
    await this.document.createEmbeddedDocuments("Item", [itemData], { renderSheet: false })
  }

  static #onToggleSheet(event, target) {
    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 #onItemEdit(event, target) {
    const id = target.getAttribute("data-item-id")
    const uuid = target.getAttribute("data-item-uuid")
    let item = await fromUuid(uuid)
    if (!item) item = this.document.items.get(id)
    if (!item) return
    item.sheet.render(true)
  }

  static async #onItemDelete(event, target) {
    const itemUuid = target.getAttribute("data-item-uuid")
    const item = await fromUuid(itemUuid)
    await item.deleteDialog()
  }
}
