/** Renderer-independent, JSON-safe reinforcement-learning contract. */

export const ARENA_API_VERSION = "tera.arena/v1" as const;

export type ArenaSplit = "train" | "dev";

export interface ArenaScenarioRequest {
  /** Public splits only. Held-out/private evaluation belongs outside this package. */
  split: ArenaSplit;
  /** Omit to choose deterministically from the requested split using the seed. */
  id?: string;
}

export interface ArenaScenario<P extends object = Record<string, number | string | boolean>> {
  id: string;
  split: ArenaSplit;
  seed: number;
  parameters: P;
  hash: string;
}

export interface ArenaSourceHashes {
  /** Hash of the environment implementation source, pinned and checked in CI. */
  environment: string;
  /** Hash of the renderer-independent controller/plan sources the environment wraps. */
  simulator: string;
}

export interface ArenaInfo {
  apiVersion: typeof ARENA_API_VERSION;
  envId: string;
  envVersion: number;
  envHash: string;
  scenarioId: string;
  scenarioSplit: ArenaSplit;
  scenarioHash: string;
  simulatorHash: string;
  environmentSourceHash: string;
  seed: number;
  step: number;
  maxSteps: number;
  terminalReason: string | null;
  stateChecksum: string;
}

export interface ArenaResetResult<O> {
  observation: O;
  info: ArenaInfo;
}

export interface ArenaStepResult<O, R extends Record<string, number>> {
  observation: O;
  reward: number;
  rewardComponents: R;
  terminated: boolean;
  truncated: boolean;
  info: ArenaInfo;
}

export interface ArenaManifest {
  apiVersion: typeof ARENA_API_VERSION;
  id: string;
  version: number;
  title: string;
  description: string;
  simulator: string;
  fixedStepSeconds: number;
  maxSteps: number;
  actionFields: readonly string[];
  observationFields: readonly string[];
  rewardComponents: Readonly<Record<string, string>>;
  safetyTerminals: readonly string[];
  scenarioIds: Readonly<Record<ArenaSplit, readonly string[]>>;
  baselines: {
    inaction: string;
    scripted: string;
  };
}

export interface ArenaSnapshot<S> {
  apiVersion: typeof ARENA_API_VERSION;
  envId: string;
  envVersion: number;
  envHash: string;
  seed: number;
  scenarioId: string;
  scenarioSplit: ArenaSplit;
  scenarioHash: string;
  step: number;
  cumulativeReward: number;
  terminated: boolean;
  truncated: boolean;
  terminalReason: string | null;
  simulation: S;
  checksum: string;
}

export interface ArenaTraceStep<A, R extends Record<string, number>> {
  index: number;
  action: A;
  reward: number;
  rewardComponents: R;
  terminated: boolean;
  truncated: boolean;
  terminalReason: string | null;
  stateChecksum: string;
}

export interface ArenaTraceEnvelope<A, R extends Record<string, number>> {
  apiVersion: typeof ARENA_API_VERSION;
  envId: string;
  envVersion: number;
  envHash: string;
  scenarioId: string;
  scenarioSplit: ArenaSplit;
  scenarioHash: string;
  sourceHashes: ArenaSourceHashes;
  seed: number;
  initialStateChecksum: string;
  steps: readonly ArenaTraceStep<A, R>[];
  finalStateChecksum: string;
  cumulativeReward: number;
  checksum: string;
}

export interface ArenaReplayResult<O> {
  observation: O;
  steps: number;
  cumulativeReward: number;
  finalStateChecksum: string;
}

export interface ArenaEnvironment<
  A,
  O,
  R extends Record<string, number>,
  S,
> {
  readonly manifest: ArenaManifest;
  reset(seed: number, scenario: string | ArenaScenarioRequest): ArenaResetResult<O>;
  step(action: A): ArenaStepResult<O, R>;
  snapshot(): ArenaSnapshot<S>;
  restore(snapshot: ArenaSnapshot<S>): ArenaResetResult<O>;
  trace(): ArenaTraceEnvelope<A, R>;
  replay(trace: ArenaTraceEnvelope<A, R>): ArenaReplayResult<O>;
}
