import type {
  Iwt2ArenaBounds,
  Iwt2ArenaCircleIndicator,
  Iwt2ArenaConeIndicator,
  Iwt2ArenaDonutIndicator,
  Iwt2ArenaEvent,
  Iwt2ArenaEventType,
  Iwt2ArenaIndicator,
  Iwt2ArenaIndicatorPhase,
  Iwt2ArenaLaneIndicator,
  Iwt2EntityId,
  Iwt2PartyEntityState,
  Iwt2Vec2,
} from './types'
import { circleIntersectsSegment } from './collision'
import {
  addVec2,
  clampVec2ToArena,
  distanceVec2,
  normalizeVec2,
  scaleVec2,
  subtractVec2,
} from './vector'

export type Iwt2LinearMovementPlan = {
  direction: Iwt2Vec2
  end: Iwt2Vec2
}

export type Iwt2PartyDamageEffect = {
  sourceId: Iwt2EntityId
  time: number
  damage: number
  damageEventType?: Iwt2ArenaEventType
  stunSeconds?: number
  knockdownSeconds?: number
  statusEventType?: Iwt2ArenaEventType
  excludedEntityIds?: Iwt2EntityId[]
}

export type Iwt2LaneHitShape = {
  kind: 'lane'
  start: Iwt2Vec2
  end: Iwt2Vec2
  width: number
}

export type Iwt2CircleHitShape = {
  kind: 'circle'
  position: Iwt2Vec2
  radius: number
}

export type Iwt2DonutHitShape = {
  kind: 'donut'
  position: Iwt2Vec2
  innerRadius: number
  outerRadius: number
}

export type Iwt2HitShape = Iwt2LaneHitShape | Iwt2CircleHitShape | Iwt2DonutHitShape

export function createArenaEvent(
  id: number,
  time: number,
  type: Iwt2ArenaEventType,
  sourceId: Iwt2EntityId,
  targetId?: Iwt2EntityId,
  value?: number,
): Iwt2ArenaEvent {
  return { id, time, type, sourceId, targetId, value }
}

export function createLinearMovementPlan({
  bounds,
  from,
  length,
  radius,
  target,
}: {
  bounds: Iwt2ArenaBounds
  from: Iwt2Vec2
  length: number
  radius: number
  target: Iwt2Vec2
}): Iwt2LinearMovementPlan {
  const direction = normalizeVec2(subtractVec2(target, from))
  const rawEnd = addVec2(from, scaleVec2(direction, length))
  return {
    direction,
    end: clampVec2ToArena(rawEnd, radius, bounds),
  }
}

export function applyPartyDamageInShape(
  party: Iwt2PartyEntityState[],
  shape: Iwt2HitShape,
  effect: Iwt2PartyDamageEffect,
): {
  party: Iwt2PartyEntityState[]
  hitEntityIds: Iwt2EntityId[]
  events: Iwt2ArenaEvent[]
} {
  const events: Iwt2ArenaEvent[] = []
  const excluded = new Set(effect.excludedEntityIds ?? [])
  const hitEntityIds: Iwt2EntityId[] = []
  const stunSeconds = effect.stunSeconds ?? 0
  const knockdownSeconds = effect.knockdownSeconds ?? stunSeconds
  const nextParty = party.map((member) => {
    if (member.health <= 0 || excluded.has(member.id) || !partyMemberIntersectsShape(member, shape)) {
      return member
    }

    hitEntityIds.push(member.id)
    const shieldAbsorbed = Math.min(member.shield, effect.damage)
    const healthDamage = effect.damage - shieldAbsorbed
    const nextShield = member.shield - shieldAbsorbed
    const nextHealth = Math.max(0, member.health - healthDamage)
    events.push(createArenaEvent(
      0,
      effect.time,
      effect.damageEventType ?? 'partyDamaged',
      effect.sourceId,
      member.id,
      effect.damage,
    ))
    if (stunSeconds > 0 || knockdownSeconds > 0) {
      events.push(createArenaEvent(
        0,
        effect.time,
        effect.statusEventType ?? 'partyStunned',
        effect.sourceId,
        member.id,
        Math.max(stunSeconds, knockdownSeconds),
      ))
    }
    if (member.health > 0 && nextHealth <= 0) {
      events.push(createArenaEvent(0, effect.time, 'entityDefeated', effect.sourceId, member.id))
    }

    return {
      ...member,
      health: nextHealth,
      shield: nextShield,
      status: {
        ...member.status,
        stunnedSeconds: Math.max(member.status.stunnedSeconds, stunSeconds),
        knockedDownSeconds: Math.max(member.status.knockedDownSeconds, knockdownSeconds),
      },
    }
  })

  return { party: nextParty, hitEntityIds, events }
}

