import { getEmptyClaudeCraftAbilityModifier, type TalentModifiers } from './claudeCraftTalents'
import { getArenaAbility, getArenaClassVitals, type ArenaClassId } from './actionClassKits'

export type PartyRole = 'healer' | 'tank' | 'melee' | 'ranged'
export type SpellSlot = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10

export type SpellDefinition = {
  slot: SpellSlot
  abilityId: ClaudeCraftHealerAbilityId
  name: string
  cooldown: number
  castTime: number
}

export type ActionHealerClassId = 'priest' | 'paladin' | 'shaman' | 'druid'

export type ClaudeCraftHealerAbilityId =
  | 'smite'
  | 'lesser_heal'
  | 'power_word_fortitude'
  | 'shadow_word_pain'
  | 'power_word_shield'
  | 'renew'
  | 'mind_blast'
  | 'heal'
  | 'mind_flay'
  | 'flash_heal'
  | 'judgement'
  | 'holy_light'
  | 'flash_of_light'
  | 'blessing_of_might'
  | 'consecration'
  | 'divine_protection'
  | 'exorcism'
  | 'lay_on_hands'
  | 'seal_of_righteousness'
  | 'lightning_bolt'
  | 'healing_wave'
  | 'lightning_shield'
  | 'earth_shock'
  | 'flame_shock'
  | 'frost_shock'
  | 'healing_touch'
  | 'rejuvenation'
  | 'regrowth'
  | 'barkskin'
  | 'wrath'
  | 'moonfire'
  | 'insect_swarm'
  | 'starfire'
  | 'faerie_fire'

export type ClaudeCraftPriestAbilityId = Extract<
  ClaudeCraftHealerAbilityId,
  | 'smite'
  | 'lesser_heal'
  | 'power_word_fortitude'
  | 'shadow_word_pain'
  | 'power_word_shield'
  | 'renew'
  | 'mind_blast'
  | 'heal'
  | 'mind_flay'
  | 'flash_heal'
>

export type ActionSpellTarget = 'Enemy target' | 'Friendly target'
export type ActionSpellSchool = 'Holy' | 'Shadow' | 'Nature' | 'Fire' | 'Frost' | 'Arcane' | 'Physical'

export type ActionSpellbook = {
  classId: ActionHealerClassId
  iconFolder: string
  spells: Record<SpellSlot, SpellDefinition>
  manaCosts: Record<SpellSlot, number>
  ranks: Record<SpellSlot, number>
  schools: Record<SpellSlot, ActionSpellSchool>
  targets: Record<SpellSlot, ActionSpellTarget>
  descriptions: Record<SpellSlot, string>
  bar: SpellSlot[]
}

export type CastState = {
  spell: SpellSlot
  targetId: string
  remaining: number
  total: number
}

export type HealOverTimeEffectId = 'renew' | 'rejuvenation' | 'regrowth'

export type HealOverTimeEffect = {
  id: HealOverTimeEffectId
  label: string
  remaining: number
  tickHeal: number
  tickSeconds: number
  tickTimer: number
}

export type HealOverTimeFrame = Pick<HealOverTimeEffect, 'id' | 'label' | 'remaining'>

export type SpellResourceState = {
  mana: number
  maxMana: number
  storedMomentumCasts: number
  storedMomentumReady: boolean
}

export type ActionRangePoint = {
  x: number
  y: number
  radius?: number
}

export type ActionSpellReachBlocker = 'range' | 'lineOfSight'

export type ActionSpellReachResult = {
  distance: number
  effectiveRange: number
  ok: true
} | {
  distance: number
  effectiveRange: number
  ok: false
  reason: ActionSpellReachBlocker
}

export type ShieldedHealthState = {
  hp: number
  maxHp: number
  shield: number
  invulnerableTimer?: number
}

export type RenewState = {
  healOverTimes?: HealOverTimeEffect[]
  renewTimer: number
  renewTickTimer: number
}

