import type {
  Iwt2ArenaEvent,
  Iwt2ArenaIndicator,
  Iwt2EntityId,
  Iwt2GroundHazardState,
  Iwt2PartyEntityState,
  Iwt2Vec2,
} from './types'
import {
  applyPartyDamageInShape,
  createCircleIndicator,
} from './mechanics'

const MAX_FIRE_PUDDLES = 8
const FIRE_PUDDLE_TICK_SECONDS = 0.55
const FIRE_PUDDLE_FADE_SECONDS = 0.8
const FIRE_PUDDLE_MERGE_DISTANCE = 28

export function addFirePuddle({
  damage,
  duration,
  hazards,
  nextHazardId,
  position,
  radius,
  sourceId,
  time,
}: {
  damage: number
  duration: number
  hazards: Iwt2GroundHazardState[]
  nextHazardId: number
  position: Iwt2Vec2
  radius: number
  sourceId: Iwt2EntityId
  time: number
}): { hazards: Iwt2GroundHazardState[], nextHazardId: number } {
  const overlappingIndex = hazards.findIndex((hazard) => (
    hazard.hazardKind === 'firePuddle'
    && hazard.remainingSeconds > hazard.fadeSeconds
    && Math.hypot(hazard.position.x - position.x, hazard.position.y - position.y) <= FIRE_PUDDLE_MERGE_DISTANCE
  ))
  if (overlappingIndex >= 0) {
    return {
      hazards: hazards.map((hazard, index) => index === overlappingIndex
        ? {
          ...hazard,
          damage: Math.max(hazard.damage, damage),
          position: {
            x: (hazard.position.x + position.x) / 2,
            y: (hazard.position.y + position.y) / 2,
          },
          radius: Math.max(hazard.radius, radius),
          remainingSeconds: Math.max(hazard.remainingSeconds, duration),
        }
        : hazard),
      nextHazardId,
    }
  }

  const nextHazard: Iwt2GroundHazardState = {
    id: `fire-puddle-${nextHazardId}`,
    kind: 'groundHazard',
    hazardKind: 'firePuddle',
    sourceId,
    position: { ...position },
    radius,
    damage,
    remainingSeconds: duration,
    fadeSeconds: FIRE_PUDDLE_FADE_SECONDS,
    nextDamageAt: time + 0.12,
  }
  return {
    hazards: capFirePuddles([...hazards, nextHazard]),
    nextHazardId: nextHazardId + 1,
  }
}

export function tickGroundHazards({
  dt,
  hazards,
  party,
  time,
}: {
  dt: number
  hazards: Iwt2GroundHazardState[]
  party: Iwt2PartyEntityState[]
  time: number
}): {
  hazards: Iwt2GroundHazardState[]
  party: Iwt2PartyEntityState[]
  events: Iwt2ArenaEvent[]
} {
  const events: Iwt2ArenaEvent[] = []
  let nextParty = party
  const nextHazards: Iwt2GroundHazardState[] = []

  for (const hazard of hazards) {
    const remainingSeconds = hazard.remainingSeconds - dt
    if (remainingSeconds <= 0) continue

    let nextDamageAt = hazard.nextDamageAt
    if (time >= hazard.nextDamageAt) {
      const result = applyPartyDamageInShape(
        nextParty,
        {
          kind: 'circle',
          position: hazard.position,
          radius: hazard.radius,
        },
        {
          damage: hazard.damage,
          sourceId: hazard.sourceId,
          time,
        },
      )
      nextParty = result.party
      events.push(...result.events)
      nextDamageAt = time + FIRE_PUDDLE_TICK_SECONDS
    }

    nextHazards.push({
      ...hazard,
      nextDamageAt,
      remainingSeconds,
    })
  }

  return {
    events,
    hazards: nextHazards,
    party: nextParty,
  }
}

export function createHazardIndicators(hazards: Iwt2GroundHazardState[]): Iwt2ArenaIndicator[] {
  return hazards.map((hazard) => createCircleIndicator({
    color: '#ff7a2f',
    id: `${hazard.id}:indicator`,
    mechanicId: 'fire-puddle',
    phase: hazard.remainingSeconds <= hazard.fadeSeconds ? 'recover' : 'active',
    position: hazard.position,
    radius: hazard.radius,
    sourceId: hazard.sourceId,
  }))
}

function capFirePuddles(hazards: Iwt2GroundHazardState[]): Iwt2GroundHazardState[] {
  let activePuddles = 0
  let oldestActivePuddleIndex = -1
  for (let index = 0; index < hazards.length; index += 1) {
    const hazard = hazards[index]
    if (hazard.hazardKind !== 'firePuddle' || hazard.remainingSeconds <= hazard.fadeSeconds) continue
    activePuddles += 1
    if (oldestActivePuddleIndex < 0) oldestActivePuddleIndex = index
  }
  if (activePuddles <= MAX_FIRE_PUDDLES || oldestActivePuddleIndex < 0) return hazards

  return hazards.map((hazard, index) => index === oldestActivePuddleIndex
    ? { ...hazard, remainingSeconds: Math.min(hazard.remainingSeconds, hazard.fadeSeconds) }
    : hazard)
}
