import { Logger } from '@apic/studio-shared';
import { SchemaHandler, YamlContent } from '@apic/studio-shared';
import { Ajv } from 'ajv';
import addFormats from 'ajv-formats';

export class AssetValidator {
  private ajv: Ajv;

  private schemaHandler: SchemaHandler;

  constructor(schemaHandler: SchemaHandler) {
    this.ajv = new Ajv();
    addFormats.default(this.ajv);
    this.ajv.addFormat('uint64', {
      type: 'number',
      validate: (x: number) => Number.isInteger(x) && x >= 0 && x <= Number.MAX_SAFE_INTEGER,
    });

    this.schemaHandler = schemaHandler;
  }

  public validateAssets(genObj: YamlContent): {
    valid: boolean;
    errors: string[];
  } {
    const errors: string[] = [];

    try {
      const kind = genObj.kind;
      const version = genObj.apiVersion || undefined;
      const assetRef = `${genObj.metadata.namespace}:${genObj.metadata.name}:${genObj.metadata.version}`;

      if (!kind) {
        const msg = 'Kind or version is undefined or null';
        Logger.error(msg);
        return { valid: false, errors: [msg] };
      }

      let allValid = true;

      const schemaStr = this.schemaHandler.getSchema(kind, version);

      if (!schemaStr) {
        const msg = `Schema not found for kind: ${kind}, version: ${version}`;
        Logger.error(msg);
        errors.push(msg);
        allValid = false;
        return { valid: false, errors };
      }
      const schema = typeof schemaStr === 'string' ? JSON.parse(schemaStr) : schemaStr;
      const validate = this.ajv.compile(schema);
      const isValid = validate(genObj);

      if (!isValid) {
        const ajvMessages = (validate.errors || [])
          .map((err: any) => {
            const path = err.instancePath || '/';
            const keyword = err.keyword;
            const message = err.message || 'Validation error';
            const params = JSON.stringify(err.params, null, 2);

            return {
              path,
              keyword,
              message,
              params,
            };
          })
          .sort((a: any, b: any) => a.path.localeCompare(b.path))
          .map((err: any) => {
            return `At path "${err.path}": ${err.message} (keyword: ${err.keyword}, params: ${err.params})`;
          })
          .join('\n');

        const msg = `The asset ${assetRef} has one or more problems. ${ajvMessages}. Review the error and try publishing again.`;
        Logger.error(msg);
        errors.push(msg);
        allValid = false;
      }

      return { valid: allValid, errors };
    } catch (err) {
      const msg = `Error validating asset: ${err instanceof Error ? err.message : String(err)}`;
      Logger.error(msg, err instanceof Error ? err : new Error(String(err)));
      return { valid: false, errors: [msg] };
    }
  }
}
