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

export interface CrossFieldNumberComparisonOptions {
    allowNull?: boolean;
    canBeEqual?: boolean;
}

export function IsLowerThan(relatedPropertyName: string, options?: CrossFieldNumberComparisonOptions, validationOptions?: ValidationOptions) {
    return (object: unknown, propertyName: string) => {
        registerDecorator({
            constraints: [relatedPropertyName, options],
            options: validationOptions,
            propertyName,
            target: object?.constructor as (...args: any) => any,
            validator: IsLowerThanConstraint,
        });
    };
}

@ValidatorConstraint({ async: false, name: 'IsLowerThan' })
export class IsLowerThanConstraint implements ValidatorConstraintInterface {
    validate(value: any, args: ValidationArguments) {
        if (typeof value !== 'number') return false;
        const [relatedPropertyName, options] = args.constraints as [string, CrossFieldNumberComparisonOptions];
        const relatedValue = (args.object as Record<string, number>)[relatedPropertyName];
        if (options?.allowNull && (relatedValue === null || value === null)) return true;
        if (relatedValue !== null && typeof relatedValue !== 'number') return false;
        if (options?.canBeEqual) return value <= relatedValue;
        return value < relatedValue;
    }

    defaultMessage(args: ValidationArguments) {
        const [relatedPropertyName] = args.constraints as [string];
        return `${args.property} must be lower than ${relatedPropertyName}`;
    }
}

export function IsGreaterThan(relatedPropertyName: string, options?: CrossFieldNumberComparisonOptions, validationOptions?: ValidationOptions) {
    return (object: unknown, propertyName: string) => {
        registerDecorator({
            constraints: [relatedPropertyName, options],
            options: validationOptions,
            propertyName,
            target: object?.constructor as (...args: any) => any,
            validator: IsGreaterThanConstraint,
        });
    };
}

@ValidatorConstraint({ async: false, name: 'IsGreaterThan' })
export class IsGreaterThanConstraint implements ValidatorConstraintInterface {
    validate(value: any, args: ValidationArguments) {
        if (typeof value !== 'number') return false;
        const [relatedPropertyName, options] = args.constraints as [string, CrossFieldNumberComparisonOptions];
        const relatedValue = (args.object as Record<string, number>)[relatedPropertyName];
        if (options?.allowNull && (relatedValue === null || value === null)) return true;
        if (relatedValue !== null && typeof relatedValue !== 'number') return false;
        if (options?.canBeEqual) return value >= relatedValue;
        return value > relatedValue;
    }

    defaultMessage(args: ValidationArguments) {
        const [relatedPropertyName] = args.constraints as [string];
        return `${args.property} must be greater than ${relatedPropertyName}`;
    }
}
