import { BadRequestException, Injectable } from '@nestjs/common';
import {
  ValidationArguments,
  ValidationOptions,
  ValidatorConstraint,
  ValidatorConstraintInterface,
} from 'class-validator';
import { DataSource } from 'typeorm';
import { SystemLang } from '../lang/system.lang';

export interface ExistsInDatabaseConstraintOptions {
  except?: (currentEntity: any) => boolean;
  allowNull?: boolean;
}

@ValidatorConstraint({ async: true })
@Injectable()
export class ExistsInDatabaseValidator implements ValidatorConstraintInterface {
  constructor(private readonly dataSource: DataSource) {}

  async validate(value: any, args: ValidationArguments): Promise<boolean> {
    const [EntityClass, field, options = {}, validationOptions] =
      args.constraints as [
        any,
        string,
        ExistsInDatabaseConstraintOptions,
        ValidationOptions?,
      ];

    const { except, allowNull } = options;

    return this.checkExists(value, EntityClass, field, except, allowNull);
  }

  private async checkExists(
    value: any,
    EntityClass: any,
    field: string,
    except?: (currentEntity: any) => boolean,
    allowNull?: boolean,
  ): Promise<boolean> {
    if ((value === null || value === undefined) && allowNull) {
      return true;
    }

    const repo = this.dataSource.getRepository(EntityClass);
    const entity = await repo.findOne({ where: { [field]: value } });

    if (!entity) return false;

    if (typeof except === 'function') {
      return !except(entity);
    }

    return true;
  }

  defaultMessage(args: ValidationArguments) {
    const [_, field, , validationOptions] = args.constraints;
    const each = validationOptions?.each ?? false;

    if (each) {
      return SystemLang.getCustomText({
        vi: `Một hoặc nhiều giá trị trong mảng không tồn tại trong cột "${field}".`,
        en: `One or more values in the array do not exist in column "${field}".`,
      });
    }

    return SystemLang.getCustomText({
      vi: `Giá trị của "${args.property}" không tồn tại trong cột "${field}".`,
      en: `The value of "${args.property}" does not exist in column "${field}".`,
    });
  }
}
