import type { BossArchetype } from "./bossCatalog";
import type { BossAnimationCue, BossMotionMode } from "./types";

export type BossAnimationState = BossAnimationCue | "melee" | "hit" | "death";

export interface BossAnimationClips {
  idle: string;
  move: string;
  attack: string;
  special: string;
  melee: string;
  hit?: string;
  death: string;
}

export interface BossProceduralPose {
  x: number;
  y: number;
  z: number;
  pitch: number;
  yaw: number;
  roll: number;
  scaleX: number;
  scaleY: number;
  scaleZ: number;
}

export const BOSS_MELEE_ANIMATION_SECONDS = 0.62;
export const BOSS_HIT_ANIMATION_SECONDS = 0.42;
export const BOSS_HIT_REACTION_COOLDOWN_SECONDS = 0.55;

export function bossAnimationClipName(clips: BossAnimationClips, state: BossAnimationState) {
  return state === "hit" ? clips.hit ?? clips.idle : clips[state];
}

export function isBossAnimationOneShot(state: BossAnimationState) {
  return state === "attack"
    || state === "special"
    || state === "melee"
    || state === "hit"
    || state === "death";
}

export function shouldStartBossAnimation(
  activeState: BossAnimationState | undefined,
  activeTrigger: number,
  nextState: BossAnimationState,
  nextTrigger: number,
) {
  if (activeState !== nextState) return true;
  if (nextState === "death" || !isBossAnimationOneShot(nextState)) return false;
  return activeTrigger !== nextTrigger;
}

export function selectBossAnimationState({
  defeated,
  activeMechanic,
  mechanicCue,
  meleeElapsed,
  hitElapsed,
}: {
  defeated: boolean;
  activeMechanic: boolean;
  mechanicCue: BossAnimationCue;
  meleeElapsed: number;
  hitElapsed: number;
}): BossAnimationState {
  if (defeated) return "death";
  if (activeMechanic) return mechanicCue;
  if (meleeElapsed < BOSS_MELEE_ANIMATION_SECONDS) return "melee";
  if (hitElapsed < BOSS_HIT_ANIMATION_SECONDS) return "hit";
  return mechanicCue;
}

export function bossAnimationTrigger(
  state: BossAnimationState,
  phaseStartedAt: number,
  lastMeleeAt: number,
  hitTrigger: number,
) {
  if (state === "attack" || state === "special") return phaseStartedAt;
  if (state === "melee") return lastMeleeAt;
  if (state === "hit") return hitTrigger;
  return 0;
}

function pulse(elapsed: number, duration: number) {
  if (elapsed < 0 || elapsed >= duration) return 0;
  return Math.sin((elapsed / duration) * Math.PI);
}

function normalizedPhase(elapsed: number, duration: number) {
  return Math.max(0, Math.min(1, elapsed / Math.max(0.001, duration)));
}

export function resetBossProceduralPose(pose: BossProceduralPose) {
  pose.x = 0;
  pose.y = 0;
  pose.z = 0;
  pose.pitch = 0;
  pose.yaw = 0;
  pose.roll = 0;
  pose.scaleX = 1;
  pose.scaleY = 1;
  pose.scaleZ = 1;
}

/**
 * Adds readable root motion where source GLBs only provide generic combat clips.
 * Caller owns and reuses `pose`; this function allocates nothing in render loops.
 */
export function writeBossProceduralPose(
  pose: BossProceduralPose,
  archetype: BossArchetype,
  mode: BossMotionMode,
  animationState: BossAnimationState,
  stateElapsed: number,
  phaseDuration: number,
) {
  resetBossProceduralPose(pose);

  if (animationState === "melee") {
    const amount = pulse(stateElapsed, BOSS_MELEE_ANIMATION_SECONDS);
    pose.z = amount * 0.34;
    pose.pitch = -amount * 0.09;
    pose.scaleZ = 1 + amount * 0.035;
    return;
  }

  if (animationState === "hit") {
    const amount = pulse(stateElapsed, BOSS_HIT_ANIMATION_SECONDS);
    pose.z = -amount * 0.2;
    pose.pitch = amount * 0.08;
    pose.roll = Math.sin((stateElapsed / BOSS_HIT_ANIMATION_SECONDS) * Math.PI * 2) * 0.045;
    return;
  }

  const progress = normalizedPhase(stateElapsed, phaseDuration);
  const windup = Math.sin(progress * Math.PI);

  if (archetype === "sky-sweeper" && (mode === "breath_telegraph" || mode === "breath_sweeping")) {
    pose.y = windup * 0.24;
    pose.pitch = -windup * 0.18;
    pose.roll = mode === "breath_sweeping" ? Math.sin(progress * Math.PI * 2) * 0.07 : 0;
    pose.scaleX = 1 + windup * 0.045;
    pose.scaleZ = 1 + windup * 0.045;
    return;
  }

  if (archetype === "golem" && mode === "golem_shockwave") {
    pose.y = -windup * 0.1;
    pose.scaleX = 1 + windup * 0.08;
    pose.scaleY = 1 - windup * 0.13;
    pose.scaleZ = 1 + windup * 0.08;
    return;
  }

  if (archetype === "golem" && (mode === "golem_crownfall" || mode === "skyfall")) {
    pose.y = windup * 0.3;
    pose.pitch = -windup * 0.08;
    pose.scaleX = 1 + windup * 0.04;
    pose.scaleZ = 1 + windup * 0.04;
    return;
  }

  if ((archetype === "ghost" || archetype === "web-caster")
    && (mode === "tethering" || mode === "venom_cast" || mode === "golem_crownfall")) {
    pose.y = windup * 0.18;
    pose.yaw = Math.sin(progress * Math.PI * 2) * 0.12;
    pose.scaleX = 1 + windup * 0.045;
    pose.scaleY = 1 + windup * 0.045;
    pose.scaleZ = 1 + windup * 0.045;
  }
}
