/**
 * TanStack Query Hooks for Layouts
 *
 * Provides React hooks for fetching and mutating layout data
 * with automatic caching, refetching, and optimistic updates.
 */

import { useQuery, useMutation, useQueryClient, type UseQueryOptions, type UseMutationOptions } from "@tanstack/react-query";

import {
  getLayouts,
  getLayout,
  createLayout,
  updateLayout,
  deleteLayout,
  duplicateLayout,
  getActiveLayouts,
  activateLayoutForLocation,
  getLocations,
  type Layout,
  type CreateLayoutData,
  type UpdateLayoutData,
  type ActivateLocationData,
  type LayoutsQueryParams,
  type PaginatedResponse,
  type AllActiveLayouts,
  type AvailableLocations,
  type ApiError,
} from "@/api/layouts";

// Query keys factory for consistent cache key management
export const layoutKeys = {
  all: ["layouts"] as const,
  lists: () => [...layoutKeys.all, "list"] as const,
  list: (params: LayoutsQueryParams) => [...layoutKeys.lists(), params] as const,
  details: () => [...layoutKeys.all, "detail"] as const,
  detail: (id: number) => [...layoutKeys.details(), id] as const,
  active: () => [...layoutKeys.all, "active"] as const,
  locations: () => [...layoutKeys.all, "locations"] as const,
};

// ============================================
// Query Hooks
// ============================================

/**
 * Hook to fetch paginated layouts list
 */
export function useLayouts(
  params: LayoutsQueryParams = {},
  options?: Omit<UseQueryOptions<PaginatedResponse<Layout>, ApiError>, "queryKey" | "queryFn">
) {
  return useQuery({
    queryKey: layoutKeys.list(params),
    queryFn: () => getLayouts(params),
    ...options,
  });
}

/**
 * Hook to fetch a single layout by ID
 */
export function useLayout(id: number, options?: Omit<UseQueryOptions<Layout, ApiError>, "queryKey" | "queryFn">) {
  return useQuery({
    queryKey: layoutKeys.detail(id),
    queryFn: () => getLayout(id),
    enabled: id > 0,
    ...options,
  });
}

// ============================================
// Mutation Hooks
// ============================================

/**
 * Hook to create a new layout
 */
export function useCreateLayout(options?: Omit<UseMutationOptions<Layout, ApiError, CreateLayoutData>, "mutationFn">) {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: createLayout,
    ...options,
    onSuccess: (...args) => {
      const [newLayout] = args;
      // Invalidate layouts list to refetch
      queryClient.invalidateQueries({ queryKey: layoutKeys.lists() });

      // Add the new layout to cache
      queryClient.setQueryData(layoutKeys.detail(newLayout.id), newLayout);

      // Call user's onSuccess if provided
      options?.onSuccess?.(...args);
    },
  });
}

/**
 * Hook to update an existing layout
 */
export function useUpdateLayout(
  options?: Omit<UseMutationOptions<Layout, ApiError, UpdateLayoutData, { previousLayout: Layout | undefined }>, "mutationFn">
) {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: updateLayout,
    ...options,
    onMutate: async (newData): Promise<{ previousLayout: Layout | undefined }> => {
      // Cancel any outgoing refetches
      await queryClient.cancelQueries({
        queryKey: layoutKeys.detail(newData.id),
      });

      // Snapshot the previous value
      const previousLayout = queryClient.getQueryData<Layout>(layoutKeys.detail(newData.id));

      // Optimistically update the cache
      if (previousLayout) {
        queryClient.setQueryData(layoutKeys.detail(newData.id), {
          ...previousLayout,
          ...newData,
        });
      }

      return { previousLayout };
    },
    onError: (...args) => {
      const [, newData, context] = args;
      // Rollback on error
      if (context?.previousLayout) {
        queryClient.setQueryData(layoutKeys.detail(newData.id), context.previousLayout);
      }
      // Call user's onError if provided
      options?.onError?.(...args);
    },
    onSettled: (...args) => {
      const [data] = args;
      if (data) {
        // Invalidate and refetch
        queryClient.invalidateQueries({ queryKey: layoutKeys.detail(data.id) });
        queryClient.invalidateQueries({ queryKey: layoutKeys.lists() });
      }
      // Call user's onSettled if provided
      options?.onSettled?.(...args);
    },
    onSuccess: (...args) => {
      // Call user's onSuccess if provided
      options?.onSuccess?.(...args);
    },
  });
}

