/**
 * Hex String Validator
 *
 * Custom validators for hex string formats (public keys, signatures, hashes).
 */

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

// ============================================================================
// Message Hash Validator (32 bytes / 64 hex chars)
// ============================================================================

@ValidatorConstraint({ name: 'isMessageHash', async: false })
export class IsMessageHashConstraint implements ValidatorConstraintInterface {
  validate(value: string, _args: ValidationArguments): boolean {
    if (!value || typeof value !== 'string') {
      return false;
    }
    const cleanHex = value.replace('0x', '');
    return /^[a-fA-F0-9]{64}$/.test(cleanHex);
  }

  defaultMessage(_args: ValidationArguments): string {
    return 'messageHash must be a 32-byte hex string (64 hex characters)';
  }
}

export function IsMessageHash(validationOptions?: ValidationOptions) {
  return function (object: object, propertyName: string) {
    registerDecorator({
      target: object.constructor,
      propertyName: propertyName,
      options: validationOptions,
      constraints: [],
      validator: IsMessageHashConstraint,
    });
  };
}

// ============================================================================
// Public Key Validator (33 bytes compressed or 65 bytes uncompressed)
// ============================================================================

@ValidatorConstraint({ name: 'isPublicKey', async: false })
export class IsPublicKeyConstraint implements ValidatorConstraintInterface {
  validate(value: string, _args: ValidationArguments): boolean {
    if (!value || typeof value !== 'string') {
      return false;
    }
    const cleanHex = value.replace('0x', '');
    // 33 bytes (compressed) = 66 hex chars
    // 65 bytes (uncompressed) = 130 hex chars
    return /^[a-fA-F0-9]{66}$/.test(cleanHex) || /^[a-fA-F0-9]{130}$/.test(cleanHex);
  }

  defaultMessage(_args: ValidationArguments): string {
    return 'publicKey must be a valid 33-byte (compressed) or 65-byte (uncompressed) public key';
  }
}

export function IsPublicKey(validationOptions?: ValidationOptions) {
  return function (object: object, propertyName: string) {
    registerDecorator({
      target: object.constructor,
      propertyName: propertyName,
      options: validationOptions,
      constraints: [],
      validator: IsPublicKeyConstraint,
    });
  };
}

// ============================================================================
// Signature Validator (64 or 65 bytes)
// ============================================================================

@ValidatorConstraint({ name: 'isSignature', async: false })
export class IsSignatureConstraint implements ValidatorConstraintInterface {
  validate(value: string, _args: ValidationArguments): boolean {
    if (!value || typeof value !== 'string') {
      return false;
    }
    const cleanHex = value.replace('0x', '');
    // 64 bytes (r + s) = 128 hex chars
    // 65 bytes (r + s + v) = 130 hex chars
    return /^[a-fA-F0-9]{128}$/.test(cleanHex) || /^[a-fA-F0-9]{130}$/.test(cleanHex);
  }

  defaultMessage(_args: ValidationArguments): string {
    return 'signature must be a valid 64-byte or 65-byte signature';
  }
}

export function IsSignature(validationOptions?: ValidationOptions) {
  return function (object: object, propertyName: string) {
    registerDecorator({
      target: object.constructor,
      propertyName: propertyName,
      options: validationOptions,
      constraints: [],
      validator: IsSignatureConstraint,
    });
  };
}

// ============================================================================
// Generic Hex String Validator
// ============================================================================

export interface HexStringOptions {
  minBytes?: number;
  maxBytes?: number;
  exactBytes?: number;
}

@ValidatorConstraint({ name: 'isHexString', async: false })
export class IsHexStringConstraint implements ValidatorConstraintInterface {
  validate(value: string, args: ValidationArguments): boolean {
    if (!value || typeof value !== 'string') {
      return false;
    }

    const cleanHex = value.replace('0x', '');
    if (!/^[a-fA-F0-9]*$/.test(cleanHex)) {
      return false;
    }

    const options = (args.constraints[0] || {}) as HexStringOptions;
    const byteLength = cleanHex.length / 2;

    if (options.exactBytes !== undefined && byteLength !== options.exactBytes) {
      return false;
    }

    if (options.minBytes !== undefined && byteLength < options.minBytes) {
      return false;
    }

    if (options.maxBytes !== undefined && byteLength > options.maxBytes) {
      return false;
    }

    return true;
  }

  defaultMessage(args: ValidationArguments): string {
    const options = (args.constraints[0] || {}) as HexStringOptions;
    if (options.exactBytes !== undefined) {
      return `Value must be exactly ${options.exactBytes} bytes (${options.exactBytes * 2} hex characters)`;
    }
    if (options.minBytes !== undefined && options.maxBytes !== undefined) {
      return `Value must be between ${options.minBytes} and ${options.maxBytes} bytes`;
    }
    return 'Value must be a valid hex string';
  }
}

export function IsHexString(
  options?: HexStringOptions,
  validationOptions?: ValidationOptions,
) {
  return function (object: object, propertyName: string) {
    registerDecorator({
      target: object.constructor,
      propertyName: propertyName,
      options: validationOptions,
      constraints: [options],
      validator: IsHexStringConstraint,
    });
  };
}
