import OathHammerActorSheet from "./base-actor-sheet.mjs"
import { SYSTEM } from "../../config/system.mjs"
import OathHammerRollDialog from "../roll-dialog.mjs"
import OathHammerWeaponDialog from "../weapon-dialog.mjs"
import OathHammerSpellDialog from "../spell-dialog.mjs"
import OathHammerMiracleDialog from "../miracle-dialog.mjs"
import OathHammerDefenseDialog from "../defense-dialog.mjs"
import OathHammerArmorDialog from "../armor-dialog.mjs"
import { rollSkillCheck, rollWeaponAttack, rollWeaponDamage, rollSpellCast, rollMiracleCast, rollArmorSave, rollWeaponDefense, rollInitiativeCheck } from "../../rolls.mjs"

export default class OathHammerCharacterSheet extends OathHammerActorSheet {
  /** @override */
  static DEFAULT_OPTIONS = {
    classes: ["character"],
    position: {
      width: 972,
      height: 780,
    },
    window: {
      contentClasses: ["character-content"],
    },
    actions: {
      createWeapon:   OathHammerCharacterSheet.#onCreateWeapon,
      createSpell:    OathHammerCharacterSheet.#onCreateSpell,
      createMiracle:  OathHammerCharacterSheet.#onCreateMiracle,
      createEquipment: OathHammerCharacterSheet.#onCreateEquipment,
      createTrait:    OathHammerCharacterSheet.#onCreateTrait,
      createOath:     OathHammerCharacterSheet.#onCreateOath,
      rollSkill:       OathHammerCharacterSheet.#onRollSkill,
      attackWeapon:    OathHammerCharacterSheet.#onAttackWeapon,
      defendWeapon:    OathHammerCharacterSheet.#onDefendWeapon,
      damageWeapon:    OathHammerCharacterSheet.#onDamageWeapon,
      castSpell:       OathHammerCharacterSheet.#onCastSpell,
      castMiracle:     OathHammerCharacterSheet.#onCastMiracle,
      rollArmorSave:   OathHammerCharacterSheet.#onRollArmorSave,
      resetMiracleBlocked: OathHammerCharacterSheet.#onResetMiracleBlocked,
      rollInitiative:  OathHammerCharacterSheet.#onRollInitiative,
      adjustQty:       OathHammerCharacterSheet.#onAdjustQty,
      adjustCurrency:  OathHammerCharacterSheet.#onAdjustCurrency,
      adjustStress:    OathHammerCharacterSheet.#onAdjustStress,
      clearStress:     OathHammerCharacterSheet.#onClearStress,
    },
  }

  /** @override */
  static PARTS = {
    main:      { template: "systems/fvtt-oath-hammer/templates/actor/character-sheet.hbs" },
    tabs:      { template: "templates/generic/tab-navigation.hbs" },
    identity:  { template: "systems/fvtt-oath-hammer/templates/actor/character-identity.hbs" },
    skills:    { template: "systems/fvtt-oath-hammer/templates/actor/character-skills.hbs" },
    combat:    { template: "systems/fvtt-oath-hammer/templates/actor/character-combat.hbs" },
    magic:     { template: "systems/fvtt-oath-hammer/templates/actor/character-magic.hbs" },
    equipment: { template: "systems/fvtt-oath-hammer/templates/actor/character-equipment.hbs" },
    notes:     { template: "systems/fvtt-oath-hammer/templates/actor/character-notes.hbs" },
  }

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

