import { render, waitFor } from '@testing-library/react-native';
import React from 'react';
import * as SplashScreen from 'expo-splash-screen';
import { router, useRouter, useRootNavigationState, useSegments } from 'expo-router';
import RootLayout from '@/app/_layout';
import { useAuth } from '@/contexts/AuthContext';
import { useBLE } from '@/contexts/BLEContext';

// Mock react-native-reanimated
jest.mock('react-native-reanimated', () => ({
  default: {},
  useSharedValue: jest.fn,
  useAnimatedStyle: jest.fn,
  withTiming: jest.fn,
  withSpring: jest.fn,
  withDecay: jest.fn,
  withDelay: jest.fn,
  withRepeat: jest.fn,
  withSequence: jest.fn,
  cancelAnimation: jest.fn,
  measure: jest.fn,
  Easing: {},
  Extrapolate: {},
  runOnJS: jest.fn,
  runOnUI: jest.fn,
}));

// Mock dependencies
jest.mock('expo-router', () => {
  const mockRouter = {
    replace: jest.fn(),
    push: jest.fn(),
  };
  const Stack = ({ children }: { children: React.ReactNode }) => <>{children}</>;
  (Stack as any).Screen = () => null;
  return {
    Stack,
    router: mockRouter,
    useRouter: jest.fn(() => mockRouter),
    useRootNavigationState: jest.fn(),
    useSegments: jest.fn(),
  };
});

jest.mock('expo-splash-screen', () => ({
  preventAutoHideAsync: jest.fn().mockResolvedValue(undefined),
  hideAsync: jest.fn().mockResolvedValue(undefined),
}));

jest.mock('@/contexts/AuthContext', () => ({
  AuthProvider: ({ children }: { children: React.ReactNode }) => <>{children}</>,
  useAuth: jest.fn(),
}));

jest.mock('@/contexts/BeneficiaryContext', () => ({
  BeneficiaryProvider: ({ children }: { children: React.ReactNode }) => <>{children}</>,
}));

jest.mock('@/contexts/BLEContext', () => ({
  BLEProvider: ({ children }: { children: React.ReactNode }) => <>{children}</>,
  useBLE: jest.fn(),
}));

jest.mock('@/hooks/use-color-scheme', () => ({
  useColorScheme: jest.fn(() => 'light'),
}));

jest.mock('@stripe/stripe-react-native', () => ({
  StripeProvider: ({ children }: { children: React.ReactNode }) => <>{children}</>,
}));

jest.mock('@/components/ui/Toast', () => ({
  ToastProvider: ({ children }: { children: React.ReactNode }) => <>{children}</>,
}));

jest.mock('@/components/ui/LoadingSpinner', () => {
  const { Text } = require('react-native');
  return {
    LoadingSpinner: ({ message }: { message?: string; fullScreen?: boolean }) => (
      <Text>{message || 'Loading'}</Text>
    ),
  };
});

