export type MemberId = "aelia" | "brann" | "nia" | "orin" | "vale";
export type AbilitySlotId = "ability1" | "ability2" | "ability3" | "ability4" | "ability5" | "ability6";
export type HealerAbilityId =
  | "priest-mend"
  | "priest-renew"
  | "priest-aegis-shield"
  | "priest-purify"
  | "priest-radiance"
  | "priest-barrier"
  | "druid-regrowth"
  | "druid-rejuvenation"
  | "druid-lifebloom"
  | "druid-natures-cure"
  | "druid-wild-growth"
  | "druid-flourish"
  | "shaman-healing-wave"
  | "shaman-riptide"
  | "shaman-earth-shield"
  | "shaman-cleanse-spirit"
  | "shaman-chain-heal"
  | "shaman-spirit-link"
  | "paladin-holy-light"
  | "paladin-crusader-strike"
  | "paladin-beacon-of-light"
  | "paladin-cleanse-light"
  | "paladin-word-of-glory"
  | "paladin-avenging-crusader"
  | "chronomancer-mend-timeline"
  | "chronomancer-time-anchor"
  | "chronomancer-echo-of-tomorrow"
  | "chronomancer-erase-affliction"
  | "chronomancer-accelerate"
  | "chronomancer-time-loop";
export type AbilityLoadout = Partial<Record<AbilitySlotId, HealerAbilityId>>;
export type BossId =
  | "bulldrome"
  | "sandglass-scorpion"
  | "cragclaw-crab"
  | "mournveil-ghost"
  | "crownshard-golem"
  | "crystal-bat-matriarch"
  | "stormwool-alpaca"
  | "cluckhorn-colossus"
  | "ashwing-demon"
  | "riftclaw-demon"
  | "tempestscale-dragon"
  | "emberfox"
  | "mirelord-frog"
  | "stonebreaker-giant"
  | "glub-sovereign"
  | "scrapking-goblin"
  | "warcaller-orc"
  | "tuskmaw-orc"
  | "broodfang-spider"
  | "silkfang-spider"
  | "thorncrown-stag"
  | "sky-totem"
  | "razorcrest-raptor"
  | "bristlequake-boar"
  | "moonfang-wolf"
  | "frostmaw-yeti"
  | "rimeclaw-yeti"
  | "gravehorn-triceratops";
export type BossMechanicId =
  | "basic-melee"
  | "bull-charge"
  | "crushing-pounce"
  | "cinder-nova"
  | "ember-brand"
  | "binding-web"
  | "venom-purge"
  | "storm-breath"
  | "stormfall"
  | "elemental-beam"
  | "guardian-cross"
  | "destruction-rush"
  | "ruin-quake"
  | "destruction-pulse"
  | "ricochet-rush"
  | "meteor-slam"
  | "burrow-rush"
  | "hourglass-eruption"
  | "sidewinder-rush"
  | "crushing-tide"
  | "vine-scissors"
  | "haunting-rifts"
  | "tri-burst"
  | "ultimate-skyfall"
  | "meteor-spread"
  | "hollow-collapse"
  | "aetheric-soak"
  | "prism-beam"
  | "memory-sequence"
  | "soul-siphon";
export type BossAnimationCue = "idle" | "move" | "attack" | "special";
export type GamePhase = "briefing" | "combat" | "intermission" | "victory" | "defeat";
export type RunMode = "encounter" | "roguelike" | "rpg-roguelike" | "rogue-trials" | "hockey-healing" | "hockey-healing-pvp" | "blockbreaker" | "aether-assault";
export type GameplayActivity = "boss" | "hockey-healing" | "hockey-healing-pvp" | "blockbreaker" | "aether-assault";
export type RunBuffId =
  | "mend-echo"
  | "mend-efficiency"
  | "mend-cast-speed"
  | "renew-spread"
  | "renew-duration"
  | "renew-potency"
  | "shield-echo"
  | "shield-potency"
  | "shield-guard"
  | "purify-renew"
  | "purify-shield"
  | "purify-chain"
  | "radiance-cooldown"
  | "radiance-renew"
  | "radiance-shield"
  | "barrier-cooldown"
  | "barrier-duration"
  | "barrier-regen";