  #getTabs() {
    const tabs = {
      identity:  { id: "identity",  group: "sheet", icon: "fa-solid fa-person",             label: "OATHHAMMER.Tab.Identity" },
      skills:    { id: "skills",    group: "sheet", icon: "fa-solid fa-scroll",              label: "OATHHAMMER.Tab.Skills" },
      combat:    { id: "combat",    group: "sheet", icon: "fa-solid fa-swords",              label: "OATHHAMMER.Tab.Combat" },
      magic:     { id: "magic",     group: "sheet", icon: "fa-solid fa-wand-magic-sparkles", label: "OATHHAMMER.Tab.Magic" },
      equipment: { id: "equipment", group: "sheet", icon: "fa-solid fa-backpack",            label: "OATHHAMMER.Tab.Equipment" },
      notes:     { id: "notes",     group: "sheet", icon: "fa-solid fa-book",               label: "OATHHAMMER.Tab.Notes" },
    }
    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()
    // class/experience available to all parts (header + identity tab)
    const doc = this.document
    context.characterClass = doc.itemTypes["class"]?.[0] ?? null
    return context
  }

  /** @override */
  async _preparePartContext(partId, context) {
    const doc = this.document
    switch (partId) {
      case "main":
        break
      case "identity":
        context.tab = context.tabs.identity
        context.traits = doc.itemTypes.trait.map(a => {
          const typeKey = SYSTEM.TRAIT_TYPE_CHOICES[a.system.traitType]
          const periodKey = SYSTEM.TRAIT_USAGE_PERIOD[a.system.usagePeriod]
          const isPassive = a.system.usagePeriod === "none"
          return {
            id: a.id, uuid: a.uuid, img: a.img, name: a.name, system: a.system,
            _typeLabel: typeKey ? game.i18n.localize(typeKey) : a.system.traitType,
            _usageLabel: isPassive
              ? game.i18n.localize("OATHHAMMER.UsagePeriod.None")
              : `${a.system.maxUses > 0 ? a.system.maxUses + " / " : ""}${game.i18n.localize(periodKey)}`,
            _descTooltip: _stripHtml(a.system.description)
          }
        })
        context.oaths = doc.itemTypes.oath.map(o => {
          const typeEntry = SYSTEM.OATH_TYPES[o.system.oathType]
          const parts = [o.system.tenet, o.system.boon, o.system.bane].filter(Boolean)
          return {
            id: o.id, uuid: o.uuid, img: o.img, name: o.name, system: o.system,
            _typeLabel: typeEntry ? game.i18n.localize(typeEntry.label) : o.system.oathType,
            _violated: o.system.violated,
            _boonText: _stripHtml(o.system.boon, 80),
            _baneText: _stripHtml(o.system.bane, 80),
            _descTooltip: _stripHtml(parts.join(" "))
          }
        })
        context.enrichedBackground = await foundry.applications.ux.TextEditor.implementation.enrichHTML(doc.system.background ?? "", { async: true })
        break
      case "skills": {
        context.tab = context.tabs.skills
        const sys = doc.system
        const skillSchemaFields = doc.system.schema.fields.skills.fields
        const attrRanks = {
          might:        sys.attributes.might.rank,
          toughness:    sys.attributes.toughness.rank,
          agility:      sys.attributes.agility.rank,
          willpower:    sys.attributes.willpower.rank,
          intelligence: sys.attributes.intelligence.rank,
          fate:         sys.attributes.fate.rank,
        }
        context.skillGroups = Object.entries(SYSTEM.SKILLS_BY_ATTRIBUTE).map(([attr, skillKeys]) => ({
          attribute: attr,
          label: `OATHHAMMER.Attribute.${attr.charAt(0).toUpperCase()}${attr.slice(1)}`,
          attrRank: attrRanks[attr],
          skillData: skillKeys.map(skillKey => {
            const sk = sys.skills[skillKey]
            return {
              key: skillKey,
              label: SYSTEM.SKILLS[skillKey].label,
              rank: sk.rank,
              modifier: sk.modifier,
              colorDice: sk.colorDice,
              colorDiceType: sk.colorDiceType,
              rankName: `system.skills.${skillKey}.rank`,
              modifierName: `system.skills.${skillKey}.modifier`,
              colorDiceName: `system.skills.${skillKey}.colorDice`,
              colorDiceTypeName: `system.skills.${skillKey}.colorDiceType`,
              rankOptions: [0,1,2,3,4].map(v => ({ value: v, label: String(v), selected: v === sk.rank })),
              total: attrRanks[attr] + sk.rank,
              // legacy - kept for formInput compatibility
              name: `system.skills.${skillKey}.rank`,
              field: skillSchemaFields[skillKey].fields.rank,
            }
          })
        }))
        break
      }
      case "combat":
        context.tab = context.tabs.combat
        context.weapons = doc.itemTypes.weapon.map(w => {
          const groupKey = SYSTEM.WEAPON_PROFICIENCY_GROUPS[w.system.proficiencyGroup]
          const traitsLabel = [...w.system.traits].map(t => {
            const tk = SYSTEM.WEAPON_TRAITS[t]
            return tk ? game.i18n.localize(tk) : t
          }).join(", ")
          return {
            id: w.id, uuid: w.uuid, img: w.img, name: w.name, system: w.system,
            _groupLabel: groupKey ? game.i18n.localize(groupKey) : w.system.proficiencyGroup,
            _traitsTooltip: traitsLabel || null,
            _descTooltip: _stripHtml(w.system.description),
            _isMagic: w.system.isMagic
          }
        })
        context.armors = doc.itemTypes.armor.map(a => {
          const typeKey = SYSTEM.ARMOR_TYPE_CHOICES[a.system.armorType]
          const traitsLabel = [...a.system.traits].map(t => {
            const tk = SYSTEM.ARMOR_TRAITS[t]
            return tk ? game.i18n.localize(tk) : t
          }).join(", ")
          return {
            id: a.id, uuid: a.uuid, img: a.img, name: a.name, system: a.system,
            _typeLabel: typeKey ? game.i18n.localize(typeKey) : a.system.armorType,
            _traitsTooltip: traitsLabel || null,
            _descTooltip: _stripHtml(a.system.description),
            _isMagic: a.system.isMagic
          }
        })
        context.ammunition = doc.itemTypes.ammunition
        // Slot tracking: max = 10 + (Might rank × 2); used = sum of all items' slots × quantity
        context.slotsMax = 10 + (doc.system.attributes.might.rank * 2)
        context.slotsUsed = doc.items.reduce((sum, item) => {
          const qty = item.system.quantity ?? 1
          return sum + (item.system.slots ?? 0) * Math.max(qty, 1)
        }, 0)
        context.slotsOver = context.slotsUsed > context.slotsMax
        // Show current initiative score if actor is in an active combat
        const combatant = game.combat?.combatants.find(c => c.actor?.id === doc.id)
        context.combatantInitiative = combatant?.initiative ?? null
        break
      case "magic":
        context.tab = context.tabs.magic
        context.stressBlocked = doc.system.arcaneStress.value >= doc.system.arcaneStress.threshold
        context.spells = doc.itemTypes.spell.map(s => ({
          id: s.id, uuid: s.uuid, img: s.img, name: s.name, system: s.system,
          _descTooltip: _stripHtml(s.system.effect)
        }))
        context.miracles = doc.itemTypes.miracle.map(m => ({
          id: m.id, uuid: m.uuid, img: m.img, name: m.name, system: m.system,
          _descTooltip: _stripHtml(m.system.effect)
        }))
        break
      case "equipment":
        context.tab = context.tabs.equipment
        context.equipment = doc.itemTypes.equipment.map(e => ({
          id: e.id, uuid: e.uuid, img: e.img, name: e.name, system: e.system,
          _descTooltip: _stripHtml(e.system.description)
        }))
        context.magicItems = doc.itemTypes["magic-item"].map(m => ({
          id: m.id, uuid: m.uuid, img: m.img, name: m.name, system: m.system,
          _descTooltip: _stripHtml(m.system.description)
        }))
        context.slotsMax = 10 + (doc.system.attributes.might.rank * 2)
        context.slotsUsed = doc.items.reduce((sum, item) => {
          const qty = item.system.quantity ?? 1
          return sum + (item.system.slots ?? 0) * Math.max(qty, 1)
        }, 0)
        context.slotsOver = context.slotsUsed > context.slotsMax
        break
      case "notes":
        context.tab = context.tabs.notes
        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
  }

  /** Auto-fill colorDice count when color type changes */
  static #COLOR_THRESHOLDS = { white: 4, red: 3, black: 2 }

  _onRender(context, options) {
    super._onRender?.(context, options)

    // Color dice auto-fill
    this.element.querySelectorAll('select.color-dice-select').forEach(select => {
      select.addEventListener('change', event => {
        const threshold = OathHammerCharacterSheet.#COLOR_THRESHOLDS[event.target.value] ?? 4
        const countInput = event.target.closest('.skill-color-col')?.querySelector('input[type="number"]')
        if (countInput) {
          countInput.value = threshold
          countInput.dispatchEvent(new Event('change', { bubbles: true }))
        }
        const dot = event.target.closest('.skill-color-col')?.querySelector('.color-dice-dot')
        if (dot) dot.className = `color-dice-dot color-dice-${event.target.value}`
      })
    })

    // Equipped checkbox — directly updates the item
    this.element.querySelectorAll('input.item-equipped-cb').forEach(cb => {
      cb.addEventListener('change', event => {
        const itemId = event.target.dataset.itemId
        const item = this.document.items.get(itemId)
        if (item) item.update({ 'system.equipped': event.target.checked })
      })
    })
  }

  async _onDrop(event) {
    if (!this.isEditable) return
    const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event)
    if (data.type === "Item") {
      const item = await fromUuid(data.uuid)
      return this._onDropItem(item)
    }
  }

  static #onCreateWeapon(event, target) {
    this.document.createEmbeddedDocuments("Item", [{ name: game.i18n.localize("OATHHAMMER.NewItem.Weapon"), type: "weapon" }])
  }

  static #onCreateSpell(event, target) {
    this.document.createEmbeddedDocuments("Item", [{ name: game.i18n.localize("OATHHAMMER.NewItem.Spell"), type: "spell" }])
  }

  static #onCreateMiracle(event, target) {
    this.document.createEmbeddedDocuments("Item", [{ name: game.i18n.localize("OATHHAMMER.NewItem.Miracle"), type: "miracle" }])
  }

  static #onCreateEquipment(event, target) {
    this.document.createEmbeddedDocuments("Item", [{ name: game.i18n.localize("OATHHAMMER.NewItem.Equipment"), type: "equipment" }])
  }

  static #onCreateTrait(event, target) {
    this.document.createEmbeddedDocuments("Item", [{ name: game.i18n.localize("OATHHAMMER.NewItem.Trait"), type: "trait" }])
  }

  static #onCreateOath(event, target) {
    this.document.createEmbeddedDocuments("Item", [{ name: game.i18n.localize("OATHHAMMER.NewItem.Oath"), type: "oath" }])
  }

  static async #onRollSkill(event, target) {
    const skillKey = target.dataset.skill
    if (!skillKey) return
    const result = await OathHammerRollDialog.prompt(this.document, skillKey)
    if (!result) return
    await rollSkillCheck(this.document, skillKey, result.dv, result)
  }

  static async #onAttackWeapon(event, target) {
    const weaponId = target.dataset.itemId
    if (!weaponId) return
    const weapon = this.document.items.get(weaponId)
    if (!weapon) return
    const opts = await OathHammerWeaponDialog.promptAttack(this.document, weapon)
    if (!opts) return
    await rollWeaponAttack(this.document, weapon, opts)
  }

  static async #onDefendWeapon(event, target) {
    const weaponId = target.dataset.itemId
    if (!weaponId) return
    const weapon = this.document.items.get(weaponId)
    if (!weapon) return
    const opts = await OathHammerWeaponDialog.promptDefense(this.document, weapon)
    if (!opts) return
    await rollWeaponDefense(this.document, weapon, opts)
  }

  static async #onDamageWeapon(event, target) {
    const weaponId = target.dataset.itemId
    if (!weaponId) return
    const weapon = this.document.items.get(weaponId)
    if (!weapon) return
    const opts = await OathHammerWeaponDialog.promptDamage(this.document, weapon, 0)
    if (!opts) return
    await rollWeaponDamage(this.document, weapon, opts)
  }

  static async #onCastSpell(event, target) {
    const spellId = target.dataset.itemId
    if (!spellId) return
    const spell = this.document.items.get(spellId)
    if (!spell) return
    const opts = await OathHammerSpellDialog.prompt(this.document, spell)
    if (!opts) return
    await rollSpellCast(this.document, spell, opts)
  }

  static async #onCastMiracle(event, target) {
    const miracleId = target.dataset.itemId
    if (!miracleId) return
    const miracle = this.document.items.get(miracleId)
    if (!miracle) return
    if (this.document.system.miracleBlocked) {
      ui.notifications.warn(game.i18n.localize("OATHHAMMER.Warning.MiracleBlocked"))
      return
    }
    const opts = await OathHammerMiracleDialog.prompt(this.document, miracle)
    if (!opts) return
    await rollMiracleCast(this.document, miracle, opts)
  }

  static async #onResetMiracleBlocked() {
    await this.document.update({ "system.miracleBlocked": false })
  }

  static async #onRollArmorSave(event, target) {
    const armorId = target.dataset.itemId
    if (!armorId) return
    const armor = this.document.items.get(armorId)
    if (!armor) return
    const opts = await OathHammerArmorDialog.prompt(this.document, armor)
    if (!opts) return
    await rollArmorSave(this.document, armor, opts)
  }

  static async #onRollInitiative() {
    const actor = this.document
    const combatant = game.combat?.combatants.find(c => c.actor?.id === actor.id)
    if (combatant) {
      // Delegate to OathHammerCombat.rollInitiative — posts to chat and updates tracker
      await game.combat.rollInitiative([combatant.id])
    } else {
      // Not in combat — roll for display only
      await rollInitiativeCheck(actor)
    }
  }

  static async #onAdjustQty(event, target) {
    const itemId = target.dataset.itemId
    const delta  = parseInt(target.dataset.delta, 10)
    if (!itemId || isNaN(delta)) return
    const item = this.document.items.get(itemId)
    if (!item) return
    const current = item.system.quantity ?? 0
    await item.update({ "system.quantity": Math.max(0, current + delta) })
  }

  static async #onAdjustCurrency(event, target) {
    const field = target.dataset.field
    const delta = parseInt(target.dataset.delta, 10)
    if (!field || isNaN(delta)) return
    const current = foundry.utils.getProperty(this.document, field) ?? 0
    await this.document.update({ [field]: Math.max(0, current + delta) })
  }

  static async #onAdjustStress(event, target) {
    const delta   = parseInt(target.dataset.delta, 10)
    const current = this.document.system.arcaneStress.value ?? 0
    const max     = this.document.system.arcaneStress.threshold
    await this.document.update({ "system.arcaneStress.value": Math.max(0, Math.min(max, current + delta)) })
  }

  static async #onClearStress(_event, _target) {
    await this.document.update({ "system.arcaneStress.value": 0 })
  }
}

/** Strip HTML tags and collapse whitespace for use in data-tooltip attributes. */
function _stripHtml(html, maxLen = 300) {
  if (!html) return ""
  const text = html.replace(/<[^>]*>/g, " ").replace(/\s+/g, " ").trim()
  return text.length > maxLen ? text.slice(0, maxLen - 1) + "…" : text
}