describe('RootLayout Redirect Logic', () => {
  const mockNavigationState = { key: 'root' };

  beforeEach(() => {
    jest.clearAllMocks();
    // Reset splash screen mocks
    (SplashScreen.hideAsync as jest.Mock).mockClear();
    (SplashScreen.preventAutoHideAsync as jest.Mock).mockClear();
    (useRootNavigationState as jest.Mock).mockReturnValue(mockNavigationState);
    (useBLE as jest.Mock).mockReturnValue({
      cleanupBLE: jest.fn(),
    });
  });

  describe('Initial redirect - Not authenticated', () => {
    it('should redirect to login when not authenticated and not in auth group', async () => {
      (useAuth as jest.Mock).mockReturnValue({
        isAuthenticated: false,
        isInitializing: false,
        user: null,
      });
      (useSegments as jest.Mock).mockReturnValue(['(tabs)', 'dashboard']);

      render(<RootLayout />);

      await waitFor(() => {
        expect(router.replace).toHaveBeenCalledWith('/(auth)/login');
      });
    });

    it('should not redirect when not authenticated but already in auth group', async () => {
      (useAuth as jest.Mock).mockReturnValue({
        isAuthenticated: false,
        isInitializing: false,
        user: null,
      });
      (useSegments as jest.Mock).mockReturnValue(['(auth)', 'login']);

      render(<RootLayout />);

      await waitFor(() => {
        expect(router.replace).not.toHaveBeenCalled();
      });
    });
  });

  describe('Initial redirect - Authenticated', () => {
    it('should not redirect when authenticated and in auth group (login flow)', async () => {
      (useAuth as jest.Mock).mockReturnValue({
        isAuthenticated: true,
        isInitializing: false,
        user: { user_id: 1, email: 'test@example.com', firstName: 'John' },
      });
      (useSegments as jest.Mock).mockReturnValue(['(auth)', 'verify-otp']);

      render(<RootLayout />);

      await waitFor(() => {
        expect(router.replace).not.toHaveBeenCalled();
      });
    });

    it('should redirect to enter-name when authenticated user has no firstName', async () => {
      (useAuth as jest.Mock).mockReturnValue({
        isAuthenticated: true,
        isInitializing: false,
        user: { user_id: 1, email: 'test@example.com', firstName: null },
      });
      (useSegments as jest.Mock).mockReturnValue(['(tabs)', 'dashboard']);

      render(<RootLayout />);

      await waitFor(() => {
        expect(router.replace).toHaveBeenCalledWith('/(auth)/enter-name');
      });
    });

    it('should not redirect when authenticated user has firstName and in tabs', async () => {
      (useAuth as jest.Mock).mockReturnValue({
        isAuthenticated: true,
        isInitializing: false,
        user: { user_id: 1, email: 'test@example.com', firstName: 'John' },
      });
      (useSegments as jest.Mock).mockReturnValue(['(tabs)', 'dashboard']);

      render(<RootLayout />);

      await waitFor(() => {
        expect(router.replace).not.toHaveBeenCalled();
      });
    });
  });

  describe('Ongoing protection', () => {
    it('should redirect to login when user logs out while in tabs', async () => {
      const { rerender } = render(<RootLayout />);

      // Initial state: authenticated
      (useAuth as jest.Mock).mockReturnValue({
        isAuthenticated: true,
        isInitializing: false,
        user: { user_id: 1, email: 'test@example.com', firstName: 'John' },
      });
      (useSegments as jest.Mock).mockReturnValue(['(tabs)', 'dashboard']);

      rerender(<RootLayout />);

      // Clear previous calls
      (router.replace as jest.Mock).mockClear();

      // Logout happens
      (useAuth as jest.Mock).mockReturnValue({
        isAuthenticated: false,
        isInitializing: false,
        user: null,
      });

      rerender(<RootLayout />);

      await waitFor(() => {
        expect(router.replace).toHaveBeenCalledWith('/(auth)/login');
      });
    });
  });

  describe('Loading states', () => {
    it('should show loading spinner while initializing', () => {
      (useAuth as jest.Mock).mockReturnValue({
        isAuthenticated: false,
        isInitializing: true,
        user: null,
      });
      (useSegments as jest.Mock).mockReturnValue([]);

      const { getByText } = render(<RootLayout />);

      expect(getByText('Loading...')).toBeTruthy();
    });

    it('should not redirect while navigation is not ready', async () => {
      (useAuth as jest.Mock).mockReturnValue({
        isAuthenticated: false,
        isInitializing: false,
        user: null,
      });
      (useRootNavigationState as jest.Mock).mockReturnValue(null);
      (useSegments as jest.Mock).mockReturnValue(['(tabs)']);

      render(<RootLayout />);

      await waitFor(() => {
        expect(router.replace).not.toHaveBeenCalled();
      });
    });
  });

});
