/**
 * # grade/measurement — alpha measurements carry a KIND, and kinds never blend (kestrel-a57.6)
 *
 * A measurement of alpha is not one number; it is a number **plus the evidence semantics that produced it**.
 * The three kinds carry DIFFERENT evidence, so silently blending or ranking one against another is a category
 * error — the same class of error ADR-0006 forbids across measurement *versions* ("EVs across measurement
 * versions refuse naive comparison"). This module is the KIND-axis sibling of that rule: **alphas across KINDS
 * refuse naive comparison**, and the enforcement is the TypeScript compiler.
 *
 *   1. RULE ALPHA      — a MECHANICAL TEMPLATE run over EVERY eligible opportunity on a tape (systematic, no
 *                        discretion). Its evidence is the template's realized measure over the whole universe.
 *   2. SELECTION ALPHA — POINT-IN-TIME CURATION (the author PICKED which opportunities to act on), measured
 *                        VS A FROZEN COUNTERFACTUAL on the SAME FUTURE TAPE (what those same picks, frozen,
 *                        would have realized). Its evidence is SELECTION skill: realized minus the frozen leg.
 *   3. DIAGNOSTIC      — exploratory / inspection only. Carries an explicit `non_promotable: true` marker and
 *                        has NO ordering surface at all (no `compare`, no `rank`, no `promote`).
 *
 * ## The barrier (compile-time)
 * Each kind is a distinctly **branded** record (a private `unique symbol` phantom brand ⇒ nominal, mutually
 * non-assignable, even if fields ever coincided). `compare` / `rank` / `promote` are pinned with `NoInfer<M>`:
 * the measurement type `M` is inferred ONLY from the first parameter, so a second argument of a different kind
 * has nothing to unify with and fails to type-check. `PromotableAlpha` excludes diagnostics, so the diagnostic
 * kind has no accepting overload anywhere — its only surface is construction and inspection.
 *
 * ## Diagnostics never promote (dual gate — fail-closed)
 * (compile) `promote<M extends PromotableAlpha>` will not bind a `DiagnosticMeasurement`; (runtime) if a caller
 * bypasses the types (an `any` / erased boundary), {@link promote} returns a typed refusal — `{ ok: false,
 * reason, kind }` — never throws, never silently succeeds. This mirrors the module's fail-closed convention
 * ({@link bankableEv} floors by RETURN; the receipt projector degrades with a logged reason) rather than
 * inventing a new error style.
 *
 * Ranking is on ONE named scalar per kind — never a blended omnibus (honors the m9i no-omnibus rule). Pure and
 * deterministic: no wall clock, no RNG; off the runtime/record path (no bus/Blotter/grammar/golden bytes move).
 */

import type { MeasurementVersions } from "./receipt.ts";

// ─────────────────────────────────────────────────────────────────────────────
// The nominal brand — a private phantom symbol, one string tag per kind
// ─────────────────────────────────────────────────────────────────────────────

declare const BRAND: unique symbol;
/** A phantom, compile-time-only tag that makes two otherwise-structural records mutually non-assignable. */
type Brand<B extends string> = { readonly [BRAND]: B };

// ─────────────────────────────────────────────────────────────────────────────
// Shared value shapes
// ─────────────────────────────────────────────────────────────────────────────

/** The identity of a mechanical template (rule alpha) — the id plus a content hash of its definition. */
export interface TemplateRef {
  readonly template_id: string;
  readonly template_hash: string;
}

/** The future tape a measurement was realized on — the id plus a content hash of the tape's bytes. */
export interface TapeRef {
  readonly tape_id: string;
  readonly tape_hash: string;
}

/**
 * A realized alpha measure: ONE declared scalar (`realized_r`) plus the banked expected-$ and the sample size
 * behind it. Ranking within a kind is on the ONE named scalar, never a blended composite of these fields.
 */
export interface AlphaScore {
  readonly realized_r: number;
  readonly expected_dollars: number;
  readonly n: number;
}

/** The frozen leg of a selection measurement — what the SAME picks, frozen, realized on the SAME future tape. */
export interface FrozenCounterfactual {
  readonly realized: AlphaScore;
  readonly frozen_hash: string;
}

