import { useState, useCallback, useMemo } from 'react';

type ValidationRule<T> = (value: T, formData?: Record<string, unknown>) => string | undefined;

type ValidationSchema<T extends Record<string, unknown>> = {
  [K in keyof T]?: ValidationRule<T[K]>[];
};

interface UseFormValidationResult<T extends Record<string, unknown>> {
  /** Current validation errors */
  errors: Partial<Record<keyof T, string>>;
  /** Whether the form is valid (no errors) */
  isValid: boolean;
  /** Validate a single field */
  validateField: (field: keyof T, value: T[keyof T]) => string | undefined;
  /** Validate all fields */
  validateForm: (data: T) => boolean;
  /** Set a specific error */
  setError: (field: keyof T, error: string) => void;
  /** Clear a specific error */
  clearError: (field: keyof T) => void;
  /** Clear all errors */
  clearAllErrors: () => void;
  /** Get error for a field */
  getError: (field: keyof T) => string | undefined;
  /** Check if a field has an error */
  hasError: (field: keyof T) => boolean;
}

/**
 * Form validation hook with schema-based validation
 *
 * @example
 * const schema = {
 *   email: [required('Email is required'), email('Invalid email')],
 *   password: [required('Password is required'), minLength(8, 'Password must be at least 8 characters')],
 * };
 *
 * const { errors, validateForm, validateField, isValid } = useFormValidation(schema);
 *
 * const handleSubmit = () => {
 *   if (validateForm(formData)) {
 *     // Submit form
 *   }
 * };
 */
export function useFormValidation<T extends Record<string, unknown>>(
  schema: ValidationSchema<T>
): UseFormValidationResult<T> {
  const [errors, setErrors] = useState<Partial<Record<keyof T, string>>>({});

  const validateField = useCallback(
    (field: keyof T, value: T[keyof T], formData?: T): string | undefined => {
      const rules = schema[field];
      if (!rules) return undefined;

      for (const rule of rules) {
        const error = rule(value, formData as Record<string, unknown>);
        if (error) return error;
      }
      return undefined;
    },
    [schema]
  );

  const validateForm = useCallback(
    (data: T): boolean => {
      const newErrors: Partial<Record<keyof T, string>> = {};
      let isValid = true;

      for (const field of Object.keys(schema) as (keyof T)[]) {
        const error = validateField(field, data[field], data);
        if (error) {
          newErrors[field] = error;
          isValid = false;
        }
      }

      setErrors(newErrors);
      return isValid;
    },
    [schema, validateField]
  );

  const setError = useCallback((field: keyof T, error: string) => {
    setErrors((prev) => ({ ...prev, [field]: error }));
  }, []);

  const clearError = useCallback((field: keyof T) => {
    setErrors((prev) => {
      const newErrors = { ...prev };
      delete newErrors[field];
      return newErrors;
    });
  }, []);

  const clearAllErrors = useCallback(() => {
    setErrors({});
  }, []);

  const getError = useCallback(
    (field: keyof T): string | undefined => errors[field],
    [errors]
  );

  const hasError = useCallback(
    (field: keyof T): boolean => !!errors[field],
    [errors]
  );

  const isValid = useMemo(() => Object.keys(errors).length === 0, [errors]);

  return {
    errors,
    isValid,
    validateField,
    validateForm,
    setError,
    clearError,
    clearAllErrors,
    getError,
    hasError,
  };
}

// ============= Built-in validation rules =============

/**
 * Required field validation
 */
export const required = (message = 'This field is required'): ValidationRule<unknown> => {
  return (value) => {
    if (value === undefined || value === null || value === '') {
      return message;
    }
    if (Array.isArray(value) && value.length === 0) {
      return message;
    }
    return undefined;
  };
};

/**
 * Email validation
 */
export const email = (message = 'Invalid email address'): ValidationRule<string> => {
  return (value) => {
    if (!value) return undefined;
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    return emailRegex.test(value) ? undefined : message;
  };
};

/**
 * Minimum length validation
 */
export const minLength = (min: number, message?: string): ValidationRule<string> => {
  return (value) => {
    if (!value) return undefined;
    return value.length >= min
      ? undefined
      : message || `Must be at least ${min} characters`;
  };
};

/**
 * Maximum length validation
 */
export const maxLength = (max: number, message?: string): ValidationRule<string> => {
  return (value) => {
    if (!value) return undefined;
    return value.length <= max
      ? undefined
      : message || `Must be at most ${max} characters`;
  };
};

/**
 * Minimum value validation (for numbers)
 */
export const minValue = (min: number, message?: string): ValidationRule<number> => {
  return (value) => {
    if (value === undefined || value === null) return undefined;
    return value >= min
      ? undefined
      : message || `Must be at least ${min}`;
  };
};

/**
 * Maximum value validation (for numbers)
 */
export const maxValue = (max: number, message?: string): ValidationRule<number> => {
  return (value) => {
    if (value === undefined || value === null) return undefined;
    return value <= max
      ? undefined
      : message || `Must be at most ${max}`;
  };
};

/**
 * Pattern/regex validation
 */
export const pattern = (regex: RegExp, message = 'Invalid format'): ValidationRule<string> => {
  return (value) => {
    if (!value) return undefined;
    return regex.test(value) ? undefined : message;
  };
};

/**
 * URL validation
 */
export const url = (message = 'Invalid URL'): ValidationRule<string> => {
  return (value) => {
    if (!value) return undefined;
    try {
      new URL(value);
      return undefined;
    } catch {
      return message;
    }
  };
};

/**
 * Match another field (e.g., confirm password)
 */
export const matches = <T extends Record<string, unknown>>(
  fieldName: keyof T,
  message = 'Fields must match'
): ValidationRule<unknown> => {
  return (value, formData) => {
    if (!formData) return undefined;
    return value === formData[fieldName as string] ? undefined : message;
  };
};

/**
 * Phone number validation (basic)
 */
export const phone = (message = 'Invalid phone number'): ValidationRule<string> => {
  return (value) => {
    if (!value) return undefined;
    const phoneRegex = /^[+]?[(]?[0-9]{1,4}[)]?[-\s./0-9]*$/;
    return phoneRegex.test(value) ? undefined : message;
  };
};

export default useFormValidation;
