/**
 * Custom SWR hooks for data fetching and mutations
 *
 * Common SWR Configuration Options:
 *
 * 1. revalidateOnFocus (default: true)
 *    - Refetches data when user focuses the browser tab
 *    - Use case: Live dashboards, real-time data
 *    - Example: Stock prices, notifications, chat messages
 *
 * 2. revalidateOnReconnect (default: true)
 *    - Refetches when internet connection is restored
 *    - Use case: Mobile apps, unstable connections
 *    - Example: Ensure data is fresh after reconnection
 *
 * 3. refreshInterval (default: 0)
 *    - Auto-refresh data every X milliseconds
 *    - Use case: Polling for updates
 *    - Example: refreshInterval: 5000 // Poll every 5 seconds
 *
 * 4. dedupingInterval (default: 2000ms)
 *    - Prevents duplicate requests within time window
 *    - Use case: Multiple components fetching same data
 *    - Example: dedupingInterval: 5000 // 5 second deduplication
 *
 * 5. errorRetryCount (default: 5)
 *    - Number of retry attempts on error
 *    - Use case: Handling flaky APIs
 *    - Example: errorRetryCount: 3
 *
 * 6. errorRetryInterval (default: 5000ms)
 *    - Delay between retry attempts
 *    - Use case: Rate-limited APIs
 *    - Example: errorRetryInterval: 10000 // 10 second delay
 *
 * 7. shouldRetryOnError (default: true)
 *    - Whether to retry on error
 *    - Use case: 404 errors shouldn't retry
 *    - Example: shouldRetryOnError: (err) => err.status !== 404
 *
 * 8. revalidateIfStale (default: true)
 *    - Refetch if data is marked as stale
 *    - Use case: Control when stale data refetches
 *    - Example: revalidateIfStale: false // Use cache even if stale
 *
 * 9. revalidateOnMount (default: true)
 *    - Refetch when component mounts
 *    - Use case: Static data that rarely changes
 *    - Example: revalidateOnMount: false // Don't refetch on mount
 *
 * 10. keepPreviousData (default: false)
 *     - Keep showing old data while fetching new data
 *     - Use case: Pagination, smooth transitions
 *     - Example: keepPreviousData: true // Show old page while loading new
 *
 * Real-world examples:
 *
 * // Live dashboard (refetch on focus, poll every 10s)
 * useQuery('/api/analytics', {}, {
 *   revalidateOnFocus: true,
 *   refreshInterval: 10000
 * });
 *
 * // Static user profile (minimal refetching)
 * useQuery('/api/user/profile', {}, {
 *   revalidateOnFocus: false,
 *   revalidateOnReconnect: false
 * });
 *
 * // Search results (keep previous while loading new)
 * useQuery('/api/search', { q: query }, {
 *   keepPreviousData: true
 * });
 *
 * // Critical data with retries
 * useQuery('/api/payment/status', {}, {
 *   errorRetryCount: 10,
 *   errorRetryInterval: 2000
 * });
 */

import { ResponseType } from "axios";
import { useMemo, useState } from "react";
import { toast } from "react-toastify";
import useSWR, { SWRConfiguration, mutate } from "swr";
import useSWRMutation from "swr/mutation";
import { api } from "./config";

type QueryParams = Record<string, string | number | boolean | undefined | null>;

interface UseQueryOptions extends SWRConfiguration {
  skip?: boolean;
}

export function useQuery<TData = unknown>(
  url: string | null,
  params?: QueryParams,
  options: UseQueryOptions = {}
) {
  const { skip, ...swrOptions } = options;

  const {
    data,
    error,
    isLoading,
    isValidating,
    mutate: refetch,
  } = useSWR<{ data: TData }>(
    url && !skip ? [url, params] : null,
    ([url, params]) => api.get(url, { params }),
    {
      revalidateOnFocus: false,
      ...swrOptions,
    }
  );

  return {
    data: data?.data,
    error,
    isLoading,
    isValidating,
    refetch,
  };
}

interface PaginatorResponse {
  current_page: number;
  per_page: number;
  total_count: number;
}

interface PaginatedData<TItem> {
  list?: TItem[];
  paginator?: PaginatorResponse;
}

