/**
 * Generic API Request Hook
 *
 * Reusable hook for API requests with built-in:
 * - Loading/error state management
 * - CSRF token fetching
 * - Automatic retry on failure
 * - Type-safe response handling
 *
 * @module lib/hooks/useApiRequest
 * @example
 * ```typescript
 * const { execute, loading, error, data } = useApiRequest<PreferencesResponse>();
 *
 * const savePreferences = async () => {
 *   await execute('/api/user/preferences', {
 *     method: 'PUT',
 *     body: JSON.stringify(preferences),
 *   });
 * };
 * ```
 */

'use client';

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

/**
 * API request configuration options
 */
export interface ApiRequestOptions extends RequestInit {
  /** Whether to include CSRF token in headers (default: true for PUT/POST/DELETE) */
  includeCsrfToken?: boolean;
  /** Enable automatic retry on network failure (default: false) */
  enableRetry?: boolean;
  /** Maximum number of retry attempts (default: 2) */
  maxRetries?: number;
  /** Delay between retries in milliseconds (default: 1000) */
  retryDelay?: number;
  /** Callback fired before request is sent */
  onRequest?: () => void;
  /** Callback fired after successful response */
  onSuccess?: (data: any) => void;
  /** Callback fired on error */
  onError?: (error: Error) => void;
}

/**
 * API response structure
 */
export interface ApiResponse<T = any> {
  success: boolean;
  data?: T;
  error?: string;
  message?: string;
  [key: string]: any;
}

/**
 * Hook return value
 */
export interface UseApiRequestReturn<T = any> {
  /** Execute the API request */
  execute: (url: string, options?: ApiRequestOptions) => Promise<T | null>;
  /** Loading state */
  loading: boolean;
  /** Error message (if any) */
  error: string | null;
  /** Response data (if successful) */
  data: T | null;
  /** Clear error state */
  clearError: () => void;
  /** Reset all state to initial values */
  reset: () => void;
}

/**
 * Generic API request hook with automatic CSRF handling and retry logic
 *
 * @template T - Expected response data type
 * @returns Hook utilities for making API requests
 *
 * @example
 * ```typescript
 * // Basic usage
 * const { execute, loading, error } = useApiRequest<{ userId: string }>();
 *
 * const handleSubmit = async () => {
 *   const result = await execute('/api/user/update', {
 *     method: 'PUT',
 *     body: JSON.stringify({ name: 'John' }),
 *   });
 *
 *   if (result) {
 *     console.log('User ID:', result.userId);
 *   }
 * };
 * ```
 *
 * @example
 * ```typescript
 * // With retry and callbacks
 * const { execute } = useApiRequest<PreferencesData>({
 *   enableRetry: true,
 *   maxRetries: 3,
 *   onSuccess: (data) => toast.success('Saved!'),
 *   onError: (err) => toast.error(err.message),
 * });
 * ```
 */
export function useApiRequest<T = any>(): UseApiRequestReturn<T> {
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [data, setData] = useState<T | null>(null);

  // Track CSRF token to avoid refetching on every request
  const csrfTokenRef = useRef<string | null>(null);
  const csrfTokenFetchedAtRef = useRef<number>(0);

  /**
   * Fetch CSRF token from server
   * Caches token for 5 minutes to reduce requests
   */
  const fetchCsrfToken = useCallback(async (): Promise<string> => {
    const now = Date.now();
    const CSRF_CACHE_DURATION = 5 * 60 * 1000; // 5 minutes

    // Return cached token if still valid
    if (
      csrfTokenRef.current &&
      now - csrfTokenFetchedAtRef.current < CSRF_CACHE_DURATION
    ) {
      return csrfTokenRef.current;
    }

    // Fetch new token
    try {
      const response = await fetch('/api/csrf');
      if (!response.ok) {
        throw new Error('Failed to fetch CSRF token');
      }
      const { csrfToken } = await response.json();

      // Cache token
      csrfTokenRef.current = csrfToken;
      csrfTokenFetchedAtRef.current = now;

      return csrfToken;
    } catch (err) {
      console.error('[useApiRequest] CSRF token fetch failed:', err);
      throw new Error('Security token unavailable. Please refresh the page.');
    }
  }, []);

  /**
   * Execute API request with retry logic
   */
  const executeWithRetry = useCallback(
    async (
      url: string,
      options: ApiRequestOptions,
      attempt: number = 1
    ): Promise<Response> => {
      try {
        return await fetch(url, options);
      } catch (err) {
        const maxRetries = options.maxRetries || 2;
        const shouldRetry = options.enableRetry && attempt < maxRetries;

        if (shouldRetry) {
          // Wait before retrying (exponential backoff)
          const delay = (options.retryDelay || 1000) * attempt;
          await new Promise(resolve => setTimeout(resolve, delay));

          console.log(`[useApiRequest] Retrying request (attempt ${attempt + 1}/${maxRetries})`);
          return executeWithRetry(url, options, attempt + 1);
        }

        throw err;
      }
    },
    []
  );

  /**
   * Main execute function
   */
  const execute = useCallback(
    async (url: string, options: ApiRequestOptions = {}): Promise<T | null> => {
      setLoading(true);
      setError(null);

      // Trigger onRequest callback
      options.onRequest?.();

      try {
        // Determine if CSRF token is needed
        const method = options.method?.toUpperCase() || 'GET';
        const needsCsrf = options.includeCsrfToken !== false &&
                         ['PUT', 'POST', 'DELETE', 'PATCH'].includes(method);

        // Build headers
        const headers: Record<string, string> = {
          'Content-Type': 'application/json',
          ...(options.headers as Record<string, string> || {}),
        };

        // Add CSRF token if needed
        if (needsCsrf) {
          try {
            const csrfToken = await fetchCsrfToken();
            headers['x-csrf-token'] = csrfToken;
          } catch (csrfError) {
            throw new Error(
              csrfError instanceof Error
                ? csrfError.message
                : 'Failed to obtain security token'
            );
          }
        }

        // Execute request with retry logic if enabled
        const response = await executeWithRetry(url, {
          ...options,
          headers,
        });

        // Parse response
        const responseData: ApiResponse<T> = await response.json();

        // Handle error responses
        if (!response.ok) {
          const errorMessage =
            responseData.error ||
            responseData.message ||
            `Request failed with status ${response.status}`;

          throw new Error(errorMessage);
        }

        // Handle unsuccessful operations
        if (responseData.success === false) {
          throw new Error(responseData.error || 'Operation failed');
        }

        // Extract data (handle different response structures)
        const resultData = responseData.data !== undefined
          ? responseData.data
          : responseData;

        setData(resultData as T);

        // Trigger onSuccess callback
        options.onSuccess?.(resultData);

        return resultData as T;
      } catch (err) {
        const errorMessage =
          err instanceof Error
            ? err.message
            : 'An unexpected error occurred';

        setError(errorMessage);

        // Trigger onError callback
        if (err instanceof Error) {
          options.onError?.(err);
        }

        console.error('[useApiRequest] Request failed:', err);
        return null;
      } finally {
        setLoading(false);
      }
    },
    [fetchCsrfToken, executeWithRetry]
  );

  /**
   * Clear error state
   */
  const clearError = useCallback(() => {
    setError(null);
  }, []);

  /**
   * Reset all state to initial values
   */
  const reset = useCallback(() => {
    setLoading(false);
    setError(null);
    setData(null);
  }, []);

  return {
    execute,
    loading,
    error,
    data,
    clearError,
    reset,
  };
}

