import { createSaphoTruthOrder, passSaphoTruthOrder, quoteSaphoTruthOrder, reorderSaphoTruthOrder, SaphoTruthOrderError, validateSaphoTruthOrder, type SaphoTruthOrderFrame, type SaphoTruthOrderMode, type SaphoTruthOrderRequest, type SaphoTruthOrderResult, } from './sapho-truth-order'; /** Public owner identities only: no card, token face or hidden choice is stored. */ export type SaphoArrivalOrder = { frame: SaphoTruthOrderFrame; queue: [{ player: string }, { player: string }]; }; export type SaphoArrivalOrderResult = { order: SaphoArrivalOrder; done: boolean }; type Entry = { player: string }; function check(condition: unknown, message: string): asserts condition { if (!condition) throw new SaphoTruthOrderError(message); } /** The original pair is already in provisional storm order. */ export function createSaphoArrivalOrder(event: string, firstOwner: string, secondOwner: string): SaphoArrivalOrder { const queue: SaphoArrivalOrder['queue'] = [{ player: firstOwner }, { player: secondOwner }]; const frame = createSaphoTruthOrder(queue, event); check(frame && frame.holders.length === 2, 'Arrival ordering requires two distinct original owners.'); return { frame, queue }; } function validateShape(order: SaphoArrivalOrder): void { check(order && typeof order === 'object' && !Array.isArray(order) && Array.isArray(order.queue) && order.queue.length === 2 && order.frame && Array.isArray(order.frame.holders) && order.frame.holders.length === 2, 'Retain the two original arrival owners and ordering frame.'); validateSaphoTruthOrder(order.frame, order.queue); check(order.queue[0].player === order.frame.holders[0] || order.frame.protectedFirst === order.queue[0].player || order.frame.protectedLast === order.queue[1].player, 'The reordered arrival must retain its original Sapho choice.'); } /** Match a saved frame to the unchanged public parent, not a caller-supplied * card or a newly sampled token. The queue may differ from original order only * through a recorded Sapho choice. */ export function validateSaphoArrivalOrder( order: SaphoArrivalOrder, event: string, originalOwners: readonly string[], selectedFirst: string, ): void { validateShape(order); check(order.frame.event === event && Array.isArray(originalOwners) && originalOwners.length === 2 && order.frame.holders[0] === originalOwners[0] && order.frame.holders[1] === originalOwners[1] && order.queue[0].player === selectedFirst, 'The arrival ordering no longer matches its original owners, event or selected first reaction.'); } export function quoteSaphoArrivalOrder(order: SaphoArrivalOrder, player: string) { validateShape(order); return quoteSaphoTruthOrder(order.frame, order.queue, player); } function detach(result: SaphoTruthOrderResult): SaphoArrivalOrderResult { check(result.queue.length === 2, 'Retain both arrival owners.'); return { order: { frame: { ...result.frame, holders: [...result.frame.holders], passed: [...result.frame.passed] }, queue: [{ player: result.queue[0].player }, { player: result.queue[1].player }], }, done: result.done, }; } /** Passing and using Sapho both retain the complete public receipt, even after * the last owner has responded, so later continuation validates the order. */ export function passSaphoArrivalOrder(order: SaphoArrivalOrder, request: SaphoTruthOrderRequest): SaphoArrivalOrderResult { validateShape(order); return detach(passSaphoTruthOrder(order.frame, order.queue, request)); } export function reorderSaphoArrivalOrder( order: SaphoArrivalOrder, request: SaphoTruthOrderRequest & { mode: SaphoTruthOrderMode }, ): SaphoArrivalOrderResult { validateShape(order); return detach(reorderSaphoTruthOrder(order.frame, order.queue, request)); }