import type { PartyMember, Spell } from '../game'
import { effectiveMaxHealth } from './rules'

export type CpuHealBehavior = {
  directHealThreshold: number
  groupHealThreshold: number
  hotThreshold: number
  shieldThreshold: number
}

export type CpuTurnBehavior = CpuHealBehavior & {
  actionEveryTicks: number
  mistakeChance: number
}

export function shouldCpuAct({
  elapsedTicks,
  behavior,
}: {
  elapsedTicks: number
  behavior: CpuTurnBehavior
}) {
  return elapsedTicks % behavior.actionEveryTicks === 0 && Math.random() >= behavior.mistakeChance
}

export function chooseCpuHealActions({
  party,
  spells,
  behavior,
  preferSlots = false,
}: {
  party: PartyMember[]
  spells: Spell[]
  behavior: CpuHealBehavior
  preferSlots?: boolean
}) {
  let livingCount = 0
  let healthRatioTotal = 0
  let lowest: PartyMember | null = null
  let tank: PartyMember | null = null
  let cleanseTarget: PartyMember | null = null
  let renewTarget: PartyMember | null = null
  let shieldTarget: PartyMember | null = null
  let woundedCount = 0

  for (const member of party) {
    if (member.health <= 0) continue
    livingCount += 1
    const ratio = member.health / effectiveMaxHealth(member)
    healthRatioTotal += ratio
    if (!lowest || ratio < lowest.health / effectiveMaxHealth(lowest)) lowest = member
    if (!tank && member.role === 'Tank') tank = member
    if (!cleanseTarget && (member.debuff || (member.poisonStacks ?? 0) > 0)) cleanseTarget = member
    if (!renewTarget && member.hotTicks <= 1 && ratio < behavior.hotThreshold) renewTarget = member
    if (!shieldTarget && member.role === 'Tank' && member.shield <= 5 && ratio < behavior.shieldThreshold) {
      shieldTarget = member
    }
    if (ratio < behavior.directHealThreshold) woundedCount += 1
  }

  if (!lowest || livingCount === 0) return []
  const averageHealth = healthRatioTotal / livingCount
  const spellByKind = (kind: Spell['kind']) => spells.find((candidate) => candidate.kind === kind)
  const spellBySlot = (slot: string) => spells.find((candidate) => candidate.key === slot)
  const direct = preferSlots ? spellBySlot('1') : spellByKind('direct')
  const hot = preferSlots ? spellBySlot('2') : spellByKind('hot')
  const group = preferSlots ? spellBySlot('3') : spellByKind('group')
  const shield = preferSlots ? spellBySlot('4') : spellByKind('shield')
  const cleanse = preferSlots ? spellBySlot('5') : spellByKind('cleanse')
  const ordered: Array<{ spell: Spell | undefined; targetId: string | null }> = [
    { spell: cleanseTarget ? cleanse : undefined, targetId: cleanseTarget?.id ?? null },
    { spell: averageHealth < behavior.groupHealThreshold ? group : undefined, targetId: lowest.id },
    { spell: shieldTarget ? shield : undefined, targetId: shieldTarget?.id ?? null },
    { spell: woundedCount > 0 ? direct : undefined, targetId: lowest.id },
    { spell: renewTarget ? hot : undefined, targetId: renewTarget?.id ?? null },
    { spell: tank ? direct : undefined, targetId: tank?.id ?? null },
  ]

  return ordered
    .filter((action): action is { spell: Spell; targetId: string } => Boolean(action.spell && action.targetId))
}

export function chooseCpuHealAction(options: Parameters<typeof chooseCpuHealActions>[0]) {
  return chooseCpuHealActions(options)[0] ?? null
}

export function runCpuHealTurn<TSide>({
  side,
  elapsedTicks,
  spells,
  behavior,
  preferSlots = false,
  applySpell,
}: {
  side: TSide & { party: PartyMember[] }
  elapsedTicks: number
  spells: Spell[]
  behavior: CpuTurnBehavior
  preferSlots?: boolean
  applySpell: (side: TSide, spell: Spell, targetId: string) => void
}) {
  if (!shouldCpuAct({ elapsedTicks, behavior })) return false
  const action = chooseCpuHealAction({
    party: side.party,
    spells,
    behavior,
    preferSlots,
  })
  if (!action) return false
  applySpell(side, action.spell, action.targetId)
  return true
}
