#!/usr/bin/env node

// src/templates/testing-types-content.ts
var DEFAULT_REJECTION_CODES = [
  "NOT_YOUR_TURN",
  "action-unavailable",
  "invalid-action-params",
  "prompt-not-owned"
];
function renderLiteralUnion(values) {
  if (values.length === 0) {
    return "never";
  }
  return values.map((value) => JSON.stringify(value)).join(" | ");
}
var REDUCER_TESTING_TYPES_WRAPPER_CONTENT = `// Generated by dreamboard \u2014 do not edit by hand.
import game from "../app/game";
import {
  contractFingerprint,
  createReducerBundle,
} from "@dreamboard-games/sdk/reducer";
import { createTestRuntime as createDreamboardTestRuntime } from "@dreamboard-games/sdk/testing";
import type { CreateTestRuntimeOptions } from "@dreamboard-games/sdk/testing";
import { literals } from "../shared/manifest-contract";
import type { PhaseName } from "../shared/generated/ui-contract";
import {
  BASE_STATES,
  BASE_STATES_CONTRACT_FINGERPRINT,
} from "./generated/base-states.generated";
import type {
  BaseDefinition,
  ScenarioDefinition,
  TestRunner,
} from "./generated/testing-contract";

export * from "./generated/testing-contract";

/**
 * Workspace-narrowed \`defineBase\` wrapper. Accepts the generated
 * \`BaseDefinition\` so \`setup({ seat, game })\` is typed against the
 * workspace's player ids and interaction contract.
 */
export function defineBase<const Definition extends BaseDefinition>(
  definition: Definition,
): Definition {
  return definition;
}

/**
 * Workspace-narrowed \`defineScenario\` wrapper. The generated
 * \`ScenarioDefinition\` narrows \`ctx.view(playerId)\` in \`then\` based on
 * the declared \`phase\`, keeps \`when\` union-typed, and constrains
 * \`phase\` / \`stage\` to the manifest-derived literal types.
 */
export function defineScenario<
  const Runners extends readonly TestRunner[] = readonly ["reducer"],
  const Phase extends PhaseName | undefined = undefined,
>(
  definition: ScenarioDefinition<Runners, Phase>,
): ScenarioDefinition<Runners, Phase> {
  return definition;
}

export function createTestRuntime(options: {
  baseId: keyof typeof BASE_STATES & string;
  phase?: PhaseName;
  controllingPlayerId?: (typeof literals.playerIds)[number];
  userId?: string | null;
}) {
  const reducerBundle =
    createReducerBundle(game) satisfies CreateTestRuntimeOptions["bundle"];
  const baseStates =
    BASE_STATES satisfies CreateTestRuntimeOptions["baseStates"];
  const runtime = createDreamboardTestRuntime({
    baseId: options.baseId,
    baseStates,
    bundle: reducerBundle,
    contractFingerprint: contractFingerprint(game).value,
    expectedBaseStateFingerprint: BASE_STATES_CONTRACT_FINGERPRINT,
    phase: options.phase,
    userId: options.userId ?? "test-user",
    playerIds: literals.playerIds.slice(
      0,
      BASE_STATES[options.baseId]?.fingerprint.players ?? literals.playerIds.length,
    ),
  });

  if (options.controllingPlayerId) {
    runtime.setControllingPlayer(options.controllingPlayerId);
  }

  return runtime;
}
`;
function buildReducerTestingContractContent(options = {}) {
  const rejectionCodes = Array.from(
    /* @__PURE__ */ new Set([...options.rejectionCodes ?? [], ...DEFAULT_REJECTION_CODES])
  ).sort((left, right) => left.localeCompare(right));
  return `// Generated by dreamboard \u2014 do not edit by hand.
import type game from "../../app/game";
import { literals, type SetupProfileId } from "../../shared/manifest-contract";
import {
  type GameView,
  type InteractionId,
  type InteractionKey,
  type InteractionParamsOf,
  type PhaseName,
  type StageName as WorkspaceStageName,
} from "../../shared/generated/ui-contract";
import type {
  ExpectFn as SharedExpectFn,
  TestRunner as SharedTestRunner,
} from "@dreamboard-games/sdk/testing";
import type { InteractionDescriptor } from "@dreamboard-games/sdk/runtime";
import { BASE_STATES } from "./base-states.generated";

export type GameDefinition = typeof game;
export type PlayerId = (typeof literals.playerIds)[number];
export type StateName = PhaseName;
export type BaseId = keyof typeof BASE_STATES & string;
export type InteractionDescriptorFor<Id extends string = string> =
  InteractionDescriptor<Id>;
export type InteractionExplanation = {
  interactionId: string;
  phase: string;
  step: string | null;
  availability:
    | "available"
    | "notYourTurn"
    | "wrongPhase"
    | "wrongStep"
    | "blocked";
  rules: ReadonlyArray<{
    ruleId: string;
    outcome: "passed" | "failed" | "notEvaluated";
    errorCode?: string;
    message?: string;
  }>;
  actor: { required: readonly string[]; playerIsActor: boolean };
  inputs: ReadonlyArray<{
    key: string;
    kind: string;
    eligibleCount: number | "lazy";
  }>;
};
export type TestRunner = SharedTestRunner;
export type ExpectFn = SharedExpectFn;
export type KnownRejectionCode = ${renderLiteralUnion(rejectionCodes)};
export type RejectionCode = [KnownRejectionCode] extends [never]
  ? string
  : KnownRejectionCode;

type DefaultRunners = readonly ["reducer"];
type PhaseTaggedView<Phase extends PhaseName> = Extract<
  GameView,
  { phase: Phase } | { currentPhase: Phase } | { state: Phase }
>;
type NarrowedView<Phase extends PhaseName> = [PhaseTaggedView<Phase>] extends [never]
  ? GameView
  : PhaseTaggedView<Phase>;

export type ViewByPhase = {
  [Phase in PhaseName]: NarrowedView<Phase>;
};

type InteractionKeyForId<Id extends InteractionId> = Extract<
  InteractionKey,
  \`\${string}.\${Id}\`
>;
type InteractionParamsForKey<Key extends InteractionKey> =
  Key extends InteractionKey ? InteractionParamsOf<Key> : never;
type InteractionParamsOfId<Id extends InteractionId> =
  InteractionParamsForKey<InteractionKeyForId<Id>>;

export interface BrowserRunnerSnapshot {
  sessionId: string | null;
  shortCode: string | null;
  version: number;
  currentPhase: string | null;
  controllingPlayerId: string;
  controllablePlayerIds: string[];
  view: unknown;
  availableInteractions?: string[];
}

export interface BrowserRunnerBridge {
  snapshot(): Promise<BrowserRunnerSnapshot>;
  submitInteraction(
    playerId: PlayerId,
    interactionId: string,
    params: unknown,
  ): Promise<void>;
}

export interface BrowserRunnerDriver {
  onReady?(bridge: BrowserRunnerBridge): Promise<void> | void;
  interaction?(
    bridge: BrowserRunnerBridge,
    input: { playerId: PlayerId; interactionId: string; params: unknown },
  ): Promise<boolean | void> | boolean | void;
}

export interface ScenarioGameApi {
  start(): Promise<void>;
  /**
   * Patch the reducer snapshot for deterministic setup-heavy scenarios.
   * This is limited to reducer snapshot materialization and is rejected by
   * live replay/browser runners so authored gameplay verification still
   * submits real interactions.
   */
  patchState(mutator: (state: Record<string, unknown>) => void): Promise<void>;
  /**
   * Submit a player interaction (action-kind or prompt-kind) to the game.
   * The \`interactionId\` matches an \`InteractionId\` from the generated
   * \`ui-contract\`; \`params\` is typed per interaction id.
   */
  submit<Id extends InteractionId>(
    playerId: PlayerId,
    interactionId: Id,
    params?: InteractionParamsOfId<Id>,
  ): Promise<void>;
}

export interface BaseContext {
  game: ScenarioGameApi;
  players(): readonly PlayerId[];
  /**
   * Resolve the seat at \`index\` in the base's players list.
   * Throws if the index is out of range. Prefer \`seat(0)\`/\`seat(1)\` over
   * literal player ids so bases stay portable across player counts and
   * we never hard-code wire-shape assumptions like "player-1".
   */
  seat(index: number): PlayerId;
}

export interface SharedScenarioContext {
  game: ScenarioGameApi;
  players(): readonly PlayerId[];
  /**
   * Resolve the seat at \`index\` in the current scenario's players list.
   * Throws if the index is out of range. Prefer \`seat(0)\`/\`seat(1)\` over
   * literal player ids so scenarios stay portable across player counts and
   * we never hard-code wire-shape assumptions like "player-1".
   */
  seat(index: number): PlayerId;
  state(): StateName;
  view(playerId: PlayerId): GameView;
  interactions(playerId: PlayerId): readonly InteractionDescriptorFor[];
  explain(playerId: PlayerId, interactionId: InteractionId): InteractionExplanation;
  expect: ExpectFn;
}

export type ScenarioContext<
  Phase extends PhaseName | undefined = undefined,
> = Omit<SharedScenarioContext, "state" | "view"> & {
  state(): Phase extends PhaseName ? Phase : StateName;
  view(playerId: PlayerId): Phase extends PhaseName ? ViewByPhase[Phase] : GameView;
};

export type ScenarioThenContext<
  _Runners extends readonly TestRunner[] = DefaultRunners,
  Phase extends PhaseName | undefined = undefined,
> = ScenarioContext<Phase>;

export interface BaseDefinition {
  id: string;
  seed?: number;
  players?: number;
  setupProfileId?: SetupProfileId;
  extends?: BaseId | string;
  setup: (ctx: BaseContext) => void | Promise<void>;
}

export interface ScenarioDefinition<
  Runners extends readonly TestRunner[] = DefaultRunners,
  Phase extends PhaseName | undefined = undefined,
> {
  id: string;
  description?: string;
  from: BaseId | string;
  runners?: Runners;
  phase?: Phase;
  stage?: Phase extends PhaseName ? WorkspaceStageName<Phase> : never;
  when: (ctx: ScenarioContext<Phase>) => void | Promise<void>;
  then: (ctx: ScenarioThenContext<Runners, Phase>) => void | Promise<void>;
}

export type {
  GameView,
  InteractionId,
  InteractionParamsOf,
  PhaseName,
  WorkspaceStageName,
};
`;
}

export {
  REDUCER_TESTING_TYPES_WRAPPER_CONTENT,
  buildReducerTestingContractContent
};
//# sourceMappingURL=chunk-F2DIOJJZ.mjs.map