export type RunBuffRanks = Partial<Record<RunBuffId, number>>;
export type BottomTab = "combat" | "map" | "pack" | "pvp";
export type HealerPulseKind = "direct-heal" | "periodic-heal" | "protective" | "cleanse" | "group-heal" | "field";
export type PulseKind = HealerPulseKind | "boss" | "debuff" | "charge" | "pounce" | "tether" | "venom" | "breath" | "skyfall" | "slash";
export type BossMotionMode =
  | "holding"
  | "telegraph"
  | "charging"
  | "stacking"
  | "pouncing"
  | "tethering"
  | "venom_cast"
  | "breath_telegraph"
  | "breath_sweeping"
  | "skyfall"
  | "mantis_line_telegraph"
  | "mantis_cross_telegraph"
  | "cinderback_slam"
  | "sandglass_hourglass"
  | "golem_shockwave"
  | "golem_crownfall";
export type WorldPosition = [number, number];

export type CircleHazardKind =
  | "venom_pool"
  | "skyfall"
  | "quake"
  | "lava_pool"
  | "stinger_eruption"
  | "hourglass"
  | "tidal_burst"
  | "soul_rift"
  | "crownfall"
  | "royal_shockwave";

export type PoolTelegraphKind = "spread" | "donut" | "soak" | "beam" | "memory" | "soul-siphon";

export type MemorySymbolId = "triangle" | "cross" | "circle" | "square";

export interface MemoryTile {
  symbol: MemorySymbolId;
  center: WorldPosition;
}

/**
 * Short-lived, encounter-agnostic boss mechanic. Individual boss scripts keep
 * ownership of their signature moves; this shape lets an encounter add a
 * balanced mechanic from the shared pool without adding another boss mode.
 */
export interface PoolTelegraph {
  id: string;
  kind: PoolTelegraphKind;
  name: string;
  center: WorldPosition;
  radius: number;
  innerRadius?: number;
  start?: WorldPosition;
  end?: WorldPosition;
  width?: number;
  activatesAt: number;
  expiresAt: number;
  damage: number;
  totalDamage?: number;
  minimumParticipants?: number;
  targetId?: MemberId;
  /** Healer-only Simon Says state. Present only for the Memory Sequence pool mechanic. */
  sequence?: MemorySymbolId[];
  tiles?: MemoryTile[];
  inputStartsAt?: number;
  inputIndex?: number;
  lastHealerTileId?: MemorySymbolId;
  soulSiphon?: SoulSiphonState;
  resolved: boolean;
  hitIds: MemberId[];
}

export interface CircleHazard {
  id: string;
  kind: CircleHazardKind;
  center: WorldPosition;
  innerRadius?: number;
  radius: number;
  activatesAt: number;
  expiresAt: number;
  damage: number;
  tickInterval?: number;
  nextDamageAt: Partial<Record<MemberId, number>>;
  resolved: boolean;
  hitIds: MemberId[];
}

export interface SlashLane {
  id: string;
  start: WorldPosition;
  end: WorldPosition;
  width: number;
  damage: number;
}

/**
 * Healer-only movement objective. The encounter owns the rules; renderers use
 * this compact state to show the pursuing shade and its cleansing destination.
 */
export interface SoulSiphonState {
  targetId: "aelia";
  ghostPosition: WorldPosition;
  wardPosition: WorldPosition;
  wardRadius: number;
  nextDamageAt: number;
  tickCount: number;
}

export interface Debuff {
  id: string;
  name: string;
  expiresAt: number;
  nextTickAt: number;
  tickDamage: number;
  sourceBossId?: BossId;
}

export type HealingEffectId = "renew" | "regrowth" | "rejuvenation" | "lifebloom" | "wild-growth" | "riptide" | "temporal-echo";

export interface HealingOverTimeEffect {
  id: HealingEffectId;
  expiresAt: number;
  nextTickAt: number;
  tickInterval: number;
  healingPerTick: number;
  stacks: number;
  expirationHealingPerStack?: number;
}

export interface ReactiveHealEffect {
  id: "earth-shield";
  charges: number;
  expiresAt: number;
  nextTriggerAt: number;
  healingPerTrigger: number;
}

