import React from 'react';
import { render, screen } from '@testing-library/react';
import { MemoryRouter, Routes, Route } from 'react-router-dom';
import { vi, describe, it, expect, beforeEach } from 'vitest';
import ProtectedRoute from '../ProtectedRoute';

// Mock the useAuth hook to control its output in tests
const mockUseAuth = vi.fn();
vi.mock('../../contexts/AuthContext', () => ({
  useAuth: () => mockUseAuth(),
}));

const TestComponent: React.FC = () => <div>Protected Content</div>;
const LoginComponent: React.FC = () => <div>Login Page</div>;
const UnauthorizedComponent: React.FC = () => <div>Unauthorized Page</div>;

const renderWithRouter = (ui: React.ReactNode, { initialEntries = ['/protected'] } = {}) => {
  return render(
    <MemoryRouter initialEntries={initialEntries}>
      <Routes>
        <Route path="/login" element={<LoginComponent />} />
        <Route path="/unauthorized" element={<UnauthorizedComponent />} />
        <Route path="/protected" element={ui} />
      </Routes>
    </MemoryRouter>
  );
};

describe('ProtectedRoute', () => {
  beforeEach(() => {
    vi.resetAllMocks();
  });

  it('shows a loading spinner while authentication is in progress', () => {
    mockUseAuth.mockReturnValue({
      user: null,
      isLoading: true,
      isInitialized: false,
    });

    renderWithRouter(
      <ProtectedRoute>
        <TestComponent />
      </ProtectedRoute>
    );

    expect(document.querySelector('.animate-spin')).toBeInTheDocument();
    expect(screen.queryByText('Protected Content')).not.toBeInTheDocument();
  });

  it('redirects to the login page if the user is not authenticated', () => {
    mockUseAuth.mockReturnValue({
      user: null,
      isLoading: false,
      isInitialized: true,
    });

    renderWithRouter(
      <ProtectedRoute>
        <TestComponent />
      </ProtectedRoute>
    );

    expect(screen.getByText('Login Page')).toBeInTheDocument();
    expect(screen.queryByText('Protected Content')).not.toBeInTheDocument();
  });

  it('renders the protected content if the user is authenticated', () => {
    mockUseAuth.mockReturnValue({
      user: { id: '1', role: 'user' },
      isLoading: false,
      isInitialized: true,
    });

    renderWithRouter(
      <ProtectedRoute>
        <TestComponent />
      </ProtectedRoute>
    );

    expect(screen.getByText('Protected Content')).toBeInTheDocument();
  });

  it('redirects to an unauthorized page if the user does not have the required role', () => {
    mockUseAuth.mockReturnValue({
      user: { id: '1', role: 'user' },
      isLoading: false,
      isInitialized: true,
    });

    renderWithRouter(
      <ProtectedRoute requiredRole="admin">
        <TestComponent />
      </ProtectedRoute>
    );

    expect(screen.getByText('Unauthorized Page')).toBeInTheDocument();
    expect(screen.queryByText('Protected Content')).not.toBeInTheDocument();
  });

  it('renders the protected content if the user has the required admin role', () => {
    mockUseAuth.mockReturnValue({
      user: { id: '1', role: 'admin' },
      isLoading: false,
      isInitialized: true,
    });

    renderWithRouter(
      <ProtectedRoute requiredRole="admin">
        <TestComponent />
      </ProtectedRoute>
    );

    expect(screen.getByText('Protected Content')).toBeInTheDocument();
  });

  it('renders the protected content if the user has the required user role', () => {
    mockUseAuth.mockReturnValue({
      user: { id: '1', role: 'user' },
      isLoading: false,
      isInitialized: true,
    });

    renderWithRouter(
      <ProtectedRoute requiredRole="user">
        <TestComponent />
      </ProtectedRoute>
    );

    expect(screen.getByText('Protected Content')).toBeInTheDocument();
  });
});