import { GREAT_JAGGI_BOSS_METADATA } from '../content/bosses'
import type { Iwt2BossTickResult } from './bossAi'
import type {
  Iwt2ArenaEvent,
  Iwt2ArenaIndicator,
  Iwt2ArenaState,
  Iwt2BossEntityState,
  Iwt2MechanicLaneState,
  Iwt2PartyEntityState,
} from './types'
import {
  applyPartyDamageInShape,
  createLaneIndicator,
  indicatorPhaseFromAttack,
} from './mechanics'
import {
  clamp,
  clampVec2ToArena,
  distanceVec2,
  moveToward,
  scaleVec2,
  subtractVec2,
  withFallbackFacing,
} from './vector'

export function tickGreatJaggi(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),
    phaseSecondsRemaining: Math.max(0, state.boss.phaseSecondsRemaining - dt),
    velocity: { x: 0, y: 0 },
  }
  const target = getBossTarget(party)

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

  if (boss.attackPhase === 'packHowlWindup') {
    boss = {
      ...boss,
      facing: withFallbackFacing(subtractVec2(target.position, boss.position), boss.facing),
    }
    if (boss.phaseSecondsRemaining <= 0) {
      const result = applyPackLanes(party, boss, state.time + dt)
      party = result.party
      events.push(...result.events)
      boss = {
        ...boss,
        attackPhase: 'packHowlRecover',
        phaseSecondsRemaining: 0.45,
      }
    }
    return withGreatJaggiIndicators({ boss, party, events })
  }

  if (boss.attackPhase === 'packHowlRecover') {
    if (boss.phaseSecondsRemaining <= 0) {
      boss = {
        ...boss,
        attackPhase: 'idle',
        mechanicLanes: [],
        phaseSecondsRemaining: 0,
      }
    }
    return withGreatJaggiIndicators({ boss, party, events })
  }

  if (boss.chargeCooldownRemaining <= 0) {
    boss = {
      ...boss,
      attackPhase: 'packHowlWindup',
      chargeCooldownRemaining: GREAT_JAGGI_BOSS_METADATA.packHowlCooldown!,
      mechanicLanes: createPackLanes(state, party),
      phaseSecondsRemaining: GREAT_JAGGI_BOSS_METADATA.packHowlWindup!,
      velocity: { x: 0, y: 0 },
    }
    return withGreatJaggiIndicators({ 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 withGreatJaggiIndicators({ boss, party, events })

  const nextPosition = clampVec2ToArena(
    moveToward(boss.position, target.position, GREAT_JAGGI_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 withGreatJaggiIndicators({ boss, party, events })
}

function createPackLanes(state: Iwt2ArenaState, party: Iwt2PartyEntityState[]): Iwt2MechanicLaneState[] {
  const living = party.filter((member) => member.health > 0)
  const count = GREAT_JAGGI_BOSS_METADATA.packLaneCount!
  const lanes: Iwt2MechanicLaneState[] = []
  for (let index = 0; index < count; index += 1) {
    const target = living[(index * 2) % Math.max(1, living.length)]
    const baseY = target
      ? target.position.y
      : state.bounds.height * ((index + 1) / (count + 1))
    const slope = index % 2 === 0 ? 54 : -54
    const y = clamp(baseY + (index - 1) * 28, 54, state.bounds.height - 54)
    lanes.push({
      id: `pack-lane-${index}`,
      start: { x: -28, y: clamp(y - slope, 36, state.bounds.height - 36) },
      end: { x: state.bounds.width + 28, y: clamp(y + slope, 36, state.bounds.height - 36) },
      width: GREAT_JAGGI_BOSS_METADATA.packLaneWidth!,
    })
  }
  return lanes
}

function applyPackLanes(
  party: Iwt2PartyEntityState[],
  boss: Iwt2BossEntityState,
  time: number,
): { party: Iwt2PartyEntityState[], events: Iwt2ArenaEvent[] } {
  let nextParty = party
  const events: Iwt2ArenaEvent[] = []
  const hitEntityIds: string[] = []
  for (const lane of boss.mechanicLanes) {
    const result = applyPartyDamageInShape(nextParty, {
      kind: 'lane',
      start: lane.start,
      end: lane.end,
      width: lane.width,
    }, {
      damage: GREAT_JAGGI_BOSS_METADATA.packLaneDamage!,
      excludedEntityIds: hitEntityIds,
      knockdownSeconds: 0,
      sourceId: boss.id,
      stunSeconds: GREAT_JAGGI_BOSS_METADATA.packLaneStunSeconds!,
      time,
    })
    nextParty = result.party
    hitEntityIds.push(...result.hitEntityIds)
    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) > GREAT_JAGGI_BOSS_METADATA.meleeRange + target.radius) {
    return { boss, party, events: [] }
  }
  const result = applyPartyDamageInShape(party, {
    kind: 'circle',
    position: boss.position,
    radius: GREAT_JAGGI_BOSS_METADATA.meleeRange,
  }, {
    damage: GREAT_JAGGI_BOSS_METADATA.meleeDamage,
    sourceId: boss.id,
    time,
  })
  return {
    boss: { ...boss, meleeCooldownRemaining: GREAT_JAGGI_BOSS_METADATA.meleeCooldown },
    party: result.party,
    events: result.events,
  }
}

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

function createGreatJaggiIndicators(boss: Iwt2BossEntityState): Iwt2ArenaIndicator[] {
  if (
    boss.attackPhase !== 'packHowlWindup'
    && boss.attackPhase !== 'packHowlRecover'
  ) {
    return []
  }
  return boss.mechanicLanes.map((lane) => createLaneIndicator({
    color: boss.attackPhase === 'packHowlRecover' ? '#f05b4f' : '#b8f0aa',
    end: lane.end,
    id: `${boss.id}:${lane.id}`,
    mechanicId: 'great-jaggi-pack-lane',
    phase: indicatorPhaseFromAttack(
      boss.attackPhase === 'packHowlWindup',
      boss.attackPhase === 'packHowlRecover',
    ),
    sourceId: boss.id,
    start: lane.start,
    width: lane.width,
  }))
}