// ─────────────────────────────────────────────────────────────────────────────
// (1) RULE ALPHA — a mechanical template over the whole eligible universe
// ─────────────────────────────────────────────────────────────────────────────

/** The honest fields of a rule-alpha measurement (the {@link ruleAlpha} builder stamps the kind + brand). */
export interface RuleAlphaInput {
  readonly template: TemplateRef;
  readonly tape: TapeRef;
  /** Every eligible opportunity the mechanical rule ran over — the systematic universe, no selection. */
  readonly universe: number;
  readonly realized: AlphaScore;
  readonly measurement_versions: MeasurementVersions;
}

/** A rule-alpha measurement: mechanically applied over EVERY opportunity. Nominal (branded). */
export type RuleAlphaMeasurement = RuleAlphaInput & { readonly kind: "rule_alpha" } & Brand<"RuleAlpha">;

/** Build a {@link RuleAlphaMeasurement}. Pure: stamps the discriminant kind; no clock, no RNG. */
export function ruleAlpha(input: RuleAlphaInput): RuleAlphaMeasurement {
  return { ...input, kind: "rule_alpha" } as RuleAlphaMeasurement;
}

// ─────────────────────────────────────────────────────────────────────────────
// (2) SELECTION ALPHA — discretionary picks vs a frozen counterfactual (same tape)
// ─────────────────────────────────────────────────────────────────────────────

/** The honest fields of a selection-alpha measurement (the {@link selectionAlpha} builder stamps kind + brand). */
export interface SelectionAlphaInput {
  /** The curation: how many opportunities existed vs how many the author PICKED to act on. */
  readonly picks: { readonly universe: number; readonly picked: number };
  readonly tape: TapeRef;
  readonly realized: AlphaScore;
  /** Stored INLINE (not referenced): the same picks, frozen, realized on the SAME future tape. */
  readonly frozen_counterfactual: FrozenCounterfactual;
  readonly measurement_versions: MeasurementVersions;
}

/** A selection-alpha measurement: discretionary curation vs its frozen leg. Nominal (branded). */
export type SelectionAlphaMeasurement = SelectionAlphaInput & { readonly kind: "selection_alpha" } & Brand<"SelectionAlpha">;

/** Build a {@link SelectionAlphaMeasurement}. Pure: stamps the discriminant kind; no clock, no RNG. */
export function selectionAlpha(input: SelectionAlphaInput): SelectionAlphaMeasurement {
  return { ...input, kind: "selection_alpha" } as SelectionAlphaMeasurement;
}

// ─────────────────────────────────────────────────────────────────────────────
// (3) DIAGNOSTIC — inspection only; never promotes, no ordering surface
// ─────────────────────────────────────────────────────────────────────────────

/** The honest fields of a diagnostic measurement (the {@link diagnostic} builder stamps the non-promotable marker). */
export interface DiagnosticInput {
  readonly probe: string;
  readonly observed: AlphaScore;
  readonly note: string;
}

/**
 * A diagnostic measurement: exploratory / inspection only. Carries an explicit literal-`true` `non_promotable`
 * marker and is NOT part of {@link PromotableAlpha}, so it has no `compare` / `rank` / `promote` surface. Nominal.
 */
export type DiagnosticMeasurement = DiagnosticInput & {
  readonly kind: "diagnostic";
  readonly non_promotable: true;
} & Brand<"Diagnostic">;

/** Build a {@link DiagnosticMeasurement}. Pure: stamps the kind + the explicit non-promotable marker. */
export function diagnostic(input: DiagnosticInput): DiagnosticMeasurement {
  return { ...input, kind: "diagnostic", non_promotable: true } as DiagnosticMeasurement;
}

// ─────────────────────────────────────────────────────────────────────────────
// The promotable universe — rule + selection ONLY (diagnostics excluded by design)
// ─────────────────────────────────────────────────────────────────────────────

/**
 * The kinds that can be compared / ranked / promoted. Diagnostics are DELIBERATELY absent: they are the
 * category that must never be laundered into a promotable claim, so they have no ordering surface at all.
 */
export type PromotableAlpha = RuleAlphaMeasurement | SelectionAlphaMeasurement;

// ─────────────────────────────────────────────────────────────────────────────
// The single named scalar per kind (never a blended omnibus)
// ─────────────────────────────────────────────────────────────────────────────

