/**
 * Utilitaires de validation de formulaires réutilisables avec messages d'erreur.
 */

export type ValidationResult = string | null;
export type Validator<T = unknown> = (value: T) => ValidationResult;

/**
 * Messages d'erreur de validation (préparés pour i18n)
 */
export const validationMessages = {
  required: 'This field is required',
  email: 'Invalid email address',
  minLength: (min: number) => `Minimum ${min} characters`,
  maxLength: (max: number) => `Maximum ${max} characters`,
  min: (min: number) => `Minimum value is ${min}`,
  max: (max: number) => `Maximum value is ${max}`,
  pattern: 'Invalid format',
  url: 'Invalid URL',
  number: 'Must be a number',
  integer: 'Must be an integer',
  positive: 'Must be a positive number',
  phone: 'Invalid phone number',
  date: 'Invalid date',
  dateMin: (min: Date) => `Date must be after ${min.toLocaleDateString()}`,
  dateMax: (max: Date) => `Date must be before ${max.toLocaleDateString()}`,
  fileSize: (maxSize: number) =>
    `File size must be less than ${formatFileSize(maxSize)}`,
  fileType: (allowedTypes: string[]) =>
    `File type must be one of: ${allowedTypes.join(', ')}`,
};

/**
 * Formatage de la taille de fichier
 */
function formatFileSize(bytes: number): string {
  if (bytes === 0) return '0 Bytes';
  const k = 1024;
  const sizes = ['Bytes', 'KB', 'MB', 'GB'];
  const i = Math.floor(Math.log(bytes) / Math.log(k));
  return `${Math.round((bytes / Math.pow(k, i)) * 100) / 100} ${sizes[i]}`;
}

/**
 * Validators de base
 */
export const validators = {
  /**
   * Vérifie que le champ est requis
   */
  required: (value: unknown): ValidationResult => {
    if (value === null || value === undefined) {
      return validationMessages.required;
    }
    if (typeof value === 'string' && value === '') {
      return validationMessages.required;
    }
    if (typeof value === 'string' && !value.trim()) {
      return validationMessages.required;
    }
    if (Array.isArray(value) && value.length === 0) {
      return validationMessages.required;
    }
    if (
      typeof value === 'object' &&
      Object.keys(value as object).length === 0
    ) {
      if (value instanceof Date) return null; // Date is an object but empty keys check assumes plain object
      return validationMessages.required;
    }
    return null;
  },

  /**
   * Vérifie que la valeur est un email valide
   */
  email: (value: string): ValidationResult => {
    if (!value) return null; // required est géré séparément
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    if (!emailRegex.test(value)) {
      return validationMessages.email;
    }
    return null;
  },

  /**
   * Vérifie la longueur minimale
   */
  minLength: (min: number): Validator<string> => {
    return (value: string): ValidationResult => {
      if (!value) return null; // required est géré séparément
      if (value.length < min) {
        return validationMessages.minLength(min);
      }
      return null;
    };
  },

  /**
   * Vérifie la longueur maximale
   */
  maxLength: (max: number): Validator<string> => {
    return (value: string): ValidationResult => {
      if (!value) return null; // required est géré séparément
      if (value.length > max) {
        return validationMessages.maxLength(max);
      }
      return null;
    };
  },

  /**
   * Vérifie la valeur minimale (pour les nombres)
   */
  min: (min: number): Validator<number> => {
    return (value: number): ValidationResult => {
      if (value === null || value === undefined) return null;
      if (typeof value !== 'number' || isNaN(value)) return null;
      if (value < min) {
        return validationMessages.min(min);
      }
      return null;
    };
  },

  /**
   * Vérifie la valeur maximale (pour les nombres)
   */
  max: (max: number): Validator<number> => {
    return (value: number): ValidationResult => {
      if (value === null || value === undefined) return null;
      if (typeof value !== 'number' || isNaN(value)) return null;
      if (value > max) {
        return validationMessages.max(max);
      }
      return null;
    };
  },

  /**
   * Vérifie que la valeur correspond à un pattern (regex)
   */
  pattern: (regex: RegExp): Validator<string> => {
    return (value: string): ValidationResult => {
      if (!value) return null; // required est géré séparément
      if (!regex.test(value)) {
        return validationMessages.pattern;
      }
      return null;
    };
  },

  /**
   * Vérifie que la valeur est une URL valide
   */
  url: (value: string): ValidationResult => {
    if (!value) return null; // required est géré séparément
    try {
      new URL(value);
      return null;
    } catch {
      return validationMessages.url;
    }
  },

  /**
   * Vérifie que la valeur est un nombre
   */
  number: (value: unknown): ValidationResult => {
    if (value === null || value === undefined || value === '') return null;
    if (typeof value === 'number' && !isNaN(value)) return null;
    if (typeof value === 'string' && !isNaN(Number(value))) return null;
    return validationMessages.number;
  },

  /**
   * Vérifie que la valeur est un entier
   */
  integer: (value: unknown): ValidationResult => {
    if (value === null || value === undefined || value === '') return null;
    const num = typeof value === 'number' ? value : Number(value);
    if (isNaN(num)) return validationMessages.number;
    if (!Number.isInteger(num)) {
      return validationMessages.integer;
    }
    return null;
  },

  /**
   * Vérifie que la valeur est positive
   */
  positive: (value: number): ValidationResult => {
    if (value === null || value === undefined) return null;
    if (typeof value !== 'number' || isNaN(value)) return null;
    if (value <= 0) {
      return validationMessages.positive;
    }
    return null;
  },

  /**
   * Vérifie que la valeur est un numéro de téléphone valide
   */
  phone: (value: string): ValidationResult => {
    if (!value) return null; // required est géré séparément
    const phoneRegex = /^[+]?[(]?[0-9]{3}[)]?[-\s.]?[0-9]{3}[-\s.]?[0-9]{4,6}$/;
    if (!phoneRegex.test(value.replace(/\s+/g, ''))) {
      return validationMessages.phone;
    }
    return null;
  },

  /**
   * Vérifie que la valeur est une date valide
   */
  date: (value: unknown): ValidationResult => {
    if (!value) return null; // required est géré séparément
    if (value instanceof Date && !isNaN(value.getTime())) return null;
    if (typeof value === 'string' && !isNaN(Date.parse(value))) return null;
    return validationMessages.date;
  },

  /**
   * Vérifie que la date est après une date minimale
   */
  dateMin: (min: Date): Validator<Date> => {
    return (value: Date): ValidationResult => {
      if (!value) return null; // required est géré séparément
      const date = value instanceof Date ? value : new Date(value);
      if (isNaN(date.getTime())) return validationMessages.date;
      if (date < min) {
        return validationMessages.dateMin(min);
      }
      return null;
    };
  },

  /**
   * Vérifie que la date est avant une date maximale
   */
  dateMax: (max: Date): Validator<Date> => {
    return (value: Date): ValidationResult => {
      if (!value) return null; // required est géré séparément
      const date = value instanceof Date ? value : new Date(value);
      if (isNaN(date.getTime())) return validationMessages.date;
      if (date > max) {
        return validationMessages.dateMax(max);
      }
      return null;
    };
  },

  /**
   * Vérifie la taille d'un fichier
   */
  fileSize: (maxSize: number): Validator<File | File[]> => {
    return (value: File | File[]): ValidationResult => {
      if (!value) return null; // required est géré séparément
      const files = Array.isArray(value) ? value : [value];
      for (const file of files) {
        if (file.size > maxSize) {
          return validationMessages.fileSize(maxSize);
        }
      }
      return null;
    };
  },

  /**
   * Vérifie le type d'un fichier
   */
  fileType: (allowedTypes: string[]): Validator<File | File[]> => {
    return (value: File | File[]): ValidationResult => {
      if (!value) return null; // required est géré séparément
      const files = Array.isArray(value) ? value : [value];
      for (const file of files) {
        const fileType = file.type || '';
        const fileName = file.name || '';
        const fileExtension = `.${fileName.split('.').pop()?.toLowerCase()}`;

        const isAllowed = allowedTypes.some((type) => {
          if (type.startsWith('.')) {
            return type.toLowerCase() === fileExtension;
          }
          if (type.includes('/')) {
            return (
              fileType === type || fileType.startsWith(`${type.split('/')[0]}/`)
            );
          }
          return false;
        });

        if (!isAllowed) {
          return validationMessages.fileType(allowedTypes);
        }
      }
      return null;
    };
  },
};

