import { KHEZU_BOSS_METADATA } from '../content/bosses'
import type { Iwt2BossTickResult } from './bossAi'
import type {
  Iwt2ArenaEvent,
  Iwt2ArenaIndicator,
  Iwt2ArenaState,
  Iwt2BossEntityState,
  Iwt2MechanicCircleState,
  Iwt2PartyEntityState,
} from './types'
import {
  applyPartyDamageInShape,
  createCircleIndicator,
  createDonutIndicator,
  indicatorPhaseFromAttack,
} from './mechanics'
import {
  clampVec2ToArena,
  distanceVec2,
  moveToward,
  scaleVec2,
  subtractVec2,
  withFallbackFacing,
} from './vector'

export function tickKhezu(state: Iwt2ArenaState, dt: number): Iwt2BossTickResult {
  const events: Iwt2ArenaEvent[] = []
  let party = state.party
  let boss = {
    ...state.boss,
    meleeCooldownRemaining: Math.max(0, state.boss.meleeCooldownRemaining - dt),
    chargeCooldownRemaining: Math.max(0, state.boss.chargeCooldownRemaining - dt),
    fireballCooldownRemaining: Math.max(0, state.boss.fireballCooldownRemaining - dt),
    phaseSecondsRemaining: Math.max(0, state.boss.phaseSecondsRemaining - dt),
    velocity: { x: 0, y: 0 },
  }
  const target = getBossTarget(party)

  if (boss.health <= 0 || !target) return withKhezuIndicators({ boss, party, events })

  if (boss.attackPhase === 'thunderRingWindup') {
    boss = {
      ...boss,
      facing: withFallbackFacing(subtractVec2(target.position, boss.position), boss.facing),
    }
    if (boss.phaseSecondsRemaining <= 0) {
      const result = applyThunderRing(party, boss, state.time + dt)
      party = result.party
      events.push(...result.events)
      boss = {
        ...boss,
        attackPhase: 'thunderRingRecover',
        phaseSecondsRemaining: 0.45,
      }
    }
    return withKhezuIndicators({ boss, party, events })
  }

  if (boss.attackPhase === 'lightningStrikeWindup') {
    if (boss.phaseSecondsRemaining <= 0) {
      const result = applyLightningStrikes(party, boss, state.time + dt)
      party = result.party
      events.push(...result.events)
      boss = {
        ...boss,
        attackPhase: 'lightningStrikeRecover',
        phaseSecondsRemaining: 0.38,
      }
    }
    return withKhezuIndicators({ boss, party, events })
  }

  if (boss.attackPhase === 'thunderRingRecover' || boss.attackPhase === 'lightningStrikeRecover') {
    if (boss.phaseSecondsRemaining <= 0) {
      boss = {
        ...boss,
        attackPhase: 'idle',
        mechanicCircles: [],
        phaseSecondsRemaining: 0,
      }
    }
    return withKhezuIndicators({ boss, party, events })
  }

  if (boss.chargeCooldownRemaining <= 0) {
    boss = {
      ...boss,
      attackPhase: 'thunderRingWindup',
      chargeCooldownRemaining: KHEZU_BOSS_METADATA.thunderRingCooldown!,
      phaseSecondsRemaining: KHEZU_BOSS_METADATA.thunderRingWindup!,
      velocity: { x: 0, y: 0 },
    }
    return withKhezuIndicators({ boss, party, events })
  }

  if (boss.fireballCooldownRemaining <= 0) {
    boss = {
      ...boss,
      attackPhase: 'lightningStrikeWindup',
      fireballCooldownRemaining: KHEZU_BOSS_METADATA.lightningStrikeCooldown!,
      mechanicCircles: createLightningTargets(party),
      phaseSecondsRemaining: KHEZU_BOSS_METADATA.lightningStrikeWindup!,
      velocity: { x: 0, y: 0 },
    }
    return withKhezuIndicators({ boss, party, events })
  }

  const meleeResult = maybeApplyMelee(party, boss, target, state.time + dt)
  party = meleeResult.party
  events.push(...meleeResult.events)
  boss = meleeResult.boss
  if (events.length > 0) return withKhezuIndicators({ boss, party, events })

  const nextPosition = clampVec2ToArena(
    moveToward(boss.position, target.position, KHEZU_BOSS_METADATA.moveSpeed * dt),
    boss.radius,
    state.bounds,
  )
  boss = {
    ...boss,
    position: nextPosition,
    velocity: scaleVec2(subtractVec2(nextPosition, boss.position), dt > 0 ? 1 / dt : 0),
    facing: withFallbackFacing(subtractVec2(target.position, nextPosition), boss.facing),
  }
  return withKhezuIndicators({ boss, party, events })
}

function createLightningTargets(party: Iwt2PartyEntityState[]): Iwt2MechanicCircleState[] {
  return [...party]
    .filter((member) => member.health > 0)
    .sort((a, b) => (a.health / a.maxHealth) - (b.health / b.maxHealth))
    .slice(0, KHEZU_BOSS_METADATA.lightningStrikeCount!)
    .map((member, index) => ({
      id: `lightning-${index}`,
      position: { ...member.position },
      radius: KHEZU_BOSS_METADATA.lightningStrikeRadius!,
    }))
}

