// src/core/eval/strategy/contract.ts
// Kernel strategy contract.
//
// The kernel owns stepOnce(state) as the deterministic transition substrate.
// Strategies are policy: they decide how Op boundaries are handled.

import type { State, StepOutcome } from "../machine";
import type { Runtime } from "../runtime";
import type { Val } from "../values";
import type { DistVal } from "../dist";
import type { BudgetTracker } from "../../governance/budgets";

export interface TraceEntry {
  step: number;
  control: string;
  kontDepth: number;
  outcome: "state" | "done" | "op";
  op?: string;
  value?: string;
}

export type RunStats = {
  steps: number;
  effectCalls: number;
  forks: number;
  solutions: number;
  elapsedMs: number;
};

export const ZERO_RUN_STATS: RunStats = {
  steps: 0,
  effectCalls: 0,
  forks: 0,
  solutions: 0,
  elapsedMs: 0,
};

// Compatibility alias used by existing strategy implementations.
export type StepFn = (state: State) => StepOutcome;

export type StrategyOptions = {
  maxSteps?: number;
  budget?: BudgetTracker;
  seed?: number;
  timeoutMs?: number;
  trace?: boolean;
  strategyConfig?: Record<string, unknown>;
};

// Compatibility alias used by existing strategy implementations.
export type RunOptions = StrategyOptions;

type RunResultBase = {
  state: State;
  stats: RunStats;
  // Compatibility field kept for middleware and package code that still reads `steps`.
  steps: number;
  trace?: TraceEntry[];
  meta?: Record<string, unknown>;
};

type LegacyRunResult = {
  value: Val;
  state: State;
  steps: number;
  trace?: TraceEntry[];
  meta?: Record<string, unknown>;
};

export type RunResult =
  | LegacyRunResult
  | (RunResultBase & {
      tag: "Value";
      value: Val;
      dist?: undefined;
      reason?: undefined;
    })
  | (RunResultBase & {
      tag: "Dist";
      dist: DistVal;
      value?: Val;
      reason?: undefined;
    })
  | (RunResultBase & {
      tag: "Fail";
      reason: string;
      value?: Val;
      dist?: undefined;
    });

export function runStatsFromSteps(
  steps: number,
  overrides: Partial<RunStats> = {},
): RunStats {
  return {
    ...ZERO_RUN_STATS,
    steps,
    ...overrides,
  };
}

export function valueResult(
  value: Val,
  state: State,
  stats: RunStats,
  extra: Pick<RunResultBase, "trace" | "meta"> = {},
): RunResult {
  return {
    tag: "Value",
    value,
    state,
    stats,
    steps: stats.steps,
    trace: extra.trace,
    meta: extra.meta,
  };
}

export function distResult(
  dist: DistVal,
  state: State,
  stats: RunStats,
  extra: Pick<RunResultBase, "trace" | "meta"> = {},
): RunResult {
  return {
    tag: "Dist",
    dist,
    state,
    stats,
    steps: stats.steps,
    trace: extra.trace,
    meta: extra.meta,
  };
}

export function failResult(
  reason: string,
  state: State,
  stats: RunStats,
  extra: Pick<RunResultBase, "trace" | "meta"> = {},
): RunResult {
  return {
    tag: "Fail",
    reason,
    state,
    stats,
    steps: stats.steps,
    trace: extra.trace,
    meta: extra.meta,
  };
}

export function resolveStrategyOptions(
  stepFnOrOptions?: StepFn | StrategyOptions,
  maybeOptions?: StrategyOptions,
): StrategyOptions {
  if (typeof stepFnOrOptions === "function") {
    return maybeOptions ?? {};
  }
  return stepFnOrOptions ?? maybeOptions ?? {};
}

export interface RunStrategy {
  readonly name: string;
  readonly description: string;
  readonly interceptedOps: readonly string[];
  run(
    runtime: Runtime,
    initial: State,
    stepFnOrOptions?: StepFn | StrategyOptions,
    maybeOptions?: StrategyOptions,
  ): Promise<RunResult>;
  dispose?(): void | Promise<void>;
}