interface Scalar {
  /** The NAME of the scalar being ordered on — so a comparison always declares what it ranked by. */
  readonly name: string;
  readonly value: number;
}

/**
 * The ONE named scalar a kind is ordered by:
 *   • rule alpha      → `realized_r` (the mechanical rule's realized measure over its whole universe);
 *   • selection alpha → `selection_edge` = realized − frozen counterfactual (the marginal value of the curation).
 * Never a composite — the honesty rule is that ranking is on a single, named measure.
 */
function scalarOf(m: PromotableAlpha): Scalar {
  if (m.kind === "rule_alpha") {
    return { name: "realized_r", value: m.realized.realized_r };
  }
  return {
    name: "selection_edge",
    value: m.realized.realized_r - m.frozen_counterfactual.realized.realized_r,
  };
}

// ─────────────────────────────────────────────────────────────────────────────
// compare / rank — same-kind only (NoInfer<M> is the cross-kind barrier)
// ─────────────────────────────────────────────────────────────────────────────

/** The result of a same-kind comparison: the sign of the ordering PLUS the name of the scalar it ranked by. */
export interface Comparison {
  readonly order: -1 | 0 | 1;
  readonly measure: string;
}

/**
 * Compare two measurements OF THE SAME KIND on that kind's one named scalar. `M` is inferred solely from `a`;
 * `NoInfer<M>` pins `b` to the same kind, so a cross-kind second argument fails to compile. Diagnostics are not
 * in {@link PromotableAlpha}, so they have no `compare` overload at all. Pure, total, deterministic.
 */
export function compare<M extends PromotableAlpha>(a: M, b: NoInfer<M>): Comparison {
  const sa = scalarOf(a);
  const sb = scalarOf(b);
  const order: -1 | 0 | 1 = sa.value > sb.value ? 1 : sa.value < sb.value ? -1 : 0;
  return { order, measure: sa.name };
}

/**
 * Rank measurements OF THE SAME KIND, descending by that kind's one named scalar. `M` is inferred solely from
 * the first element; every remaining element is pinned to the same kind via `NoInfer<M>`, so a mixed-kind list
 * (or a list containing a diagnostic) fails to compile. Pure: returns a sorted COPY; the input is never mutated.
 */
export function rank<M extends PromotableAlpha>(xs: readonly [M, ...NoInfer<M>[]]): M[] {
  return [...xs].sort((a, b) => scalarOf(b).value - scalarOf(a).value);
}

// ─────────────────────────────────────────────────────────────────────────────
// promote — fail-closed; diagnostics can NEVER promote
// ─────────────────────────────────────────────────────────────────────────────

/**
 * The typed outcome of a promotion attempt — a discriminated union, fail-closed by RETURN (never a thrown
 * error, never a silent default). `ok:false` carries the machine-readable `reason` and the offending `kind`.
 */
export type PromotionOutcome =
  | { readonly ok: true; readonly candidate: PromotableAlpha }
  | { readonly ok: false; readonly reason: string; readonly kind: string };

/**
 * Promote a promotable alpha to a candidate. At compile time only {@link PromotableAlpha} binds `M`, so a
 * diagnostic has no accepting path. At RUNTIME this is defense-in-depth for a caller that bypassed the types
 * (an `any` / erased boundary): it inspects the actual object and FAILS CLOSED — a diagnostic, a non-promotable
 * marker, or any unrecognized kind returns a typed refusal with a logged reason. It never throws, never silently
 * succeeds. Pure and deterministic.
 */
export function promote<M extends PromotableAlpha>(candidate: M): PromotionOutcome {
  // Read the runtime shape through a widened view so the guard is NOT narrowed away by the compile-time type —
  // a types-bypassed caller may hand us an object whose real kind is "diagnostic".
  const probe = candidate as { readonly kind?: unknown; readonly non_promotable?: unknown };
  const kind = typeof probe.kind === "string" ? probe.kind : "unknown";
  const isPromotableKind = kind === "rule_alpha" || kind === "selection_alpha";
  if (probe.non_promotable === true || !isPromotableKind) {
    return {
      ok: false,
      reason: `diagnostic / non-promotable measurement can never promote (kind=${kind})`,
      kind,
    };
  }
  return { ok: true, candidate };
}
