export type DecoderConstraints = {
  rejectEmptyCoercion?: boolean;
  rejectNullCoercion?: boolean;
  decimalIntegerOnly?: boolean;
  safeIntegerOnly?: boolean;
  rejectNonFinite?: boolean;
  rejectMalformedDate?: boolean;
  idempotent?: boolean;
  reversible?: boolean;
  collisionSafe?: boolean;
};

export type DecoderObservation = {
  accepted: boolean;
  value?: unknown;
  error?: {
    kind: string;
    message: string;
    rawLeak?: string;
  };
};

export type FallbackCandidate = {
  value: unknown;
  valid: boolean;
};

export type CanonicalizationPass = {
  input: unknown;
  firstPass: unknown;
  secondPass: unknown;
  decodeAfterCanonicalize?: unknown;
};

export type SentinelScenario = {
  state: "missing" | "pending" | "unknown" | "empty" | "zero" | "failed";
  decodedAs: unknown;
  validValues: unknown[];
  declaredEquivalences?: Array<{
    states: Array<SentinelScenario["state"]>;
    equivalentValue: unknown;
  }>;
};

export type DecoderScenario =
  | {
      scenarioId: string;
      kind: "integer";
      input: unknown;
      constraints: DecoderConstraints;
      observation: DecoderObservation;
    }
  | {
      scenarioId: string;
      kind: "date";
      input: string;
      malformed: boolean;
      constraints: DecoderConstraints;
      observation: DecoderObservation;
    }
  | {
      scenarioId: string;
      kind: "canonicalization";
      passes: CanonicalizationPass;
      constraints: DecoderConstraints;
      collisionPair?: { left: unknown; right: unknown; canonical: unknown };
    }
  | {
      scenarioId: string;
      kind: "fallback";
      candidates: FallbackCandidate[];
      selectedIndex: number;
      constraints: DecoderConstraints;
      observation: DecoderObservation;
    }
  | {
      scenarioId: string;
      kind: "sentinel";
      sentinel: SentinelScenario;
      constraints: DecoderConstraints;
    }
  | {
      scenarioId: string;
      kind: "error-boundary";
      input: unknown;
      constraints: DecoderConstraints;
      observation: DecoderObservation;
      driverException: string;
    };

export type DecoderFixture = {
  fixtureId: string;
  scenarios: DecoderScenario[];
  intentionalException?: {
    rules: string[];
    reason: string;
  };
};
