import type { Spell } from '../game'
import { stackCount, type StackCounts } from './stackCounts'

export type SlotModifier<T extends string> = {
  stacks: StackCounts<T>
  id: (slot: string) => T
  multiplier?: number
}

export type StackModifier<T extends string> = {
  stacks: StackCounts<T>
  id: T
  multiplier?: number
}

function slotKey(spell: Spell) {
  return String(spell.key)
}

function slotModifierStacks<T extends string>(spell: Spell, modifier?: SlotModifier<T>) {
  return modifier ? stackCount(modifier.stacks, modifier.id(slotKey(spell))) : 0
}

function stackModifierStacks<T extends string>(modifier?: StackModifier<T>) {
  return modifier ? stackCount(modifier.stacks, modifier.id) : 0
}

function stackMultiplier<T extends string>(modifier?: StackModifier<T>) {
  if (!modifier) return 1
  return (modifier.multiplier ?? 1.25) ** stackModifierStacks(modifier)
}

export function spellSlotMultiplier<TPositive extends string, TNegative extends string = never>(
  spell: Spell,
  positive?: SlotModifier<TPositive>,
  negative?: SlotModifier<TNegative>,
) {
  const positiveMultiplier = (positive?.multiplier ?? 0.75) ** slotModifierStacks(spell, positive)
  const negativeMultiplier = (negative?.multiplier ?? 1.25) ** slotModifierStacks(spell, negative)
  return positiveMultiplier * negativeMultiplier
}

export function spellCooldownMultiplier<TPositive extends string, TNegative extends string = never>(
  spell: Spell,
  cooldownDown?: SlotModifier<TPositive>,
  cooldownUp?: SlotModifier<TNegative>,
) {
  return spellSlotMultiplier(spell, cooldownDown, cooldownUp)
}

export function spellResourceCost<TPositive extends string, TNegative extends string = never>({
  spell,
  costDown,
  costUp,
  freeCastReady = false,
  freeCast,
}: {
  spell: Spell
  costDown?: SlotModifier<TPositive>
  costUp?: SlotModifier<TNegative>
  freeCastReady?: boolean
  freeCast?: StackModifier<TPositive>
}) {
  if (freeCastReady && stackModifierStacks(freeCast) > 0) return 0
  return Math.ceil(spell.cost * spellSlotMultiplier(spell, costDown, costUp))
}

export function spellExtraTargets<T extends string>(
  spell: Spell,
  extraTarget: SlotModifier<T>,
) {
  return slotModifierStacks(spell, extraTarget)
}

export function spellPowerMultiplier<T extends string>(modifier: StackModifier<T>) {
  return stackMultiplier(modifier)
}

export function hasSpellStack<T extends string>(modifier: StackModifier<T>) {
  return stackModifierStacks(modifier) > 0
}