export interface PartyMember {
  id: MemberId;
  name: string;
  className: string;
  role: "Healer" | "Tank" | "Damage";
  color: string;
  maxHp: number;
  hp: number;
  absorb: number;
  healingEffects: HealingOverTimeEffect[];
  reactiveHeal: ReactiveHealEffect | null;
  knockedUntil: number;
  debuffs: Debuff[];
  /** Optional serializable projection supplied by modular run modes. */
  runProfile?: {
    instanceId: string;
    combatProfileId: string;
    combatKitId: PartyCombatKitId;
    tier: string;
    visualArchetype: "knight" | "ranger" | "mage" | "rogue" | "druid";
  };
}

/** Renderer-independent companion behavior family; run modes may map any archetype to one. */
export type PartyCombatKitId = "tank" | "ranged" | "caster" | "melee";

export interface BossState {
  id: BossId;
  name: string;
  maxHp: number;
  hp: number;
  nextMeleeAt: number;
  /** Simulation time when health first reached zero. Used for delayed endless replacement. */
  defeatedAt?: number;
}

export interface BossMotionState {
  bossId: BossId;
  activeMechanicId: BossMechanicId | null;
  formationOffsetX: number;
  mode: BossMotionMode;
  position: WorldPosition;
  chargeStart: WorldPosition;
  chargeEnd: WorldPosition;
  chargeTargetId: MemberId;
  chargeHitIds: MemberId[];
  phaseEndsAt: number;
  chargeCount: number;
  pounceTargetId: MemberId;
  pounceCenter: WorldPosition;
  nextMechanicAt: number;
  mechanicCount: number;
  phaseStartedAt: number;
  /** Simulation time of latest basic melee impact, or -1 before first impact. */
  lastMeleeAt: number;
  mechanicHitIds: MemberId[];
  mechanicNextDamageAt: Partial<Record<MemberId, number>>;
  tetherIds: MemberId[];
  tetherBreakDistance: number;
  breathAngle: number;
  breathStartAngle: number;
  breathEndAngle: number;
  hazards: CircleHazard[];
  slashLanes: SlashLane[];
  poolMechanicCount: number;
  poolTelegraphs: PoolTelegraph[];
}

export interface AbilityDefinition {
  id: HealerAbilityId;
  slot: AbilitySlotId;
  pulseKind: HealerPulseKind;
  name: string;
  shortName: string;
  key: string;
  gamepad: string;
  cooldown: number;
  castTime?: number;
  mana: number;
  icon: string;
  description: string;
  targeting: "enemy" | "ally" | "party";
  color: string;
}

export interface ActiveCast {
  slotId: AbilitySlotId;
  abilityId: HealerAbilityId;
  targetId: MemberId;
  startedAt: number;
  completesAt: number;
  resourceSpent: number;
}

export interface BarrierState {
  kind: "barrier" | "spirit-link" | null;
  center: WorldPosition;
  expiresAt: number;
  nextHealAt: number;
}

export interface HealerMechanicState {
  resource: number;
  maxResource: number;
  flourishExpiresAt: number;
  nextPulseAt: number;
  beaconTargetId: MemberId | null;
  beaconExpiresAt: number;
  avengingCrusaderExpiresAt: number;
  temporalAnchor: {
    targetId: MemberId;
    hp: number;
    expiresAt: number;
  } | null;
  timeLoop: {
    restoresAt: number;
    partyHp: Record<MemberId, number>;
  } | null;
}

export interface ScenePulse {
  id: number;
  kind: PulseKind;
  targetId?: MemberId;
}

export interface InventoryItem {
  id: string;
  name: string;
  slot: string;
  rarity: "Common" | "Uncommon" | "Rare";
  icon: string;
  stats: string[];
  effect: string;
  equipped: boolean;
}

export type HealerClassId = "priest" | "druid" | "shaman" | "paladin" | "chronomancer";

export interface HealerClassDefinition {
  id: HealerClassId;
  name: string;
  specialization: string;
  icon: string;
  color: string;
  resourceName: string;
  secondaryResourceName?: string;
  description: string;
  abilities: Record<AbilitySlotId, AbilityDefinition>;
}
