import { useState, useCallback } from 'react';

export interface ValidationRule {
  required?: boolean;
  min?: number;
  max?: number;
  pattern?: RegExp;
  email?: boolean;
  url?: boolean;
  custom?: (value: any) => string | null;
  message?: string;
}

export interface ValidationRules {
  [field: string]: ValidationRule | ValidationRule[];
}

export interface ValidationErrors {
  [field: string]: string;
}

export interface UseFormValidationResult<T> {
  values: T;
  errors: ValidationErrors;
  touched: { [K in keyof T]?: boolean };
  isValid: boolean;
  isSubmitting: boolean;
  setValue: (field: keyof T, value: any) => void;
  setValues: (values: Partial<T>) => void;
  setError: (field: keyof T, error: string) => void;
  setErrors: (errors: ValidationErrors) => void;
  clearError: (field: keyof T) => void;
  clearErrors: () => void;
  touch: (field: keyof T) => void;
  validate: () => boolean;
  validateField: (field: keyof T) => boolean;
  handleChange: (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) => void;
  handleBlur: (e: React.FocusEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) => void;
  handleSubmit: (onSubmit: (values: T) => void | Promise<void>) => (e: React.FormEvent) => Promise<void>;
  reset: () => void;
}

/**
 * Hook for form validation with built-in rules
 * Supports required, min/max length, patterns, email, URL, and custom validators
 * 
 * @example
 * const { values, errors, handleChange, handleSubmit } = useFormValidation({
 *   title: '',
 *   email: ''
 * }, {
 *   title: { required: true, min: 3, max: 100 },
 *   email: { required: true, email: true }
 * });
 */
export function useFormValidation<T extends Record<string, any>>(
  initialValues: T,
  rules: ValidationRules = {}
): UseFormValidationResult<T> {
  const [values, setValuesState] = useState<T>(initialValues);
  const [errors, setErrorsState] = useState<ValidationErrors>({});
  const [touched, setTouched] = useState<{ [K in keyof T]?: boolean }>({});
  const [isSubmitting, setIsSubmitting] = useState(false);

  const validateValue = useCallback(
    (field: keyof T, value: any): string | null => {
      const fieldRules = rules[field as string];
      if (!fieldRules) return null;

      const rulesArray = Array.isArray(fieldRules) ? fieldRules : [fieldRules];

      for (const rule of rulesArray) {
        // Required
        if (rule.required && (!value || (typeof value === 'string' && !value.trim()))) {
          return rule.message || `${String(field)} is required`;
        }

        // Skip other validations if empty and not required
        if (!value || (typeof value === 'string' && !value.trim())) {
          continue;
        }

        // Min length
        if (rule.min !== undefined && typeof value === 'string' && value.length < rule.min) {
          return rule.message || `${String(field)} must be at least ${rule.min} characters`;
        }

        // Max length
        if (rule.max !== undefined && typeof value === 'string' && value.length > rule.max) {
          return rule.message || `${String(field)} must be at most ${rule.max} characters`;
        }

        // Pattern
        if (rule.pattern && typeof value === 'string' && !rule.pattern.test(value)) {
          return rule.message || `${String(field)} format is invalid`;
        }

        // Email
        if (rule.email && typeof value === 'string') {
          const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
          if (!emailRegex.test(value)) {
            return rule.message || `${String(field)} must be a valid email`;
          }
        }

        // URL
        if (rule.url && typeof value === 'string') {
          try {
            new URL(value);
          } catch {
            return rule.message || `${String(field)} must be a valid URL`;
          }
        }

        // Custom validator
        if (rule.custom) {
          const customError = rule.custom(value);
          if (customError) return customError;
        }
      }

      return null;
    },
    [rules]
  );

  const validateField = useCallback(
    (field: keyof T): boolean => {
      const error = validateValue(field, values[field]);
      if (error) {
        setErrorsState((prev) => ({ ...prev, [field]: error }));
        return false;
      } else {
        setErrorsState((prev) => {
          const newErrors = { ...prev };
          delete newErrors[field as string];
          return newErrors;
        });
        return true;
      }
    },
    [values, validateValue]
  );

  const validate = useCallback((): boolean => {
    const newErrors: ValidationErrors = {};
    let isValid = true;

    Object.keys(rules).forEach((field) => {
      const error = validateValue(field as keyof T, values[field as keyof T]);
      if (error) {
        newErrors[field] = error;
        isValid = false;
      }
    });

    setErrorsState(newErrors);
    return isValid;
  }, [rules, values, validateValue]);

  const setValue = useCallback((field: keyof T, value: any) => {
    setValuesState((prev) => ({ ...prev, [field]: value }));
  }, []);

  const setValues = useCallback((newValues: Partial<T>) => {
    setValuesState((prev) => ({ ...prev, ...newValues }));
  }, []);

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

  const setErrors = useCallback((newErrors: ValidationErrors) => {
    setErrorsState(newErrors);
  }, []);

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

  const clearErrors = useCallback(() => {
    setErrorsState({});
  }, []);

  const touch = useCallback((field: keyof T) => {
    setTouched((prev) => ({ ...prev, [field]: true }));
  }, []);

  const handleChange = useCallback(
    (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) => {
      const { name, value, type } = e.target;
      const fieldValue = type === 'checkbox' ? (e.target as HTMLInputElement).checked : value;
      setValue(name as keyof T, fieldValue);

      // Clear error on change if field was touched
      if (touched[name as keyof T]) {
        clearError(name as keyof T);
      }
    },
    [setValue, clearError, touched]
  );

  const handleBlur = useCallback(
    (e: React.FocusEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) => {
      const { name } = e.target;
      touch(name as keyof T);
      validateField(name as keyof T);
    },
    [touch, validateField]
  );

  const handleSubmit = useCallback(
    (onSubmit: (values: T) => void | Promise<void>) => {
      return async (e: React.FormEvent) => {
        e.preventDefault();
        setIsSubmitting(true);

        // Mark all fields as touched
        const allTouched = Object.keys(rules).reduce(
          (acc, key) => ({ ...acc, [key]: true }),
          {}
        );
        setTouched(allTouched);

        if (validate()) {
          try {
            await onSubmit(values);
          } finally {
            setIsSubmitting(false);
          }
        } else {
          setIsSubmitting(false);
        }
      };
    },
    [validate, values, rules]
  );

  const reset = useCallback(() => {
    setValuesState(initialValues);
    setErrorsState({});
    setTouched({});
    setIsSubmitting(false);
  }, [initialValues]);

  const isValid = Object.keys(errors).length === 0;

  return {
    values,
    errors,
    touched,
    isValid,
    isSubmitting,
    setValue,
    setValues,
    setError,
    setErrors,
    clearError,
    clearErrors,
    touch,
    validate,
    validateField,
    handleChange,
    handleBlur,
    handleSubmit,
    reset,
  };
}
