/**
 * Enhanced Form Hook with Advanced Error Handling
 * 
 * Provides comprehensive form state management, validation, and error handling
 * with both user-friendly and developer-friendly error messages.
 */

import { useState, useCallback, useEffect, FormEvent, ChangeEvent, Dispatch, SetStateAction } from 'react';
import { parseApiError, formatValidationErrorsForForm, logErrorWithContext } from '@/lib/utils/errorUtils';

export interface FormField<T> {
  value: T;
  error?: string;
  touched: boolean;
  validating: boolean;
}

export interface FormState<T extends Record<string, unknown>> {
  fields: { [K in keyof T]: FormField<T[K]> };
  isValid: boolean;
  isSubmitting: boolean;
  submitError?: string;
  submitCount: number;
}

export interface FormOptions<T extends Record<string, unknown>> {
  initialValues: T;
  validate?: (values: T) => Record<string, string> | Promise<Record<string, string>>;
  onSubmit: (values: T) => Promise<void>;
  onError?: (error: unknown, values: T) => void;
  onSuccess?: (values: T) => void;
  component?: string;
}

export interface UseEnhancedFormReturn<T extends Record<string, unknown>> {
  formState: FormState<T>;
  setFormState: Dispatch<SetStateAction<FormState<T>>>;
  handleChange: (e: ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>) => void;
  setFieldValue: <K extends keyof T>(field: K, value: T[K]) => void;
  setFieldError: <K extends keyof T>(field: K, error: string) => void;
  handleSubmit: (e: FormEvent<HTMLFormElement>) => Promise<void>;
  reset: () => void;
  setFormError: (error: string) => void;
  clearFieldError: <K extends keyof T>(field: K) => void;
  validateField: <K extends keyof T>(field: K) => Promise<void>;
}

/**
 * Enhanced form hook with comprehensive error handling
 */