export const ACTION_SQUARE_SIZE = 48
export const HEAL_RANGE = ACTION_SQUARE_SIZE * 6
export const RANGED_ATTACK_RANGE = ACTION_SQUARE_SIZE * 6

export const ACTION_MOVEMENT_RULES = {
  baseSpeed: 245,
} as const

export const ACTION_CONTROL_RULES = {
  stunSeconds: 0.92,
  invulnerableSeconds: 0.7,
} as const

export const HEALING_SPELL_RULES = {
  mendHeal: 34,
  renewSeconds: 15,
  renewTickSeconds: 3,
  renewHeal: 28,
  radianceHeal: 250,
  sunWardShield: 145,
  purifyHeal: 131,
  dungeonShieldCap: 60,
  arenaShieldCapRatio: 0.72,
} as const

export const SPELLS: Record<SpellSlot, SpellDefinition> = {
  1: { slot: 1, abilityId: 'smite', name: 'Smite', cooldown: 0, castTime: 2.5 },
  2: { slot: 2, abilityId: 'lesser_heal', name: 'Lesser Heal', cooldown: 0, castTime: 2 },
  3: { slot: 3, abilityId: 'power_word_fortitude', name: 'Power Word: Fortitude', cooldown: 0, castTime: 0 },
  4: { slot: 4, abilityId: 'shadow_word_pain', name: 'Shadow Word: Pain', cooldown: 0, castTime: 0 },
  5: { slot: 5, abilityId: 'power_word_shield', name: 'Power Word: Shield', cooldown: 6, castTime: 0 },
  6: { slot: 6, abilityId: 'renew', name: 'Renew', cooldown: 0, castTime: 0 },
  7: { slot: 7, abilityId: 'mind_blast', name: 'Mind Blast', cooldown: 8, castTime: 1.5 },
  8: { slot: 8, abilityId: 'heal', name: 'Heal', cooldown: 0, castTime: 2.5 },
  9: { slot: 9, abilityId: 'mind_flay', name: 'Mind Flay', cooldown: 0, castTime: 0 },
  10: { slot: 10, abilityId: 'flash_heal', name: 'Flash Heal', cooldown: 0, castTime: 1.5 },
}

export const SPELL_MANA_COSTS: Record<SpellSlot, number> = {
  1: 70,
  2: 65,
  3: 80,
  4: 55,
  5: 100,
  6: 75,
  7: 95,
  8: 130,
  9: 45,
  10: 75,
}

