import { GENERATION_LIMITS } from "./generation-limits";
import {
  GENERATED_CASE_GENERATOR_ID,
  GENERATED_CASE_GENERATOR_VERSION,
  type GeneratedCaseProvenance,
} from "./generation.types";

export const MAXIMUM_GENERATED_CANDIDATE_ID_UTF16 = 128;

function record(value: unknown, label: string): Record<string, unknown> {
  if (!value || typeof value !== "object" || Array.isArray(value)) {
    throw new Error(`${label} must be a JSON object.`);
  }
  return value as Record<string, unknown>;
}

function text(value: unknown, label: string, maximum: number): string {
  if (typeof value !== "string") {
    throw new Error(`${label} must be text.`);
  }
  if (value.length === 0 || value.length > maximum) {
    throw new Error(
      `${label} must contain 1 to ${maximum.toLocaleString()} UTF-16 units.`,
    );
  }
  return value;
}

export function parseGeneratedCaseProvenance(
  value: unknown,
  label = "Generated-case provenance",
): GeneratedCaseProvenance {
  const input = record(value, label);
  const supported = new Set([
    "kind",
    "generatorId",
    "generatorVersion",
    "seed",
    "candidateId",
    "intendedOutcome",
  ]);
  const unknown = Object.keys(input).filter((key) => !supported.has(key));
  if (unknown.length > 0) {
    throw new Error(
      `${label} contains unsupported entries: ${unknown.join(", ")}.`,
    );
  }
  if (input.kind !== "generated") {
    throw new Error(`${label} kind must be "generated".`);
  }
  if (input.generatorId !== GENERATED_CASE_GENERATOR_ID) {
    throw new Error(
      `${label} generatorId must be ${JSON.stringify(GENERATED_CASE_GENERATOR_ID)}.`,
    );
  }
  if (input.generatorVersion !== GENERATED_CASE_GENERATOR_VERSION) {
    throw new Error(
      `${label} generatorVersion must be ${JSON.stringify(GENERATED_CASE_GENERATOR_VERSION)}.`,
    );
  }
  if (
    input.intendedOutcome !== "match" &&
    input.intendedOutcome !== "no-match"
  ) {
    throw new Error(`${label} intendedOutcome must be "match" or "no-match".`);
  }
  return {
    kind: "generated",
    generatorId: GENERATED_CASE_GENERATOR_ID,
    generatorVersion: GENERATED_CASE_GENERATOR_VERSION,
    seed: text(input.seed, `${label} seed`, GENERATION_LIMITS.maximumSeedUtf16),
    candidateId: text(
      input.candidateId,
      `${label} candidateId`,
      MAXIMUM_GENERATED_CANDIDATE_ID_UTF16,
    ),
    intendedOutcome: input.intendedOutcome,
  };
}