/**
 * Combinaison de validators (tous doivent passer)
 */
export function combineValidators<T>(
  ...validators: Validator<T>[]
): Validator<T> {
  return (value: T): ValidationResult => {
    for (const validator of validators) {
      const error = validator(value);
      if (error) {
        return error;
      }
    }
    return null;
  };
}

/**
 * Validation conditionnelle (validator optionnel)
 */
export function optionalValidator<T>(validator: Validator<T>): Validator<T> {
  return (value: T): ValidationResult => {
    if (value === null || value === undefined || value === '') {
      return null; // Si vide, la validation est ignorée
    }
    return validator(value);
  };
}

/**
 * Validation personnalisée avec message d'erreur
 */
export function customValidator<T>(
  validate: (value: T) => boolean,
  errorMessage: string,
): Validator<T> {
  return (value: T): ValidationResult => {
    if (!validate(value)) {
      return errorMessage;
    }
    return null;
  };
}

/**
 * Validation d'objet (pour les formulaires complexes)
 */
export function validateObject<T extends Record<string, unknown>>(
  object: T,
  validators: Record<keyof T, Validator<any>>,
): Record<keyof T, string> | null {
  const errors: Partial<Record<keyof T, string>> = {};
  let hasErrors = false;

  for (const key in validators) {
    if (Object.prototype.hasOwnProperty.call(validators, key)) {
      const validator = validators[key];
      const error = validator(object[key]);
      if (error) {
        errors[key] = error;
        hasErrors = true;
      }
    }
  }

  return hasErrors ? (errors as Record<keyof T, string>) : null;
}

/**
 * Valide un email avec retour détaillé pour validation en temps réel
 * @param email - L'adresse email à valider
 * @returns Objet avec valid (boolean) et message (string optionnel)
 */
export function validateEmail(email: string): {
  valid: boolean;
  message?: string;
} {
  const emailRegex = /^[a-zA-Z0-9._%+-]+@[-a-zA-Z0-9.]+\.[a-zA-Z]{2,}$/;

  if (!email) {
    return { valid: false, message: 'Email is required' };
  }

  if (email.length > 254) {
    return { valid: false, message: 'Email is too long' };
  }

  if (!emailRegex.test(email)) {
    return { valid: false, message: 'Invalid email format' };
  }

  return { valid: true };
}