const ALL_SPELL_SLOTS: SpellSlot[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
export const CLAUDECRAFT_PRIEST_ACTION_BAR: SpellSlot[] = ALL_SPELL_SLOTS
export const DUNGEON_PRIEST_SPELL_BAR: SpellSlot[] = CLAUDECRAFT_PRIEST_ACTION_BAR
export const ARENA_PRIEST_SPELL_BAR: SpellSlot[] = CLAUDECRAFT_PRIEST_ACTION_BAR
export const SPELL_GLOBAL_COOLDOWN_SECONDS = 1.5
export const SPELL_RESOURCE_MAX = 900
export const SPELL_RESOURCE_REGEN = 10

export const PRIEST_SPELL_ICONS: Record<SpellSlot, ClaudeCraftPriestAbilityId> = {
  1: 'smite',
  2: 'lesser_heal',
  3: 'power_word_fortitude',
  4: 'shadow_word_pain',
  5: 'power_word_shield',
  6: 'renew',
  7: 'mind_blast',
  8: 'heal',
  9: 'mind_flay',
  10: 'flash_heal',
}

export const PRIEST_SPELL_RANKS: Record<SpellSlot, number> = {
  1: 4,
  2: 3,
  3: 3,
  4: 3,
  5: 3,
  6: 3,
  7: 3,
  8: 2,
  9: 1,
  10: 1,
}

export const PRIEST_SPELL_SCHOOLS: Record<SpellSlot, 'Holy' | 'Shadow'> = {
  1: 'Holy',
  2: 'Holy',
  3: 'Holy',
  4: 'Shadow',
  5: 'Holy',
  6: 'Holy',
  7: 'Shadow',
  8: 'Holy',
  9: 'Shadow',
  10: 'Holy',
}

export const PRIEST_SPELL_TARGETS: Record<SpellSlot, 'Enemy target' | 'Friendly target'> = {
  1: 'Enemy target',
  2: 'Friendly target',
  3: 'Friendly target',
  4: 'Enemy target',
  5: 'Friendly target',
  6: 'Friendly target',
  7: 'Enemy target',
  8: 'Friendly target',
  9: 'Enemy target',
  10: 'Friendly target',
}

export const PRIEST_SPELL_DESCRIPTIONS: Record<SpellSlot, string> = {
  1: 'Smites the enemy for 64 to 78 Holy damage.',
  2: 'Heals a friendly target for 110 to 132.',
  3: 'Increases the target Stamina by 12 for 30 min.',
  4: 'A word of darkness causes 84 Shadow damage over 18 sec.',
  5: 'Shields the target, absorbing 145 damage for 30 sec.',
  6: 'Heals the target for 140 over 15 sec.',
  7: 'Blasts the target mind for 86 to 94 Shadow damage.',
  8: 'A slow but powerful prayer that heals a friendly target for 230 to 270.',
  9: 'Assaults the target mind, causing 12 Shadow damage each second for 3 sec.',
  10: 'A fast prayer that heals a friendly target for 120 to 142.',
}

export const ACTION_HEALER_SPELLBOOKS: Record<ActionHealerClassId, ActionSpellbook> = {
  priest: createSpellbook('priest', 'priest', SPELLS, SPELL_MANA_COSTS, PRIEST_SPELL_RANKS, PRIEST_SPELL_SCHOOLS, PRIEST_SPELL_TARGETS, PRIEST_SPELL_DESCRIPTIONS),
  paladin: createSpellbook(
    'paladin',
    'paladin',
    {
      1: spell(1, 'judgement', 'Judgement', 8, 0),
      2: spell(2, 'holy_light', 'Holy Light', 0, 2.5),
      3: spell(3, 'flash_of_light', 'Flash of Light', 0, 1.5),
      4: spell(4, 'blessing_of_might', 'Blessing of Might', 0, 0),
      5: spell(5, 'consecration', 'Consecration', 8, 0),
      6: spell(6, 'divine_protection', 'Divine Protection', 30, 0),
      7: spell(7, 'exorcism', 'Exorcism', 15, 0),
      8: spell(8, 'lay_on_hands', 'Lay on Hands', 60, 0),
      9: spell(9, 'seal_of_righteousness', 'Seal of Righteousness', 0, 0),
      10: spell(10, 'flash_of_light', 'Flash of Light', 0, 1.5),
    },
    costs(70, 130, 75, 80, 105, 100, 95, 220, 45, 75),
    ranks(4, 3, 3, 3, 3, 2, 2, 1, 2, 3),
    schools('Holy', 'Holy', 'Holy', 'Holy', 'Holy', 'Holy', 'Holy', 'Holy', 'Holy', 'Holy'),
    targets('Enemy target', 'Friendly target', 'Friendly target', 'Friendly target', 'Enemy target', 'Friendly target', 'Enemy target', 'Friendly target', 'Enemy target', 'Friendly target'),
    descriptions(
      'Judges the enemy for Holy damage.',
      'Heals a friendly target with a large burst of Holy light.',
      'A fast, efficient Holy heal.',
      'Blesses an ally, represented here as a small protective ward.',
      'Consecrates nearby ground under the enemy for Holy damage.',
      'Wraps the target in divine protection.',
      'Strikes an enemy with Holy force.',
      'A long-cooldown emergency heal.',
      'Seals your weapon with Holy damage against the enemy.',
      'A fast, efficient Holy heal.',
    ),
  ),
  shaman: createSpellbook(
    'shaman',
    'shaman',
    {
      1: spell(1, 'lightning_bolt', 'Lightning Bolt', 0, 2.5),
      2: spell(2, 'healing_wave', 'Healing Wave', 0, 2.5),
      3: spell(3, 'lightning_shield', 'Lightning Shield', 10, 0),
      4: spell(4, 'earth_shock', 'Earth Shock', 6, 0),
      5: spell(5, 'flame_shock', 'Flame Shock', 6, 0),
      6: spell(6, 'frost_shock', 'Frost Shock', 6, 0),
      7: spell(7, 'healing_wave', 'Healing Wave', 0, 2.5),
      8: spell(8, 'lightning_shield', 'Lightning Shield', 10, 0),
      9: spell(9, 'lightning_bolt', 'Lightning Bolt', 0, 2.5),
      10: spell(10, 'healing_wave', 'Healing Wave', 0, 2.5),
    },
    costs(70, 125, 90, 75, 80, 80, 125, 90, 70, 125),
    ranks(4, 3, 3, 3, 3, 3, 3, 3, 4, 3),
    schools('Nature', 'Nature', 'Nature', 'Nature', 'Fire', 'Frost', 'Nature', 'Nature', 'Nature', 'Nature'),
    targets('Enemy target', 'Friendly target', 'Friendly target', 'Enemy target', 'Enemy target', 'Enemy target', 'Friendly target', 'Friendly target', 'Enemy target', 'Friendly target'),
    descriptions(
      'Hurls lightning at the enemy.',
      'Heals a friendly target with a steady wave.',
      'Surrounds an ally with a lightning shield.',
      'Shocks an enemy with earth power.',
      'Burns an enemy with flame shock damage over time.',
      'Shocks an enemy with frost.',
      'Heals a friendly target with a steady wave.',
      'Surrounds an ally with a lightning shield.',
      'Hurls lightning at the enemy.',
      'Heals a friendly target with a steady wave.',
    ),
  ),
  druid: createSpellbook(
    'druid',
    'druid',
    {
      1: spell(1, 'wrath', 'Wrath', 0, 2),
      2: spell(2, 'rejuvenation', 'Rejuvenation', 0, 0),
      3: spell(3, 'regrowth', 'Regrowth', 0, 2),
      4: spell(4, 'healing_touch', 'Healing Touch', 0, 2.5),
      5: spell(5, 'barkskin', 'Barkskin', 12, 0),
      6: spell(6, 'moonfire', 'Moonfire', 0, 0),
      7: spell(7, 'insect_swarm', 'Insect Swarm', 0, 0),
      8: spell(8, 'starfire', 'Starfire', 0, 3),
      9: spell(9, 'faerie_fire', 'Faerie Fire', 6, 0),
      10: spell(10, 'regrowth', 'Regrowth', 0, 2),
    },
    costs(70, 75, 105, 130, 100, 55, 65, 120, 50, 105),
    ranks(4, 3, 3, 3, 2, 3, 2, 2, 2, 3),
    schools('Nature', 'Nature', 'Nature', 'Nature', 'Nature', 'Arcane', 'Nature', 'Arcane', 'Nature', 'Nature'),
    targets('Enemy target', 'Friendly target', 'Friendly target', 'Friendly target', 'Friendly target', 'Enemy target', 'Enemy target', 'Enemy target', 'Enemy target', 'Friendly target'),
    descriptions(
      'Calls nature damage down on the enemy.',
      'Heals the target over time.',
      'Heals immediately and leaves a rejuvenating bloom.',
      'A slow, powerful nature heal.',
      'Protects the target with barkskin.',
      'Burns the enemy with arcane lunar damage over time.',
      'Sends insects to wear down the enemy.',
      'A slow, heavy arcane strike.',
      'Weakens the enemy with faerie fire.',
      'Heals immediately and leaves a rejuvenating bloom.',
    ),
  ),
}

function spell(slot: SpellSlot, abilityId: ClaudeCraftHealerAbilityId, name: string, cooldown: number, castTime: number): SpellDefinition {
  return { abilityId, castTime, cooldown, name, slot }
}

function createSpellbook(
  classId: ActionHealerClassId,
  iconFolder: string,
  spells: Record<SpellSlot, SpellDefinition>,
  manaCosts: Record<SpellSlot, number>,
  ranks: Record<SpellSlot, number>,
  schools: Record<SpellSlot, ActionSpellSchool>,
  targets: Record<SpellSlot, ActionSpellTarget>,
  descriptions: Record<SpellSlot, string>,
): ActionSpellbook {
  return {
    bar: ALL_SPELL_SLOTS,
    classId,
    descriptions,
    iconFolder,
    manaCosts,
    ranks,
    schools,
    spells,
    targets,
  }
}

function costs(
  one: number,
  two: number,
  three: number,
  four: number,
  five: number,
  six: number,
  seven: number,
  eight: number,
  nine: number,
  ten: number,
): Record<SpellSlot, number> {
  return { 1: one, 2: two, 3: three, 4: four, 5: five, 6: six, 7: seven, 8: eight, 9: nine, 10: ten }
}

function ranks(
  one: number,
  two: number,
  three: number,
  four: number,
  five: number,
  six: number,
  seven: number,
  eight: number,
  nine: number,
  ten: number,
): Record<SpellSlot, number> {
  return { 1: one, 2: two, 3: three, 4: four, 5: five, 6: six, 7: seven, 8: eight, 9: nine, 10: ten }
}

function schools(
  one: ActionSpellSchool,
  two: ActionSpellSchool,
  three: ActionSpellSchool,
  four: ActionSpellSchool,
  five: ActionSpellSchool,
  six: ActionSpellSchool,
  seven: ActionSpellSchool,
  eight: ActionSpellSchool,
  nine: ActionSpellSchool,
  ten: ActionSpellSchool,
): Record<SpellSlot, ActionSpellSchool> {
  return { 1: one, 2: two, 3: three, 4: four, 5: five, 6: six, 7: seven, 8: eight, 9: nine, 10: ten }
}

function targets(
  one: ActionSpellTarget,
  two: ActionSpellTarget,
  three: ActionSpellTarget,
  four: ActionSpellTarget,
  five: ActionSpellTarget,
  six: ActionSpellTarget,
  seven: ActionSpellTarget,
  eight: ActionSpellTarget,
  nine: ActionSpellTarget,
  ten: ActionSpellTarget,
): Record<SpellSlot, ActionSpellTarget> {
  return { 1: one, 2: two, 3: three, 4: four, 5: five, 6: six, 7: seven, 8: eight, 9: nine, 10: ten }
}

function descriptions(
  one: string,
  two: string,
  three: string,
  four: string,
  five: string,
  six: string,
  seven: string,
  eight: string,
  nine: string,
  ten: string,
): Record<SpellSlot, string> {
  return { 1: one, 2: two, 3: three, 4: four, 5: five, 6: six, 7: seven, 8: eight, 9: nine, 10: ten }
}

function normalizeHealerClassId(classId: string | undefined): ActionHealerClassId {
  if (classId === 'paladin' || classId === 'shaman' || classId === 'druid') return classId
  return 'priest'
}

export function createSpellCooldowns(): Record<SpellSlot, number> {
  return {
    1: 0,
    2: 0,
    3: 0,
    4: 0,
    5: 0,
    6: 0,
    7: 0,
    8: 0,
    9: 0,
    10: 0,
  }
}

export function createSpellResourceState(classId?: string): SpellResourceState {
  const vitals = classId ? getArenaClassVitals(classId as ArenaClassId) : null
  const maxMana = vitals?.maxMana ?? SPELL_RESOURCE_MAX
  return {
    mana: maxMana,
    maxMana,
    storedMomentumCasts: 0,
    storedMomentumReady: false,
  }
}

export function tickSpellCooldowns(cooldowns: Record<SpellSlot, number>, deltaSeconds: number) {
  for (const slot of ALL_SPELL_SLOTS) {
    cooldowns[slot] = Math.max(0, cooldowns[slot] - deltaSeconds)
  }
}

export function tickSpellResource(resource: SpellResourceState, deltaSeconds: number) {
  resource.mana = Math.min(resource.maxMana, resource.mana + SPELL_RESOURCE_REGEN * deltaSeconds)
}

export function getSpellManaCost(spell: SpellSlot, options?: { costMultiplier?: number, freeCast?: boolean }) {
  if (options?.freeCast) return 0
  return Math.max(1, Math.round(SPELL_MANA_COSTS[spell] * (options?.costMultiplier ?? 1)))
}

export function getActionSpellbook(classId: string | undefined): ActionSpellbook {
  return ACTION_HEALER_SPELLBOOKS[normalizeHealerClassId(classId)]
}

export function getActionSpellDefinition(classId: string | undefined, spell: SpellSlot, talentMods?: TalentModifiers) {
  const definition = getActionSpellbook(classId).spells[spell]
  const modifier = getActionTalentAbilityModifier(talentMods, definition.abilityId)
  return {
    ...definition,
    castTime: Math.max(0, roundTenths(definition.castTime * (1 + modifier.castPct))),
    cooldown: Math.max(0, roundTenths(definition.cooldown * (1 + modifier.cooldownPct))),
  }
}

export function getActionSpellRange(classId: string | undefined, spell: SpellSlot, talentMods?: TalentModifiers) {
  return getArenaAbility(getActionSpellDefinition(classId, spell, talentMods).abilityId).range
}

export function getActionSpellReach(options: {
  caster: ActionRangePoint
  target: ActionRangePoint
  classId: string | undefined
  spell: SpellSlot
  talentMods?: TalentModifiers
  includeTargetRadius?: boolean
  hasLineOfSight?: (from: ActionRangePoint, to: ActionRangePoint) => boolean
}): ActionSpellReachResult {
  const baseRange = getActionSpellRange(options.classId, options.spell, options.talentMods)
  const effectiveRange = baseRange + (options.includeTargetRadius ? options.target.radius ?? 0 : 0)
  const distance = Math.hypot(options.caster.x - options.target.x, options.caster.y - options.target.y)
  if (distance > effectiveRange) return { ok: false, reason: 'range', distance, effectiveRange }
  if (options.hasLineOfSight && !options.hasLineOfSight(options.caster, options.target)) {
    return { ok: false, reason: 'lineOfSight', distance, effectiveRange }
  }
  return { ok: true, distance, effectiveRange }
}

export function getActionSpellManaCost(classId: string | undefined, spell: SpellSlot, options?: { costMultiplier?: number, freeCast?: boolean, talentMods?: TalentModifiers }) {
  if (options?.freeCast) return 0
  const definition = getActionSpellbook(classId).spells[spell]
  const modifier = getActionTalentAbilityModifier(options?.talentMods, definition.abilityId)
  return Math.max(1, Math.round(getActionSpellbook(classId).manaCosts[spell] * (1 + modifier.costPct) * (options?.costMultiplier ?? 1)))
}

export function getActionTalentAdjustedAmount(classId: string | undefined, spell: SpellSlot, baseAmount: number, kind: 'damage' | 'heal' | 'shield', talentMods?: TalentModifiers) {
  const definition = getActionSpellbook(classId).spells[spell]
  const abilityModifier = getActionTalentAbilityModifier(talentMods, definition.abilityId)
  const globalMultiplier = kind === 'heal'
    ? talentMods?.global.healPct ?? 0
    : kind === 'damage'
      ? talentMods?.global.spellDmgPct ?? 0
      : 0
  return Math.max(0, Math.round((baseAmount + abilityModifier.flatDmg) * (1 + abilityModifier.dmgPct + globalMultiplier)))
}

export function getActionSpellTarget(classId: string | undefined, spell: SpellSlot) {
  return getActionSpellbook(classId).targets[spell]
}

export function getActionSpellIconUrl(classId: string | undefined, spell: SpellSlot) {
  const book = getActionSpellbook(classId)
  return `/ui/skills/${book.iconFolder}/${book.spells[spell].abilityId}.png`
}

export function getActionSpellTooltip(classId: string | undefined, spell: SpellSlot) {
  const book = getActionSpellbook(classId)
  return {
    description: book.descriptions[spell],
    rank: book.ranks[spell],
    school: book.schools[spell],
    target: book.targets[spell],
  }
}

export function getDungeonSpellManaCost(spell: SpellSlot, options?: { freeCast?: boolean }) {
  if (options?.freeCast) return 0
  return getSpellManaCost(spell, options)
}

export function spendSpellMana(resource: SpellResourceState, spell: SpellSlot, options?: { costMultiplier?: number }) {
  const cost = getSpellManaCost(spell, {
    costMultiplier: options?.costMultiplier,
    freeCast: resource.storedMomentumReady,
  })
  if (!resource.storedMomentumReady && resource.mana < cost) {
    return { ok: false, cost }
  }

  if (resource.storedMomentumReady) resource.storedMomentumReady = false
  else resource.mana = Math.max(0, resource.mana - cost)
  return { ok: true, cost }
}

export function spendActionSpellMana(resource: SpellResourceState, classId: string | undefined, spell: SpellSlot, options?: { costMultiplier?: number, talentMods?: TalentModifiers }) {
  const cost = getActionSpellManaCost(classId, spell, {
    costMultiplier: options?.costMultiplier,
    freeCast: resource.storedMomentumReady,
    talentMods: options?.talentMods,
  })
  if (!resource.storedMomentumReady && resource.mana < cost) {
    return { ok: false, cost }
  }

  if (resource.storedMomentumReady) resource.storedMomentumReady = false
  else resource.mana = Math.max(0, resource.mana - cost)
  return { ok: true, cost }
}

function getActionTalentAbilityModifier(talentMods: TalentModifiers | undefined, abilityId: ClaudeCraftHealerAbilityId) {
  return talentMods?.abilities[abilityId] ?? getEmptyClaudeCraftAbilityModifier()
}

function roundTenths(value: number) {
  return Math.round(value * 10) / 10
}

export function spendDungeonSpellMana(resource: SpellResourceState, spell: SpellSlot) {
  const cost = getDungeonSpellManaCost(spell, { freeCast: resource.storedMomentumReady })
  if (!resource.storedMomentumReady && resource.mana < cost) {
    return { ok: false, cost }
  }

  if (resource.storedMomentumReady) resource.storedMomentumReady = false
  else resource.mana = Math.max(0, resource.mana - cost)
  return { ok: true, cost }
}

export function recordSpellCast(resource: SpellResourceState, options?: { storedMomentum?: boolean }) {
  if (!options?.storedMomentum) return
  resource.storedMomentumCasts += 1
  if (resource.storedMomentumCasts >= 5) {
    resource.storedMomentumCasts = 0
    resource.storedMomentumReady = true
  }
}

export function applyShieldedDamage(target: ShieldedHealthState, amount: number) {
  if ((target.invulnerableTimer ?? 0) > 0) return 0
  const absorbed = Math.min(target.shield, amount)
  target.shield -= absorbed
  const damage = amount - absorbed
  target.hp = Math.max(0, target.hp - damage)
  return damage
}

export function addShield(target: ShieldedHealthState, amount: number, options?: { cap?: number, capRatio?: number }) {
  const cap = options?.cap ?? target.maxHp * (options?.capRatio ?? HEALING_SPELL_RULES.arenaShieldCapRatio)
  target.shield = Math.min(cap, target.shield + amount)
}

export function dampenHealing(amount: number, dampeningPercent = 0) {
  return amount * Math.max(0, 1 - dampeningPercent / 100)
}

export function applyRenewTimers(target: RenewState, durationSeconds: number = HEALING_SPELL_RULES.renewSeconds) {
  applyHealOverTimeTimers(target, 'renew', { durationSeconds })
}

export function applyHealOverTimeTimers(
  target: RenewState,
  id: HealOverTimeEffectId,
  options: {
    durationSeconds?: number
    tickHeal?: number
  } = {},
) {
  const durationSeconds = options.durationSeconds ?? HEALING_SPELL_RULES.renewSeconds
  const tickSeconds = HEALING_SPELL_RULES.renewTickSeconds
  const healOverTimes = target.healOverTimes ?? []
  const existing = healOverTimes.find((effect) => effect.id === id)
  if (existing) {
    existing.remaining = Math.max(existing.remaining, durationSeconds)
    existing.tickHeal = options.tickHeal ?? existing.tickHeal
    existing.tickTimer = tickSeconds
  } else {
    healOverTimes.push({
      id,
      label: HEAL_OVER_TIME_LABELS[id],
      remaining: durationSeconds,
      tickHeal: options.tickHeal ?? HEALING_SPELL_RULES.renewHeal,
      tickSeconds,
      tickTimer: tickSeconds,
    })
  }
  target.healOverTimes = healOverTimes
  syncRenewCompatibilityTimers(target)
}

export function tickHealOverTimes(target: RenewState, deltaSeconds: number, onTick: (effect: HealOverTimeEffect) => void) {
  const healOverTimes = target.healOverTimes
  if (!healOverTimes || healOverTimes.length === 0) {
    tickLegacyRenew(target, deltaSeconds, onTick)
    return
  }

  for (const effect of healOverTimes) {
    effect.remaining = Math.max(0, effect.remaining - deltaSeconds)
    effect.tickTimer -= deltaSeconds
    while (effect.remaining > 0 && effect.tickTimer <= 0) {
      onTick(effect)
      effect.tickTimer += effect.tickSeconds
    }
  }
  target.healOverTimes = healOverTimes.filter((effect) => effect.remaining > 0)
  syncRenewCompatibilityTimers(target)
}

export function getActiveHealOverTimeFrames(target: RenewState): HealOverTimeFrame[] {
  const healOverTimes = target.healOverTimes?.filter((effect) => effect.remaining > 0) ?? []
  if (healOverTimes.length > 0) {
    return healOverTimes.map(({ id, label, remaining }) => ({ id, label, remaining }))
  }
  return target.renewTimer > 0 ? [{ id: 'renew', label: HEAL_OVER_TIME_LABELS.renew, remaining: target.renewTimer }] : []
}

export function getHealOverTimeEffectId(abilityId: string): HealOverTimeEffectId | null {
  if (abilityId === 'renew' || abilityId === 'rejuvenation' || abilityId === 'regrowth') return abilityId
  return null
}

function tickLegacyRenew(target: RenewState, deltaSeconds: number, onTick: (effect: HealOverTimeEffect) => void) {
  if (target.renewTimer <= 0) return
  target.renewTimer = Math.max(0, target.renewTimer - deltaSeconds)
  target.renewTickTimer -= deltaSeconds
  if (target.renewTickTimer <= 0) {
    onTick({
      id: 'renew',
      label: HEAL_OVER_TIME_LABELS.renew,
      remaining: target.renewTimer,
      tickHeal: HEALING_SPELL_RULES.renewHeal,
      tickSeconds: HEALING_SPELL_RULES.renewTickSeconds,
      tickTimer: target.renewTickTimer,
    })
    target.renewTickTimer += HEALING_SPELL_RULES.renewTickSeconds
  }
}

function syncRenewCompatibilityTimers(target: RenewState) {
  const renew = target.healOverTimes?.find((effect) => effect.id === 'renew')
  target.renewTimer = renew?.remaining ?? 0
  target.renewTickTimer = renew?.tickTimer ?? 0
}

const HEAL_OVER_TIME_LABELS: Record<HealOverTimeEffectId, string> = {
  regrowth: 'Regrowth',
  rejuvenation: 'Rejuvenation',
  renew: 'Renew',
}