/**
 * Hook to delete a layout
 */
export function useDeleteLayout(options?: Omit<UseMutationOptions<{ deleted: boolean; id: number }, ApiError, number>, "mutationFn">) {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: deleteLayout,
    ...options,
    onSuccess: (...args) => {
      const [, id] = args;
      // Remove from cache
      queryClient.removeQueries({ queryKey: layoutKeys.detail(id) });

      // Invalidate lists to refetch and update UI
      queryClient.invalidateQueries({ queryKey: layoutKeys.lists() });

      // Call user's onSuccess if provided
      options?.onSuccess?.(...args);
    },
  });
}

/**
 * Hook to duplicate a layout
 */
export function useDuplicateLayout(options?: Omit<UseMutationOptions<Layout, ApiError, number>, "mutationFn">) {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: duplicateLayout,
    onSuccess: (newLayout) => {
      // Invalidate layouts list
      queryClient.invalidateQueries({ queryKey: layoutKeys.lists() });

      // Add new layout to cache
      queryClient.setQueryData(layoutKeys.detail(newLayout.id), newLayout);
    },
    ...options,
  });
}

/**
 * Hook to fetch layouts filtered by scenario
 */
export function useLayoutsByScenario(
  scenario: LayoutsQueryParams["scenario"],
  options?: Omit<UseQueryOptions<PaginatedResponse<Layout>, ApiError>, "queryKey" | "queryFn">
) {
  return useQuery({
    queryKey: layoutKeys.list({ scenario }),
    queryFn: () => getLayouts({ scenario, perPage: 100 }), // Get all layouts for scenario
    enabled: !!scenario,
    ...options,
  });
}

// ============================================
// Utility Hooks
// ============================================

/**
 * Hook to prefetch a layout (useful for hover states)
 */
export function usePrefetchLayout() {
  const queryClient = useQueryClient();

  return (id: number) => {
    queryClient.prefetchQuery({
      queryKey: layoutKeys.detail(id),
      queryFn: () => getLayout(id),
      staleTime: 60 * 1000, // Consider fresh for 1 minute
    });
  };
}

/**
 * Hook to get a layout from cache without fetching
 */
export function useLayoutFromCache(id: number): Layout | undefined {
  const queryClient = useQueryClient();
  return queryClient.getQueryData<Layout>(layoutKeys.detail(id));
}

// ============================================
// Active Layouts Hooks
// ============================================

/**
 * Hook to fetch all active layouts for all scenarios and locations
 */
export function useActiveLayouts(options?: Omit<UseQueryOptions<AllActiveLayouts, ApiError>, "queryKey" | "queryFn">) {
  return useQuery({
    queryKey: layoutKeys.active(),
    queryFn: getActiveLayouts,
    ...options,
  });
}

/**
 * Hook to activate or deactivate a layout for a specific location
 */
export function useActivateLayoutLocation(options?: Omit<UseMutationOptions<Layout, ApiError, ActivateLocationData>, "mutationFn">) {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: activateLayoutForLocation,
    onSuccess: (updatedLayout) => {
      // Update the layout in cache
      queryClient.setQueryData(layoutKeys.detail(updatedLayout.id), updatedLayout);

      // Invalidate all related caches
      queryClient.invalidateQueries({ queryKey: layoutKeys.lists() });
      queryClient.invalidateQueries({ queryKey: layoutKeys.active() });
    },
    ...options,
  });
}

/**
 * Hook to fetch available locations (can be extended via apply_filters on backend)
 */
export function useLocations(options?: Omit<UseQueryOptions<AvailableLocations, ApiError>, "queryKey" | "queryFn">) {
  return useQuery({
    queryKey: layoutKeys.locations(),
    queryFn: getLocations,
    staleTime: 1000 * 60 * 5, // Cache for 5 minutes as locations rarely change
    ...options,
  });
}
