import PrismRPGActorSheet from "./base-actor-sheet.mjs"
import PrismRPGRoll from "../../documents/roll.mjs"
import { SYSTEM } from "../../config/system.mjs"

export default class PrismRPGCharacterSheet extends PrismRPGActorSheet {
  /** @override */
  static DEFAULT_OPTIONS = {
    classes: ["character"],
    position: {
      width: 780,
      height: 780,
    },
    window: {
      contentClasses: ["character-content"],
    },
    actions: {
      createEquipment: PrismRPGCharacterSheet.#onCreateEquipment,
      rollInitiative: PrismRPGCharacterSheet.#onRollInitiative,
      armorHitPointsPlus: PrismRPGCharacterSheet.#onArmorHitPointsPlus,
      armorHitPointsMinus: PrismRPGCharacterSheet.#onArmorHitPointsMinus,
      armorPointsPlus: PrismRPGCharacterSheet.#onArmorPointsPlus,
      armorPointsMinus: PrismRPGCharacterSheet.#onArmorPointsMinus,
      actionPointsPlus: PrismRPGCharacterSheet.#onActionPointsPlus,
      actionPointsMinus: PrismRPGCharacterSheet.#onActionPointsMinus,
      manaPointsPlus: PrismRPGCharacterSheet.#onManaPointsPlus,
      manaPointsMinus: PrismRPGCharacterSheet.#onManaPointsMinus,
      hpPlus: PrismRPGCharacterSheet.#onHpPlus,
      hpMinus: PrismRPGCharacterSheet.#onHpMinus,
      hpTempPlus: PrismRPGCharacterSheet.#onHpTempPlus,
      hpTempMinus: PrismRPGCharacterSheet.#onHpTempMinus,
      postItemToChat: PrismRPGCharacterSheet.#onPostItemToChat,
      useConsumable: PrismRPGCharacterSheet.#onUseConsumable,
      quantityPlus: PrismRPGCharacterSheet.#onQuantityPlus,
      quantityMinus: PrismRPGCharacterSheet.#onQuantityMinus,
      toggleContainerEquipped: PrismRPGCharacterSheet.#onToggleContainerEquipped,
      toggleEquipped: PrismRPGCharacterSheet.#onToggleEquipped,
      assignToContainer: PrismRPGCharacterSheet.#onAssignToContainer,
      removeFromContainer: PrismRPGCharacterSheet.#onRemoveFromContainer,
    },
  }

  /** @override */
  static PARTS = {
    main: {
      template: "systems/fvtt-prism-rpg/templates/character-main.hbs",
    },
    tabs: {
      template: "templates/generic/tab-navigation.hbs",
    },
    skills: {
      template: "systems/fvtt-prism-rpg/templates/character-skills.hbs",
    },
    subattributes: {
      template: "systems/fvtt-prism-rpg/templates/character-subattributes.hbs",
    },
    combat: {
      template: "systems/fvtt-prism-rpg/templates/character-combat.hbs",
    },
    equipment: {
      template: "systems/fvtt-prism-rpg/templates/character-equipment.hbs",
    },
    spells: {
      template: "systems/fvtt-prism-rpg/templates/character-spells.hbs",
    },
    biography: {
      template: "systems/fvtt-prism-rpg/templates/character-biography.hbs",
    },
  }

  /** @override */
  tabGroups = {
    sheet: "skills",
  }

