import type { ReactNode } from 'react';
import { useEffect } from 'react';
import {
  QueryClient,
  QueryClientProvider,
  QueryCache,
  MutationCache,
} from '@tanstack/react-query';
import { BrowserRouter } from 'react-router-dom';
import { toast } from 'sonner';
import { Toaster } from '@/components/ui/toaster';
import { Toaster as Sonner } from '@/components/ui/sonner';
import { TooltipProvider } from '@/components/ui/tooltip';
import { ThemeProvider } from '@/components/theme-provider';
import { AuthProvider } from '@/contexts/AuthContext';
import { ScopeProvider } from '@/contexts/ScopeProvider';
import { APP_DEFAULT_THEME } from '@/shared/constants/theme';
import {
  FORBIDDEN_ERROR_MESSAGE,
  isForbiddenError,
} from '@/shared/errors/errorMessages';
import { AUTH_EXPIRED_EVENT } from '@/shared/constants/auth';
import { ApiError, AuthExpiredError } from '@/shared/api/httpClient';

// Single handler so a backend 403 (permission denied) degrades to a toast
// rather than a crash or silent failure, wherever it occurs.
function notifyOnForbidden(error: unknown): void {
  if (isForbiddenError(error)) {
    toast.error(FORBIDDEN_ERROR_MESSAGE);
  }
}

function shouldRetryQuery(failureCount: number, error: unknown): boolean {
  if (error instanceof AuthExpiredError) {
    return false;
  }

  if (error instanceof ApiError && error.status === 401) {
    return false;
  }

  return failureCount < 3;
}

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      retry: shouldRetryQuery,
    },
  },
  queryCache: new QueryCache({ onError: notifyOnForbidden }),
  mutationCache: new MutationCache({ onError: notifyOnForbidden }),
});

interface AppProvidersProps {
  readonly children: ReactNode;
}

export function AppProviders({ children }: AppProvidersProps) {
  useEffect(() => {
    const clearQueries = () => queryClient.clear();
    window.addEventListener(AUTH_EXPIRED_EVENT, clearQueries);

    return () => {
      window.removeEventListener(AUTH_EXPIRED_EVENT, clearQueries);
    };
  }, []);

  return (
    <ThemeProvider defaultTheme={APP_DEFAULT_THEME}>
      <QueryClientProvider client={queryClient}>
        <TooltipProvider>
          <Toaster />
          <Sonner />
          <BrowserRouter>
            <AuthProvider>
              <ScopeProvider>{children}</ScopeProvider>
            </AuthProvider>
          </BrowserRouter>
        </TooltipProvider>
      </QueryClientProvider>
    </ThemeProvider>
  );
}