function applyThunderRing(
  party: Iwt2PartyEntityState[],
  boss: Iwt2BossEntityState,
  time: number,
): { party: Iwt2PartyEntityState[], events: Iwt2ArenaEvent[] } {
  const result = applyPartyDamageInShape(party, {
    kind: 'donut',
    position: boss.position,
    innerRadius: KHEZU_BOSS_METADATA.thunderRingInnerRadius!,
    outerRadius: KHEZU_BOSS_METADATA.thunderRingOuterRadius!,
  }, {
    damage: KHEZU_BOSS_METADATA.thunderRingDamage!,
    knockdownSeconds: 0,
    sourceId: boss.id,
    stunSeconds: KHEZU_BOSS_METADATA.thunderRingStunSeconds!,
    time,
  })
  return { party: result.party, events: result.events }
}

function applyLightningStrikes(
  party: Iwt2PartyEntityState[],
  boss: Iwt2BossEntityState,
  time: number,
): { party: Iwt2PartyEntityState[], events: Iwt2ArenaEvent[] } {
  let nextParty = party
  const events: Iwt2ArenaEvent[] = []
  for (const circle of boss.mechanicCircles) {
    const result = applyPartyDamageInShape(nextParty, {
      kind: 'circle',
      position: circle.position,
      radius: circle.radius,
    }, {
      damage: KHEZU_BOSS_METADATA.lightningStrikeDamage!,
      knockdownSeconds: 0,
      sourceId: boss.id,
      stunSeconds: KHEZU_BOSS_METADATA.lightningStrikeStunSeconds!,
      time,
    })
    nextParty = result.party
    events.push(...result.events)
  }
  return { party: nextParty, events }
}

function getBossTarget(party: Iwt2PartyEntityState[]): Iwt2PartyEntityState | undefined {
  const livingTank = party.find((member) => member.classId === 'paladin' && member.health > 0)
  if (livingTank) return livingTank
  return party.find((member) => member.health > 0)
}

function maybeApplyMelee(
  party: Iwt2PartyEntityState[],
  boss: Iwt2BossEntityState,
  target: Iwt2PartyEntityState,
  time: number,
): { boss: Iwt2BossEntityState, party: Iwt2PartyEntityState[], events: Iwt2ArenaEvent[] } {
  if (boss.meleeCooldownRemaining > 0) return { boss, party, events: [] }
  if (distanceVec2(boss.position, target.position) > KHEZU_BOSS_METADATA.meleeRange + target.radius) {
    return { boss, party, events: [] }
  }
  const result = applyPartyDamageInShape(party, {
    kind: 'circle',
    position: boss.position,
    radius: KHEZU_BOSS_METADATA.meleeRange,
  }, {
    damage: KHEZU_BOSS_METADATA.meleeDamage,
    sourceId: boss.id,
    time,
  })
  return {
    boss: { ...boss, meleeCooldownRemaining: KHEZU_BOSS_METADATA.meleeCooldown },
    party: result.party,
    events: result.events,
  }
}

function withKhezuIndicators(result: Omit<Iwt2BossTickResult, 'indicators'>): Iwt2BossTickResult {
  return {
    ...result,
    indicators: createKhezuIndicators(result.boss),
  }
}

function createKhezuIndicators(boss: Iwt2BossEntityState): Iwt2ArenaIndicator[] {
  const indicators: Iwt2ArenaIndicator[] = []
  if (boss.attackPhase === 'thunderRingWindup' || boss.attackPhase === 'thunderRingRecover') {
    indicators.push(createDonutIndicator({
      color: '#77d9ff',
      id: `${boss.id}:thunder-ring`,
      innerRadius: KHEZU_BOSS_METADATA.thunderRingInnerRadius!,
      mechanicId: 'khezu-thunder-ring',
      outerRadius: KHEZU_BOSS_METADATA.thunderRingOuterRadius!,
      phase: indicatorPhaseFromAttack(
        boss.attackPhase === 'thunderRingWindup',
        boss.attackPhase === 'thunderRingRecover',
      ),
      position: boss.position,
      sourceId: boss.id,
    }))
  }
  if (boss.attackPhase === 'lightningStrikeWindup' || boss.attackPhase === 'lightningStrikeRecover') {
    for (const circle of boss.mechanicCircles) {
      indicators.push(createCircleIndicator({
        color: '#9be8ff',
        id: `${boss.id}:${circle.id}`,
        mechanicId: 'khezu-lightning-strike',
        phase: indicatorPhaseFromAttack(
          boss.attackPhase === 'lightningStrikeWindup',
          boss.attackPhase === 'lightningStrikeRecover',
        ),
        position: circle.position,
        radius: circle.radius,
        sourceId: boss.id,
      }))
    }
  }
  return indicators
}