export function partyMemberIntersectsShape(member: Iwt2PartyEntityState, shape: Iwt2HitShape): boolean {
  if (shape.kind === 'lane') {
    return circleIntersectsSegment(
      { position: member.position, radius: member.radius },
      shape.start,
      shape.end,
      shape.width,
    )
  }
  const distance = distanceVec2(member.position, shape.position)
  if (shape.kind === 'circle') return distance <= shape.radius + member.radius
  return distance <= shape.outerRadius + member.radius && distance >= Math.max(0, shape.innerRadius - member.radius)
}

export function createLaneIndicator({
  color,
  end,
  id,
  mechanicId,
  phase,
  sourceId,
  start,
  width,
}: {
  color: string
  end: Iwt2Vec2
  id: string
  mechanicId: string
  phase: Iwt2ArenaIndicatorPhase
  sourceId: Iwt2EntityId
  start: Iwt2Vec2
  width: number
}): Iwt2ArenaLaneIndicator {
  return {
    color,
    end: { ...end },
    fillAlpha: phase === 'active' ? 0.18 : 0.11,
    id,
    kind: 'lane',
    lineAlpha: phase === 'active' ? 0.8 : 0.45,
    mechanicId,
    phase,
    sourceId,
    start: { ...start },
    width,
  }
}

export function createCircleIndicator({
  color,
  id,
  mechanicId,
  phase,
  position,
  radius,
  sourceId,
}: {
  color: string
  id: string
  mechanicId: string
  phase: Iwt2ArenaIndicatorPhase
  position: Iwt2Vec2
  radius: number
  sourceId: Iwt2EntityId
}): Iwt2ArenaCircleIndicator {
  return {
    color,
    fillAlpha: phase === 'active' ? 0.18 : 0.12,
    id,
    kind: 'circle',
    lineAlpha: phase === 'active' ? 0.75 : 0.55,
    mechanicId,
    phase,
    position: { ...position },
    radius,
    sourceId,
  }
}

export function createConeIndicator({
  angleRadians,
  color,
  direction,
  id,
  mechanicId,
  origin,
  phase,
  range,
  sourceId,
}: {
  angleRadians: number
  color: string
  direction: Iwt2Vec2
  id: string
  mechanicId: string
  origin: Iwt2Vec2
  phase: Iwt2ArenaIndicatorPhase
  range: number
  sourceId: Iwt2EntityId
}): Iwt2ArenaConeIndicator {
  return {
    angleRadians,
    color,
    direction: normalizeVec2(direction),
    fillAlpha: phase === 'active' ? 0.18 : 0.1,
    id,
    kind: 'cone',
    lineAlpha: phase === 'active' ? 0.72 : 0.48,
    mechanicId,
    origin: { ...origin },
    phase,
    range,
    sourceId,
  }
}

export function createDonutIndicator({
  color,
  id,
  innerRadius,
  mechanicId,
  outerRadius,
  phase,
  position,
  sourceId,
}: {
  color: string
  id: string
  innerRadius: number
  mechanicId: string
  outerRadius: number
  phase: Iwt2ArenaIndicatorPhase
  position: Iwt2Vec2
  sourceId: Iwt2EntityId
}): Iwt2ArenaDonutIndicator {
  return {
    color,
    fillAlpha: phase === 'active' ? 0.14 : 0.08,
    id,
    innerRadius,
    kind: 'donut',
    lineAlpha: phase === 'active' ? 0.76 : 0.5,
    mechanicId,
    outerRadius,
    phase,
    position: { ...position },
    sourceId,
  }
}

export function indicatorPhaseFromAttack(
  windup: boolean,
  active: boolean,
): Iwt2ArenaIndicatorPhase {
  if (active) return 'active'
  if (windup) return 'windup'
  return 'recover'
}

export function isIndicatorActive(indicator: Iwt2ArenaIndicator): boolean {
  return indicator.phase === 'active'
}