export function useEnhancedForm<T extends Record<string, unknown>>(
  options: FormOptions<T>
): UseEnhancedFormReturn<T> {
  const [formState, setFormState] = useState<FormState<T>>(() => ({
    fields: Object.keys(options.initialValues).reduce((acc, key) => ({
      ...acc,
      [key]: {
        value: options.initialValues[key as keyof T],
        error: undefined,
        touched: false,
        validating: false,
      }
    }), {} as { [K in keyof T]: FormField<T[K]> }),
    isValid: true,
    isSubmitting: false,
    submitError: undefined,
    submitCount: 0,
  }));

  const getValues = useCallback((): T => {
    return Object.keys(formState.fields).reduce((acc, key) => ({
      ...acc,
      [key]: formState.fields[key as keyof T].value,
    }), {} as T);
  }, [formState.fields]);

  // Validate form on change
  useEffect(() => {
    if (options.validate && formState.submitCount > 0) {
      const validateAsync = async () => {
        try {
          const errors = await options.validate!(getValues());
          setFormState(prev => ({
            ...prev,
            isValid: Object.keys(errors).length === 0,
            fields: Object.keys(prev.fields).reduce((acc, key) => ({
              ...acc,
              [key]: {
                ...prev.fields[key as keyof T],
                error: errors[key],
              }
            }), {} as { [K in keyof T]: FormField<T[K]> }),
          }));
        } catch (error) {
          console.error('Validation error:', error);
        }
      };
      validateAsync();
    }
  }, [formState.fields, formState.submitCount, options.validate, getValues]);

  const handleChange = useCallback((e: ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>) => {
    const { name, value, type } = e.target;
    const checked = 'checked' in e.target ? (e.target as HTMLInputElement).checked : false;
    const fieldValue = type === 'checkbox' ? checked : value;
    
    setFormState(prev => ({
      ...prev,
      fields: {
        ...prev.fields,
        [name]: {
          ...prev.fields[name as keyof T],
          value: fieldValue as T[keyof T],
          touched: true,
          error: undefined, // Clear error on change
        },
      },
    }));
  }, []);

  const setFieldValue = useCallback(<K extends keyof T>(field: K, value: T[K]) => {
    setFormState(prev => ({
      ...prev,
      fields: {
        ...prev.fields,
        [field]: {
          ...prev.fields[field],
          value,
          touched: true,
          error: undefined,
        },
      },
    }));
  }, []);

  const setFieldError = useCallback(<K extends keyof T>(field: K, error: string) => {
    setFormState(prev => ({
      ...prev,
      fields: {
        ...prev.fields,
        [field]: {
          ...prev.fields[field],
          error,
          touched: true,
        },
      },
      isValid: false,
    }));
  }, []);

  const clearFieldError = useCallback(<K extends keyof T>(field: K) => {
    setFormState(prev => ({
      ...prev,
      fields: {
        ...prev.fields,
        [field]: {
          ...prev.fields[field],
          error: undefined,
        },
      },
    }));
  }, []);

  const setFormError = useCallback((error: string) => {
    setFormState(prev => ({
      ...prev,
      submitError: error,
    }));
  }, []);

  const validateField = useCallback(async <K extends keyof T>(field: K) => {
    if (!options.validate) return;
    
    setFormState(prev => ({
      ...prev,
      fields: {
        ...prev.fields,
        [field]: {
          ...prev.fields[field],
          validating: true,
        },
      },
    }));

    try {
      const values = getValues();
      const errors = await options.validate(values);
      
      setFormState(prev => ({
        ...prev,
        fields: {
          ...prev.fields,
          [field]: {
            ...prev.fields[field],
            error: errors[field as string],
            validating: false,
          },
        },
      }));
    } catch (error) {
      setFormState(prev => ({
        ...prev,
        fields: {
          ...prev.fields,
          [field]: {
            ...prev.fields[field],
            validating: false,
          },
        },
      }));
    }
  }, [options.validate, getValues]);

  const reset = useCallback(() => {
    setFormState({
      fields: Object.keys(options.initialValues).reduce((acc, key) => ({
        ...acc,
        [key]: {
          value: options.initialValues[key as keyof T],
          error: undefined,
          touched: false,
          validating: false,
        }
      }), {} as { [K in keyof T]: FormField<T[K]> }),
      isValid: true,
      isSubmitting: false,
      submitError: undefined,
      submitCount: 0,
    });
  }, [options.initialValues]);

  const handleSubmit = useCallback(async (e: FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    
    const values = getValues();
    
    // Increment submit count to trigger validation
    setFormState(prev => ({
      ...prev,
      submitCount: prev.submitCount + 1,
      isSubmitting: true,
      submitError: undefined,
    }));

    // Run validation if provided
    if (options.validate) {
      try {
        const errors = await options.validate(values);
        const hasErrors = Object.keys(errors).length > 0;
        
        if (hasErrors) {
          setFormState(prev => ({
            ...prev,
            isSubmitting: false,
            isValid: false,
            fields: Object.keys(prev.fields).reduce((acc, key) => ({
              ...acc,
              [key]: {
                ...prev.fields[key as keyof T],
                error: errors[key],
                touched: true,
              }
            }), {} as { [K in keyof T]: FormField<T[K]> }),
          }));
          return;
        }
      } catch (validationError) {
        logErrorWithContext(validationError, {
          timestamp: new globalThis.Date().toISOString(),
          component: options.component || 'useEnhancedForm',
          action: 'validate',
          formData: values,
        });
        
        setFormState(prev => ({
          ...prev,
          isSubmitting: false,
          submitError: 'Validation failed. Please check your input.',
        }));
        return;
      }
    }

    // Submit the form
    try {
      await options.onSubmit(values);
      
      setFormState(prev => ({
        ...prev,
        isSubmitting: false,
        submitError: undefined,
      }));
      
      options.onSuccess?.(values);
    } catch (error) {
      const parsed = parseApiError(error);
      
      // Log for developers
      logErrorWithContext(error, {
        timestamp: new globalThis.Date().toISOString(),
        component: options.component || 'useEnhancedForm',
        action: 'submit',
        formData: values,
      });

      // Handle validation errors from API
      if (parsed.isValidationError && parsed.validationErrors.length > 0) {
        const fieldErrors = formatValidationErrorsForForm(parsed.validationErrors);
        
        setFormState(prev => ({
          ...prev,
          isSubmitting: false,
          isValid: false,
          fields: Object.keys(prev.fields).reduce((acc, key) => ({
            ...acc,
            [key]: {
              ...prev.fields[key as keyof T],
              error: fieldErrors[key],
              touched: true,
            }
          }), {} as { [K in keyof T]: FormField<T[K]> }),
        }));
      } else {
        // General submit error
        setFormState(prev => ({
          ...prev,
          isSubmitting: false,
          submitError: parsed.userMessage,
        }));
      }
      
      options.onError?.(error, values);
    }
  }, [getValues, options]);

  return {
    formState,
    setFormState,
    handleChange,
    setFieldValue,
    setFieldError,
    handleSubmit,
    reset,
    setFormError,
    clearFieldError,
    validateField,
  };
}