// Evaluator core types — RI-N4 (card RI-3-002, SDLC-D-037 second half).
//
// The quality-rails evaluator is the SOLE authoritative producer of check
// verdicts for the checks it owns. Every verdict is typed and fail-closed:
// missing implementations, missing inputs, unknown check ids, process errors,
// timeouts, and malformed adapter output can never become `passed` or an
// unqualified skip — they surface as `blocked` or `error` with a reason.
// (Vocabulary mirrors MACP's GateStatus discipline from packages/macp.)

/**
 * Typed verdict for a single check execution.
 *
 * - `passed` — the check really ran and its condition held.
 * - `failed` — the check really ran and its condition did NOT hold.
 * - `blocked` — the check could not run at all (missing subject, missing
 *   input). Never a green outcome.
 * - `error` — the check attempted to run but its outcome cannot be trusted
 *   (unknown check id, implementation threw, process error, timeout, malformed
 *   adapter output). Never a green outcome.
 * - `not-applicable` — the check definition explicitly declares it does not
 *   apply to this subject (a qualified skip, always with a reason).
 */
export type CheckStatus = 'passed' | 'failed' | 'blocked' | 'error' | 'not-applicable';

/** Aggregate outcome, MACP-style: `passed` only when every result is green. */
export type AggregateState = 'passed' | 'failed' | 'blocked' | 'error';

/** Kinds of subjects the evaluator can assess. */
export type SubjectKind = 'node' | 'python' | 'rust' | 'monorepo' | 'unknown';

/**
 * A single check verdict. This is the canonical result shape: `status`,
 * `checkId`, `checkVersion`, `subject`, `reason`. `reason` is REQUIRED
 * (enforced by the runner) for every status other than `passed`.
 */
export interface CheckResult {
  status: CheckStatus;
  checkId: string;
  checkVersion: string;
  subject: string;
  reason?: string;
}

/** The project being evaluated. */
export interface Subject {
  /** Absolute path. */
  path: string;
  kind: SubjectKind;
}

/**
 * The data half of a check definition. Definitions live as DATA with a version
 * and a content digest (see `digestOfSpec`); the executable half is attached
 * separately so the digest covers only reviewable, declarative content.
 */
export interface CheckDefinitionSpec {
  /** Stable id, e.g. `qc-19-rails-files-present`. */
  id: string;
  /** Semver of this definition's data+semantics. */
  version: string;
  /** Canonical check id from docs/release-integrity/probe-inventory.md (QC-n). */
  canonicalCheck: string;
  description: string;
  /** Subject kinds this check can assess (others yield `not-applicable`). */
  appliesTo: readonly SubjectKind[];
  /** Declarative parameters (file lists, markers, timeouts) — digest-covered. */
  params: Record<string, unknown>;
}

/** A fully assembled check definition: spec + digest + implementation. */
export interface CheckDefinition extends CheckDefinitionSpec {
  /** sha256 content digest of the spec (canonical JSON projection). */
  definitionDigest: string;
  evaluate(ctx: CheckContext): Promise<CheckOutcome>;
}

/** What a check implementation returns; the runner stamps id/version/subject. */
export interface CheckOutcome {
  status: CheckStatus;
  reason?: string;
}

/** Caller-provided inputs for one check invocation (e.g. the QC-20 probe path). */
export type CheckInputs = Record<string, unknown>;

/** Everything a check implementation may use. */
export interface CheckContext {
  subject: Subject;
  params: Record<string, unknown>;
  inputs: CheckInputs;
  adapter: ProcessAdapter;
}

/** Outcome of running a shell probe through the thin process adapter. */
export type AdapterOutcome =
  | { ok: true; exitCode: number | null; stdout: string; stderr: string }
  | { ok: false; kind: 'spawn-error' | 'timeout'; message: string };

/** Request for the process adapter. */
export interface AdapterRequest {
  file: string;
  args: string[];
  cwd: string;
  timeoutMs: number;
}

/**
 * Thin process adapter: runs a command, owns NO verdict logic. Verdict parsing
 * always lives in the check implementation (TS), never in the shell probe.
 */
export interface ProcessAdapter {
  run(request: AdapterRequest): Promise<AdapterOutcome>;
}

/** Per-subject-kind check-set selection policy (versioned and digested). */
export interface CheckSetPolicySpec {
  version: string;
  byKind: Record<SubjectKind, readonly string[]>;
}

export interface CheckSetPolicy extends CheckSetPolicySpec {
  /** sha256 content digest of the policy spec. */
  policyDigest: string;
}

/** Full typed evaluation report for one subject. */
export interface EvaluationReport {
  subject: Subject;
  results: CheckResult[];
  /** checkId → content digest of the definition that produced the verdicts. */
  definitionDigests: Record<string, string>;
  /** Version of the check-set policy used for subject selection. */
  checkSetVersion: string;
  state: AggregateState;
}

/** Options for `evaluateSubject`. */
export interface EvaluateOptions {
  subjectPath: string;
  /** Restrict to these check ids; defaults to the subject kind's check set. */
  checkIds?: string[];
  /** Per-check inputs, keyed by check id (e.g. `{ 'qc-20-enforcement-verify': { probePath } }`). */
  inputs?: Record<string, CheckInputs>;
  /** Replace the built-in definitions (tests / future batches). */
  definitions?: CheckDefinition[];
  /** Inject a process adapter (tests / instrumentation). */
  adapter?: ProcessAdapter;
}
