import { registerDecorator, ValidationArguments, ValidationOptions, ValidatorConstraint, ValidatorConstraintInterface } from 'class-validator';

export function ArrayNoDuplicates(mapper: (value: any) => unknown = (value) => value, validationOptions?: ValidationOptions) {
    return (object: unknown, propertyName: string) => {
        registerDecorator({
            constraints: [mapper],
            options: validationOptions,
            propertyName,
            target: object?.constructor as (...args: any) => any,
            validator: ArrayNoDuplicatesConstraint,
        });
    };
}

@ValidatorConstraint({ name: 'ArrayNoDuplicates' })
class ArrayNoDuplicatesConstraint implements ValidatorConstraintInterface {
    validate(value: any, args: ValidationArguments): boolean {
        if (!Array.isArray(value)) return false;
        const mappedArray = args.constraints?.length > 0 ? value.map(args.constraints[0] as (value: any) => unknown) : value;
        const arraySet = new Set(mappedArray);
        return value.length === arraySet.size;
    }

    defaultMessage(args?: ValidationArguments): string {
        const propertyName = args?.property as string;
        return `Array ${propertyName} must have no duplicates`;
    }
}