/**
 * Convenience hook for GET requests
 *
 * @template T - Expected response data type
 * @param url - API endpoint URL
 * @param options - Request options
 * @returns Hook utilities
 *
 * @example
 * ```typescript
 * const { data, loading, error, refetch } = useApiGet<User[]>('/api/users');
 *
 * useEffect(() => {
 *   refetch();
 * }, []);
 * ```
 */
export function useApiGet<T = any>(
  url: string,
  options: Omit<ApiRequestOptions, 'method'> = {}
) {
  const { execute, ...rest } = useApiRequest<T>();

  const refetch = useCallback(() => {
    return execute(url, { ...options, method: 'GET' });
  }, [execute, url, options]);

  return {
    ...rest,
    refetch,
  };
}

/**
 * Convenience hook for POST requests
 *
 * @template T - Expected response data type
 * @param url - API endpoint URL
 * @returns Hook utilities
 *
 * @example
 * ```typescript
 * const { submit, loading } = useApiPost<{ id: string }>('/api/bookings');
 *
 * const handleCreate = async (data: BookingData) => {
 *   const result = await submit(data);
 * };
 * ```
 */
export function useApiPost<T = any>(url: string) {
  const { execute, ...rest } = useApiRequest<T>();

  const submit = useCallback(
    (body: any, options: Omit<ApiRequestOptions, 'method' | 'body'> = {}) => {
      return execute(url, {
        ...options,
        method: 'POST',
        body: JSON.stringify(body),
      });
    },
    [execute, url]
  );

  return {
    ...rest,
    submit,
  };
}

/**
 * Convenience hook for PUT requests
 *
 * @template T - Expected response data type
 * @param url - API endpoint URL
 * @returns Hook utilities
 *
 * @example
 * ```typescript
 * const { update, loading } = useApiPut<Preferences>('/api/user/preferences');
 *
 * const handleSave = async (prefs: Preferences) => {
 *   await update(prefs);
 * };
 * ```
 */
export function useApiPut<T = any>(url: string) {
  const { execute, ...rest } = useApiRequest<T>();

  const update = useCallback(
    (body: any, options: Omit<ApiRequestOptions, 'method' | 'body'> = {}) => {
      return execute(url, {
        ...options,
        method: 'PUT',
        body: JSON.stringify(body),
      });
    },
    [execute, url]
  );

  return {
    ...rest,
    update,
  };
}

/**
 * Convenience hook for DELETE requests
 *
 * @template T - Expected response data type
 * @param url - API endpoint URL
 * @returns Hook utilities
 *
 * @example
 * ```typescript
 * const { remove, loading } = useApiDelete('/api/bookings');
 *
 * const handleDelete = async (id: string) => {
 *   await remove(`/api/bookings/${id}`);
 * };
 * ```
 */
export function useApiDelete<T = any>(url?: string) {
  const { execute, ...rest } = useApiRequest<T>();

  const remove = useCallback(
    (deleteUrl?: string, options: Omit<ApiRequestOptions, 'method'> = {}) => {
      const targetUrl = deleteUrl || url;
      if (!targetUrl) {
        throw new Error('URL is required for DELETE request');
      }

      return execute(targetUrl, {
        ...options,
        method: 'DELETE',
      });
    },
    [execute, url]
  );

  return {
    ...rest,
    remove,
  };
}