  /**
   * Prepare an array of form header tabs.
   * @returns {Record<string, Partial<ApplicationTab>>}
   */
  #getTabs() {
    let tabs = {
      skills: { id: "skills", group: "sheet", icon: "fa-solid fa-shapes", label: "PRISMRPG.Label.skills" },
      subattributes: { id: "subattributes", group: "sheet", icon: "fa-solid fa-diagram-project", label: "PRISMRPG.Label.subattributes" },
      combat: { id: "combat", group: "sheet", icon: "fa-solid fa-swords", label: "PRISMRPG.Label.combat" },
      equipment: { id: "equipment", group: "sheet", icon: "fa-solid fa-backpack", label: "PRISMRPG.Label.equipment" },
      biography: { id: "biography", group: "sheet", icon: "fa-solid fa-book", label: "PRISMRPG.Label.biography" },
    }
    if (this.actor.system.biodata.magicUser) {
      tabs.spells = { id: "spells", group: "sheet", icon: "fa-sharp-duotone fa-solid fa-wand-magic-sparkles", label: "PRISMRPG.Label.spells" }
    }
    for (const v of Object.values(tabs)) {
      v.active = this.tabGroups[v.group] === v.id
      v.cssClass = v.active ? "active" : ""
    }
    return tabs
  }

  /** @override */
  async _prepareContext() {
    const context = await super._prepareContext()
    context.tabs = this.#getTabs()
    context.config = SYSTEM

    return context
  }

  /** @override */
  async _preparePartContext(partId, context) {
    const doc = this.document
    switch (partId) {
      case "main":
        context.race = doc.itemTypes.race?.[0] || null
        const classes = doc.itemTypes.class || []
        // Create 3 class slots
        context.classSlots = [
          classes[0] || null,
          classes[1] || null,
          classes[2] || null
        ]
        // Burden computed values
        const bSubAttr = doc.system.burden.subAttribute
        const bSubVal = doc.system.subAttributes[bSubAttr]?.value ?? 0
        const baseBurden = doc.itemTypes.race?.[0]?.system.baseBurden ?? 0
        context.burdenMax = Math.max(0, baseBurden + bSubVal + doc.system.burden.other)
        // Equipped burden: only items with equipped=true count toward burden
        const equippableTypes = [
          ...doc.itemTypes.weapon,
          ...doc.itemTypes.armor,
          ...doc.itemTypes.shield,
          ...doc.itemTypes.equipment,
          ...doc.itemTypes.container,
        ]
        const burdenEquipped = equippableTypes
          .filter(i => i.system.equipped)
          .reduce((sum, i) => sum + (i.system.encLoad ?? 0) * (i.system.quantity ?? 1), 0)
        context.burdenUsed = burdenEquipped
        // Excess equipped burden reduces Movement Rating
        const excessBurden = Math.max(0, burdenEquipped - context.burdenMax)
        // Movement Rating computed value (excess burden adds to reduction)
        const mrSubAttr = doc.system.movementRating.subAttribute
        const mrSubVal = doc.system.subAttributes[mrSubAttr]?.value ?? 0
        context.movementRatingValue = Math.max(0,
          3 + mrSubVal + doc.system.movementRating.other - doc.system.movementRating.reduction - excessBurden
        )
        context.excessBurden = excessBurden
        break
      case "skills":
        context.tab = context.tabs.skills
        context.skills = doc.itemTypes.skill
        context.racialAbilities = doc.itemTypes["racial-ability"]
        context.abilities = doc.itemTypes.ability
        context.vulnerabilities = doc.itemTypes.vulnerability ?? []
        break
      case "subattributes":
        context.tab = context.tabs.subattributes
        break
      case "spells":
        context.tab = context.tabs.spells
        context.spells = doc.itemTypes.spell
        context.hasSpells = context.spells.length > 0
        break
      case "combat":
        context.tab = context.tabs.combat
        context.weapons = doc.itemTypes.weapon
        context.armors = doc.itemTypes.armor
        context.shields = doc.itemTypes.shield
        break
      case "equipment":
        context.tab = context.tabs.equipment
        // All items that can be stored in containers
        const allStorable = [
          ...doc.itemTypes.weapon,
          ...doc.itemTypes.armor,
          ...doc.itemTypes.shield,
          ...doc.itemTypes.equipment,
          ...doc.itemTypes.consumable,
          ...doc.itemTypes.loot,
        ]
        // Build a map: containerId → items[]
        const containerGroups = {}
        for (const container of doc.itemTypes.container) {
          containerGroups[container.id] = { container, items: [] }
        }
        for (const item of allStorable) {
          const cid = item.system.containerId
          if (cid && containerGroups[cid]) {
            containerGroups[cid].items.push(item)
          }
        }
        context.containerGroups = Object.values(containerGroups)

        // Items are "uncontained" if they have no containerId, or if their container was deleted
        const isUncontained = i => !i.system.containerId || !containerGroups[i.system.containerId]
        context.weapons = doc.itemTypes.weapon.filter(isUncontained)
        context.armors = [...doc.itemTypes.armor, ...doc.itemTypes.shield].filter(isUncontained)
        context.consumables = doc.itemTypes.consumable.filter(isUncontained)
        context.kits = doc.itemTypes.equipment.filter(i => i.system.isKit && isUncontained(i))
        context.equipmentItems = doc.itemTypes.equipment.filter(i => !i.system.isKit && isUncontained(i))
        context.loots = doc.itemTypes.loot.filter(isUncontained)
        context.containers = doc.itemTypes.container

        context.packBurdenMax = doc.itemTypes.container
          .filter(c => c.system.equipped)
          .reduce((sum, c) => sum + (c.system.packBurden ?? 0), 0)
        // Pack burden = items stored in an existing container
        context.packBurdenUsed = allStorable
          .filter(i => i.system.containerId && containerGroups[i.system.containerId])
          .reduce((sum, i) => sum + (i.system.encLoad ?? 0) * (i.system.quantity ?? 1), 0)
        break
      case "biography":
        context.tab = context.tabs.biography
        context.enrichedDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(doc.system.description, { async: true })
        context.enrichedNotes = await foundry.applications.ux.TextEditor.implementation.enrichHTML(doc.system.notes, { async: true })
        break
    }
    return context
  }

  // #region Drag-and-Drop Workflow

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

    // Handle different data types
    if (data.type === "Item") {
      const item = await fromUuid(data.uuid)

      // Check if dropped onto a container row
      const containerEl = event.target.closest("[data-container-id]")
      if (containerEl && item?.parent === this.document) {
        const containerId = containerEl.dataset.containerId
        // Don't store containers inside containers
        if (item.type !== "container") {
          await item.update({ "system.containerId": containerId })
          return
        }
      }

      return this._onDropItem(item)
    }
  }

  static async #onRollInitiative(event, target) {
    await this.document.system.rollInitiative()
  }

  static async #onArmorHitPointsPlus(event, target) {
    let armorHP = this.actor.system.combat.armorHitPoints
    armorHP += 1
    this.actor.update({ "system.combat.armorHitPoints": armorHP })
  }

  static async #onArmorHitPointsMinus(event, target) {
    let armorHP = this.actor.system.combat.armorHitPoints
    armorHP -= 1
    this.actor.update({ "system.combat.armorHitPoints": Math.max(armorHP, 0) })
  }

      static async #onManaPointsPlus(event, target) {
    let mana = this.actor.system.manaPoints.value
    mana += 1
    this.actor.update({ "system.manaPoints.value": Math.min(mana, this.actor.system.manaPoints.max) })
  }

  static async #onManaPointsMinus(event, target) {
    let mana = this.actor.system.manaPoints.value
    mana -= 1
    this.actor.update({ "system.manaPoints.value": Math.max(mana, 0) })
  }

  static async #onArmorPointsPlus(event, target) {
    let armor = this.actor.system.armorPoints.value
    armor += 1
    this.actor.update({ "system.armorPoints.value": Math.min(armor, this.actor.system.armorPoints.max) })
  }

 static async #onArmorPointsMinus(event, target) {
    let armor = this.actor.system.armorPoints.value
    armor -= 1
    this.actor.update({ "system.armorPoints.value": Math.max(armor, 0) })
  }

  static async#onActionPointsPlus(event, target) {
    let actionPoints = this.actor.system.actionPoints.value
    actionPoints += 1
    this.actor.update({ "system.actionPoints.value": Math.min(actionPoints, this.actor.system.actionPoints.max) })
  }

  static async#onActionPointsMinus(event, target) {
    let actionPoints = this.actor.system.actionPoints.value
    actionPoints -= 1
    this.actor.update({ "system.actionPoints.value": Math.max(actionPoints, 0) })
  }

  static async#onHpPlus(event, target) {
    let hp = this.actor.system.hp.value
    hp += 1
    this.actor.update({ "system.hp.value": Math.min(hp, this.actor.system.hp.max) })
  }

  static async#onHpMinus(event, target) {
    let hp = this.actor.system.hp.value
    hp -= 1
    this.actor.update({ "system.hp.value": Math.max(hp, 0) })
  }

  static async#onHpTempPlus(event, target) {
    const temp = this.actor.system.hp.temp
    this.actor.update({ "system.hp.temp": temp + 1 })
  }

  static async#onHpTempMinus(event, target) {
    const temp = this.actor.system.hp.temp
    this.actor.update({ "system.hp.temp": Math.max(temp - 1, 0) })
  }

  static async #onCreateEquipment(event, target) {
    const itemType = target.dataset.itemType ?? "equipment"
    const isKit = target.dataset.itemKit === "true"
    const typeLabel = game.i18n.localize(`TYPES.Item.${itemType}`) || itemType
    const itemData = {
      name: game.i18n.format("DOCUMENT.New", { type: typeLabel }),
      type: itemType,
    }
    if (isKit) itemData["system.isKit"] = true
    await this.document.createEmbeddedDocuments("Item", [itemData])
  }

  static async #onUseConsumable(event, target) {
    const itemElement = target.closest("[data-item-id]")
    if (!itemElement) return
    const item = this.document.items.get(itemElement.dataset.itemId)
    if (!item) return
    if (item.system.uses <= 0) return

    const confirmed = await foundry.applications.api.DialogV2.confirm({
      window: { title: game.i18n.localize("PRISMRPG.Dialog.useConsumable") },
      content: `<p>${game.i18n.format("PRISMRPG.Dialog.useConsumableContent", { name: item.name, uses: item.system.uses })}</p>`,
    })
    if (!confirmed) return

    if (item.system.uses <= 1) {
      await item.delete()
    } else {
      await item.update({ "system.uses": item.system.uses - 1 })
    }
  }

  static async #onToggleContainerEquipped(event, target) {
    const itemElement = target.closest("[data-item-id]")
    if (!itemElement) return
    const item = this.document.items.get(itemElement.dataset.itemId)
    if (!item) return
    await item.update({ "system.equipped": !item.system.equipped })
  }

  static async #onToggleEquipped(event, target) {
    const itemElement = target.closest("[data-item-id]")
    if (!itemElement) return
    const item = this.document.items.get(itemElement.dataset.itemId)
    if (!item) return
    await item.update({ "system.equipped": !item.system.equipped })
  }

  static async #onQuantityPlus(event, target) {
    const itemElement = target.closest("[data-item-id]")
    if (!itemElement) return
    const item = this.document.items.get(itemElement.dataset.itemId)
    if (!item) return
    await item.update({ "system.quantity": (item.system.quantity ?? 1) + 1 })
  }

  static async #onQuantityMinus(event, target) {
    const itemElement = target.closest("[data-item-id]")
    if (!itemElement) return
    const item = this.document.items.get(itemElement.dataset.itemId)
    if (!item) return
    const newQty = (item.system.quantity ?? 1) - 1
    if (newQty < 1) {
      const confirmed = await foundry.applications.api.DialogV2.confirm({
        window: { title: game.i18n.localize("PRISMRPG.Dialog.deleteItem") },
        content: `<p>${game.i18n.format("PRISMRPG.Dialog.deleteItemContent", { name: item.name })}</p>`,
      })
      if (confirmed) await item.delete()
      return
    }
    await item.update({ "system.quantity": newQty })
  }

  static async #onAssignToContainer(event, target) {
    const itemElement = target.closest("[data-item-id]")
    if (!itemElement) return
    const item = this.document.items.get(itemElement.dataset.itemId)
    if (!item || item.type === "container") return

    const containers = this.document.itemTypes.container
    if (!containers.length) {
      ui.notifications.warn(game.i18n.localize("PRISMRPG.Message.noContainers"))
      return
    }

    const options = containers.map(c => {
      const escapedName = foundry.utils.escapeHTML(c.name)
      return `<option value="${c.id}">${escapedName}</option>`
    }).join("")
    const content = `<div class="form-group">
      <label>${game.i18n.localize("PRISMRPG.Label.container")}</label>
      <div class="form-fields">
        <select name="containerId">${options}</select>
      </div>
    </div>`

    const containerId = await foundry.applications.api.DialogV2.prompt({
      window: { title: game.i18n.localize("PRISMRPG.Dialog.assignToContainer") },
      classes: ["prismrpg"],
      content,
      ok: {
        callback: (event, button) => button.form.elements.containerId.value,
      },
    })
    if (containerId) await item.update({ "system.containerId": containerId })
  }

  static async #onRemoveFromContainer(event, target) {
    const itemElement = target.closest("[data-item-id]")
    if (!itemElement) return
    const item = this.document.items.get(itemElement.dataset.itemId)
    if (!item) return
    await item.update({ "system.containerId": "" })
  }

  static async #onPostItemToChat(event, target) {
    console.log("PRISM RPG | PostItemToChat action triggered", { event: event, target: target })

    // Try to find the item element from the clicked target or its parents
    let itemElement = null

    // First try with the target (the actual clicked element)
    if (event.target) {
      itemElement = event.target.closest('[data-item-id]')
    }

    // If not found, try with currentTarget (the element with the action)
    if (!itemElement && event.currentTarget) {
      itemElement = event.currentTarget.closest('[data-item-id]')
    }

    // If still not found, try with the target parameter
    if (!itemElement && target) {
      itemElement = target.closest('[data-item-id]')
    }

    console.log("PRISM RPG | Found item element", { itemElement: itemElement })

    if (!itemElement) {
      console.warn("PRISM RPG | Could not find item element for posting to chat")
      return
    }

    const itemId = itemElement.dataset.itemId

    if (!itemId) {
      console.warn("PRISM RPG | Item ID not found for posting to chat")
      return
    }

    const item = this.actor.items.get(itemId)

    if (!item) {
      console.warn("PRISM RPG | Item not found for posting to chat", { itemId: itemId })
      return
    }

    // Create a chat message with the item data
    const speaker = ChatMessage.getSpeaker({ actor: this.actor })
    const content = await this.formatItemForChat(item)

    await ChatMessage.create({
      content: content,
      speaker: speaker,
    })
  }

  async formatItemForChat(item) {
    // Format the item data for chat display
    const itemTypeClass = `${item.type}-item`
    let htmlContent = `
      <div class="prismrpg-item-chat-card ${itemTypeClass}">
        <div class="item-header">
          <h3>${item.name}</h3>
          <span class="item-type">${game.i18n.localize(`TYPES.Item.${item.type}`) || item.type}</span>
        </div>
    `

    // Add item image if available
    if (item.img && !item.img.includes('icons/svg/mystery-man.svg')) {
      htmlContent += `<div class="item-image"><img src="${item.img}" alt="${item.name}"></div>`
    }

    // Add item description if available
    if (item.system.description && item.system.description.trim() !== '') {
      const enrichedDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(item.system.description, { async: true })
      htmlContent += `<div class="item-description">${enrichedDescription}</div>`
    }

    // Add specific item data based on item type
    htmlContent += `<div class="item-details">`

    switch (item.type) {
      case 'weapon':
        htmlContent += `
          <div class="item-detail"><strong>Type:</strong> ${item.system.weaponType || 'Unknown'}</div>
          <div class="item-detail"><strong>Damage:</strong> ${item.system.damage || 'N/A'}</div>
          <div class="item-detail"><strong>APC:</strong> ${item.system.apc || 0}</div>
        `
        if (item.system.damageType) {
          const damageTypes = []
          if (item.system.damageType.piercing) damageTypes.push('Piercing')
          if (item.system.damageType.bludgeoning) damageTypes.push('Bludgeoning')
          if (item.system.damageType.slashing) damageTypes.push('Slashing')
          if (damageTypes.length > 0) {
            htmlContent += `<div class="item-detail"><strong>Damage Type:</strong> ${damageTypes.join('/')}</div>`
          }
        }
        break

      case 'armor':
        htmlContent += `
          <div class="item-detail"><strong>Armor Type:</strong> ${item.system.armorType || 'Unknown'}</div>
          <div class="item-detail"><strong>Armor Points:</strong> ${item.system.armorPoints || 0}</div>
          <div class="item-detail"><strong>APC:</strong> ${item.system.apc || 0}</div>
        `
        break

      case 'shield':
        htmlContent += `
          <div class="item-detail"><strong>Shield Type:</strong> ${item.system.shieldType || 'Unknown'}</div>
          <div class="item-detail"><strong>Armor Points:</strong> ${item.system.armorPoints || 0}</div>
          <div class="item-detail"><strong>APC:</strong> ${item.system.apc || 0}</div>
        `
        break

      case 'skill':
        htmlContent += `
          <div class="item-detail"><strong>Modifier:</strong> ${item.system.modifier || 0}</div>
          <div class="item-detail"><strong>Core Skill:</strong> ${item.system.isCoreSkill ? 'Yes' : 'No'}</div>
        `
        break

      case 'spell':
        htmlContent += `
          <div class="item-detail"><strong>Level:</strong> ${item.system.level || 'Unknown'}</div>
          <div class="item-detail"><strong>Mana Cost:</strong> ${item.system.manaCost || 0}</div>
          <div class="item-detail"><strong>Casting Time:</strong> ${item.system.castingTime || 'N/A'}</div>
        `
        break

      case 'miracle':
        htmlContent += `
          <div class="item-detail"><strong>Prayer Time:</strong> ${item.system.prayerTime || 'N/A'}</div>
        `
        break

      case 'equipment':
        htmlContent += `
          <div class="item-detail"><strong>Weight:</strong> ${item.system.weight || 'N/A'}</div>
        `
        break

      default:
        // For other item types, just show basic info
        htmlContent += `<div class="item-detail"><strong>Item Type:</strong> ${item.type}</div>`
    }

    htmlContent += `</div></div>`

    return htmlContent
  }

  _onRender(context, options) {
    // Inputs with class `item-quantity`
    const woundDescription = this.element.querySelectorAll('.wound-data')
    for (const input of woundDescription) {
      input.addEventListener("change", (e) => {
        e.preventDefault();
        e.stopImmediatePropagation();
        const newValue = e.currentTarget.value
        const index = e.currentTarget.dataset.index
        const fieldName = e.currentTarget.dataset.name
        let tab = foundry.utils.duplicate(this.actor.system.hp.wounds)
        tab[index][fieldName] = newValue
        console.log(tab, index, fieldName, newValue)
        this.actor.update({ "system.hp.wounds": tab });
      })
    }

    // Container drag-over highlight
    this.element.querySelectorAll("[data-container-id]").forEach(el => {
      el.addEventListener("dragover", (e) => {
        e.preventDefault()
        el.classList.add("drag-over")
      })
      el.addEventListener("dragleave", () => el.classList.remove("drag-over"))
      el.addEventListener("drop", () => el.classList.remove("drag-over"))
    })

    super._onRender();
  }


  /**
   * Handles the roll action triggered by user interaction.
   *
   * @param {PointerEvent} event The event object representing the user interaction.
   * @param {HTMLElement} target The target element that triggered the roll.
   *
   * @returns {Promise<void>} A promise that resolves when the roll action is complete.
   *
   * @throws {Error} Throws an error if the roll type is not recognized.
   *
   * @description This method checks the current mode (edit or not) and determines the type of roll
   * (save, resource, or damage) based on the target element's data attributes. It retrieves the
   * corresponding value from the document's system and performs the roll.
   */

  async _onRoll(event, target) {
    if (this.isEditMode) return
    // Use closest to find the rollable element in case user clicked on a child
    const rollableElement = event.target.closest('.rollable') || event.target
    const rollType = rollableElement.dataset.rollType
    let rollKey = rollableElement.dataset.rollKey;
    let rollDice = rollableElement.dataset?.rollDice;

    this.actor.prepareRoll(rollType, rollKey, rollDice)

  }
  // #endregion
}
