import type { DomainEvent } from "./events.js"; import { type CombatantId, type DomainError, type Encounter, findCombatant, isDomainError, } from "./types.js"; export interface EditCombatantSuccess { readonly encounter: Encounter; readonly events: DomainEvent[]; } /** * Pure function that renames a combatant in an encounter by ID. * * FR-001: Accepts Encounter, CombatantId, and newName; returns next state + events. * FR-002: Emits a CombatantUpdated event with combatantId, oldName, newName. * FR-004: Rejects empty/whitespace-only names with DomainError. * FR-005: Preserves activeIndex and roundNumber. * FR-006: Preserves combatant list order. */ export function editCombatant( encounter: Encounter, id: CombatantId, newName: string, ): EditCombatantSuccess | DomainError { const trimmed = newName.trim(); if (trimmed === "") { return { kind: "domain-error", code: "invalid-name", message: "Combatant name must not be empty", }; } const found = findCombatant(encounter, id); if (isDomainError(found)) return found; const oldName = found.combatant.name; return { encounter: { combatants: encounter.combatants.map((c) => c.id === id ? { ...c, name: trimmed } : c, ), activeIndex: encounter.activeIndex, roundNumber: encounter.roundNumber, }, events: [ { type: "CombatantUpdated", combatantId: id, oldName, newName: trimmed, }, ], }; }