export function useQueryPagination<TItem = unknown>(
  url: string | null,
  params?: QueryParams,
  options: UseQueryOptions = {}
) {
  const [page, setPage] = useState(1);
  const [pageSize, setPageSize] = useState((params?.per_page as number) || 10);

  // Stringify params to avoid object reference changes causing re-renders
  const paramsString = JSON.stringify(params);

  const queryParams = useMemo(
    () => ({
      page,
      per_page: pageSize,
      ...(params || {}),
    }),
    // eslint-disable-next-line react-hooks/exhaustive-deps
    [page, pageSize, paramsString]
  );

  const swr = useQuery<PaginatedData<TItem>>(url, queryParams, options);

  const paginationProps = useMemo(() => {
    const paginator = (swr.data as PaginatedData<TItem>)?.paginator;

    return {
      currentPage: paginator?.current_page ?? page,
      pageSize: paginator?.per_page ?? pageSize,
      total: paginator?.total_count ?? 0,
      onChange: (newPage: number, newPageSize?: number) => {
        setPage(newPage);
        if (newPageSize && newPageSize !== pageSize) {
          setPageSize(newPageSize);
        }
      },
    };
  }, [swr.data, page, pageSize]);

  const dataWithPaginator = swr.data as PaginatedData<TItem>;
  const dataArray = dataWithPaginator?.list ?? (swr.data as TItem[]) ?? [];

  return {
    ...swr,
    pagination: paginationProps,
    data: dataArray,
    paginator: dataWithPaginator?.paginator,
  };
}

interface MutationResponse {
  message?: string;
  data?: unknown;
}

/**
 * useMutation - Hook for Create/Update/Delete operations
 *
 * This hook uses useSWRMutation for each operation, which provides:
 * - Built-in loading states (isMutating)
 * - Automatic cache invalidation
 * - Error handling
 * - Optimistic updates support
 *
 * Why use useSWRMutation instead of manual useState(false)?
 * ✅ Automatic loading state management
 * ✅ Built-in error state
 * ✅ Better integration with SWR cache
 * ✅ Supports optimistic updates
 * ✅ Prevents race conditions
 *
 * Example usage:
 * const { create, update, remove, isLoading } = useMutation<ApiResponse, User>('/api/users');
 * await create({ name: 'John' });
 * await update({ name: 'Jane' }, userId);
 * await remove(userId);
 */

export function useMutation<
  TResponse extends MutationResponse = MutationResponse,
  TData = unknown
>(url: string, responseType: ResponseType = "json") {
  // Create operation using useSWRMutation
  const createMutation = useSWRMutation(
    url,
    async (_key: string, { arg }: { arg: TData }) => {
      try {
        const response = await api.post<TResponse>(url, arg, { responseType });
        await mutate(url); // Invalidate cache
        if (response.data?.message) {
          toast.success(response.data.message);
        }
        return response;
      } catch (error) {
        console.error("Error creating resource:", error);
        if (error instanceof Error) {
          toast.error(error.message || "Failed to create resource");
        } else {
          toast.error("An unexpected error occurred");
        }
        throw error;
      }
    }
  );

  // Update operation using useSWRMutation
  const updateMutation = useSWRMutation(
    url,
    async (
      _key: string,
      { arg }: { arg: { data: Partial<TData>; id: string | number } }
    ) => {
      try {
        const response = await api.post<TResponse>(`${url}/${arg.id}`, {
          ...arg.data,
          _method: "PUT",
        });
        await mutate(url); // Invalidate cache
        if (response.data?.message) {
          toast.success(response.data.message);
        }
        return response;
      } catch (error) {
        console.error("Error updating resource:", error);
        if (error instanceof Error) {
          toast.error(error.message || "Failed to update resource");
        } else {
          toast.error("An unexpected error occurred");
        }
        throw error;
      }
    }
  );

  // Delete operation using useSWRMutation
  const deleteMutation = useSWRMutation(
    url,
    async (_key: string, { arg }: { arg: string | number }) => {
      try {
        const response = await api.delete<TResponse>(`${url}/${arg}`);
        await mutate(url); // Invalidate cache
        if (response.data?.message) {
          toast.success(response.data.message);
        }
        return response;
      } catch (error) {
        console.error("Error deleting resource:", error);
        if (error instanceof Error) {
          toast.error(error.message || "Failed to delete resource");
        } else {
          toast.error("An unexpected error occurred");
        }
        throw error;
      }
    }
  );

  return {
    create: createMutation.trigger,
    update: (data: Partial<TData>, id: string | number) =>
      updateMutation.trigger({ data, id }),
    remove: deleteMutation.trigger,
    // isLoading is true if ANY mutation is in progress
    isLoading:
      createMutation.isMutating ||
      updateMutation.isMutating ||
      deleteMutation.isMutating,
    // Individual loading states if needed
    isCreating: createMutation.isMutating,
    isUpdating: updateMutation.isMutating,
    isDeleting: deleteMutation.isMutating,
    // Error states
    error: createMutation.error || updateMutation.error || deleteMutation.error,
  } as const;
}
