import fs from "fs";
import path from "path";
import Ajv2020, { type ErrorObject, type ValidateFunction } from "ajv/dist/2020";
import { SCHEMAS_DIR } from "../config";
import type { NormalizedPayload, ValidationResult } from "../types/normalizer";

type SchemaVersion = "v1" | "v2" | "v2_0_1" | "v2_0_2";

const validators = new Map<SchemaVersion, ValidateFunction>();

function schemaPath(version: SchemaVersion): string {
  if (version === "v1") {
    return path.resolve(SCHEMAS_DIR, "normalized_query_v1.json");
  }
  if (version === "v2_0_1") {
    return path.resolve(SCHEMAS_DIR, "normalized_query_v2_0_1.json");
  }
  if (version === "v2_0_2") {
    return path.resolve(SCHEMAS_DIR, "normalized_query_v2_0_2.json");
  }
  return path.resolve(SCHEMAS_DIR, "normalized_query_v2.json");
}

function loadValidator(version: SchemaVersion): ValidateFunction {
  const cached = validators.get(version);
  if (cached) {
    return cached;
  }
  const raw = fs.readFileSync(schemaPath(version), "utf-8");
  const schema = JSON.parse(raw);
  const ajv = new Ajv2020({ allErrors: true, strict: false });
  const compiled = ajv.compile(schema);
  validators.set(version, compiled);
  return compiled;
}

function normalizeAjvErrors(errors: ErrorObject[] | null | undefined): string[] {
  if (!errors || errors.length === 0) {
    return [];
  }
  return errors.map((item) => `${item.instancePath || "/"} ${item.message ?? "validation error"}`.trim());
}

export function validateNormalized(payload: unknown, schemaVersion: SchemaVersion = "v1"): ValidationResult {
  const check = loadValidator(schemaVersion);
  const passed = check(payload);
  return {
    passed: Boolean(passed),
    errors: passed ? [] : normalizeAjvErrors(check.errors)
  };
}

export function assertNormalized(payload: unknown, schemaVersion: SchemaVersion = "v1"): NormalizedPayload {
  const validation = validateNormalized(payload, schemaVersion);
  if (!validation.passed) {
    throw new Error(`Invalid normalized JSON: ${validation.errors.join("; ")}`);
  }
  return payload as NormalizedPayload;
}
