/**
 * Tests for UserMenu Component
 * Verifies user menu rendering, navigation, and logout functionality
 */

import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { UserMenu } from '@/components/layout/UserMenu';
import { useAuth } from '@/lib/auth/AuthContext';
import { useLogout } from '@/lib/api/hooks/useAuth';
import type { User } from '@/lib/stores/authStore';

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

jest.mock('@/lib/api/hooks/useAuth', () => ({
  useLogout: jest.fn(),
}));

// Helper to create mock user
function createMockUser(overrides: Partial<User> = {}): User {
  return {
    id: 'user-123',
    email: 'test@example.com',
    first_name: 'Test',
    last_name: 'User',
    phone_number: null,
    is_active: true,
    is_superuser: false,
    created_at: new Date().toISOString(),
    updated_at: null,
    ...overrides,
  };
}

describe('UserMenu', () => {
  const mockLogout = jest.fn();

  beforeEach(() => {
    jest.clearAllMocks();

    (useLogout as jest.Mock).mockReturnValue({
      mutate: mockLogout,
      isPending: false,
    });

    (useAuth as unknown as jest.Mock).mockReturnValue({
      user: createMockUser(),
    });
  });

  describe('Rendering', () => {
    it('renders user menu trigger', () => {
      render(<UserMenu />);
      expect(screen.getByTestId('user-menu-trigger')).toBeInTheDocument();
    });

    it('displays user initials in avatar', () => {
      (useAuth as unknown as jest.Mock).mockReturnValue({
        user: createMockUser({
          first_name: 'John',
          last_name: 'Doe',
        }),
      });

      render(<UserMenu />);

      expect(screen.getByText('JD')).toBeInTheDocument();
    });

    it('displays single initial when no last name', () => {
      (useAuth as unknown as jest.Mock).mockReturnValue({
        user: createMockUser({
          first_name: 'John',
          last_name: null,
        }),
      });

      render(<UserMenu />);

      expect(screen.getByText('J')).toBeInTheDocument();
    });

    it('displays default initial when no first name', () => {
      (useAuth as unknown as jest.Mock).mockReturnValue({
        user: createMockUser({
          first_name: '',
        }),
      });

      render(<UserMenu />);

      expect(screen.getByText('U')).toBeInTheDocument();
    });

    it('returns null when no user', () => {
      (useAuth as unknown as jest.Mock).mockReturnValue({
        user: null,
      });

      const { container } = render(<UserMenu />);

      expect(container).toBeEmptyDOMElement();
    });
  });

  describe('Dropdown Menu', () => {
    it('opens menu when trigger is clicked', async () => {
      const user = userEvent.setup();

      render(<UserMenu />);

      const trigger = screen.getByTestId('user-menu-trigger');
      await user.click(trigger);

      await waitFor(() => {
        expect(screen.getByTestId('user-menu-content')).toBeInTheDocument();
      });
    });

    it('displays user info in menu header', async () => {
      const user = userEvent.setup();

      (useAuth as unknown as jest.Mock).mockReturnValue({
        user: createMockUser({
          first_name: 'John',
          last_name: 'Doe',
          email: 'john@example.com',
        }),
      });

      render(<UserMenu />);

      const trigger = screen.getByTestId('user-menu-trigger');
      await user.click(trigger);

      await waitFor(() => {
        expect(screen.getByText('John Doe')).toBeInTheDocument();
        expect(screen.getByText('john@example.com')).toBeInTheDocument();
      });
    });
  });

  describe('Navigation Links', () => {
    it('includes profile link', async () => {
      const user = userEvent.setup();

      render(<UserMenu />);

      const trigger = screen.getByTestId('user-menu-trigger');
      await user.click(trigger);

      const profileLink = await screen.findByTestId('user-menu-profile');
      expect(profileLink).toHaveAttribute('href', '/settings/profile');
    });

    it('includes password/settings link', async () => {
      const user = userEvent.setup();

      render(<UserMenu />);

      const trigger = screen.getByTestId('user-menu-trigger');
      await user.click(trigger);

      const passwordLink = await screen.findByTestId('user-menu-password');
      expect(passwordLink).toHaveAttribute('href', '/settings/password');
    });

    it('includes sessions link', async () => {
      const user = userEvent.setup();

      render(<UserMenu />);

      const trigger = screen.getByTestId('user-menu-trigger');
      await user.click(trigger);

      const sessionsLink = await screen.findByTestId('user-menu-sessions');
      expect(sessionsLink).toHaveAttribute('href', '/settings/sessions');
    });

    it('includes preferences link', async () => {
      const user = userEvent.setup();

      render(<UserMenu />);

      const trigger = screen.getByTestId('user-menu-trigger');
      await user.click(trigger);

      const preferencesLink = await screen.findByTestId('user-menu-preferences');
      expect(preferencesLink).toHaveAttribute('href', '/settings/preferences');
    });
  });

  describe('Admin Link', () => {
    it('shows admin link for superusers', async () => {
      const user = userEvent.setup();

      (useAuth as unknown as jest.Mock).mockReturnValue({
        user: createMockUser({ is_superuser: true }),
      });

      render(<UserMenu />);

      const trigger = screen.getByTestId('user-menu-trigger');
      await user.click(trigger);

      const adminLink = await screen.findByTestId('user-menu-admin');
      expect(adminLink).toHaveAttribute('href', '/admin');
    });

    it('does not show admin link for regular users', async () => {
      const user = userEvent.setup();

      (useAuth as unknown as jest.Mock).mockReturnValue({
        user: createMockUser({ is_superuser: false }),
      });

      render(<UserMenu />);

      const trigger = screen.getByTestId('user-menu-trigger');
      await user.click(trigger);

      await waitFor(() => {
        expect(screen.queryByTestId('user-menu-admin')).not.toBeInTheDocument();
      });
    });
  });

  describe('Logout Functionality', () => {
    it('calls logout when logout button is clicked', async () => {
      const user = userEvent.setup();

      render(<UserMenu />);

      const trigger = screen.getByTestId('user-menu-trigger');
      await user.click(trigger);

      const logoutButton = await screen.findByTestId('user-menu-logout');
      await user.click(logoutButton);

      expect(mockLogout).toHaveBeenCalledTimes(1);
    });

    it('shows loading state when logging out', async () => {
      const user = userEvent.setup();

      (useLogout as jest.Mock).mockReturnValue({
        mutate: mockLogout,
        isPending: true,
      });

      render(<UserMenu />);

      const trigger = screen.getByTestId('user-menu-trigger');
      await user.click(trigger);

      await waitFor(() => {
        expect(screen.getByText('loggingOut')).toBeInTheDocument();
      });
    });

    it('disables logout button when logging out', async () => {
      const user = userEvent.setup();

      (useLogout as jest.Mock).mockReturnValue({
        mutate: mockLogout,
        isPending: true,
      });

      render(<UserMenu />);

      const trigger = screen.getByTestId('user-menu-trigger');
      await user.click(trigger);

      const logoutButton = await screen.findByTestId('user-menu-logout');
      expect(logoutButton).toHaveAttribute('data-disabled');
    });
  });

  describe('Accessibility', () => {
    it('has accessible label on trigger', () => {
      (useAuth as unknown as jest.Mock).mockReturnValue({
        user: createMockUser({
          first_name: 'John',
        }),
      });

      render(<UserMenu />);

      const trigger = screen.getByTestId('user-menu-trigger');
      expect(trigger).toHaveAttribute('aria-label', 'User menu for John');
    });

    it('uses email when no first name', () => {
      (useAuth as unknown as jest.Mock).mockReturnValue({
        user: createMockUser({
          first_name: '',
          email: 'test@example.com',
        }),
      });

      render(<UserMenu />);

      const trigger = screen.getByTestId('user-menu-trigger');
      expect(trigger).toHaveAttribute('aria-label', 'User menu for test@example.com');
    });

    it('logout button has destructive styling', async () => {
      const user = userEvent.setup();

      render(<UserMenu />);

      const trigger = screen.getByTestId('user-menu-trigger');
      await user.click(trigger);

      const logoutButton = await screen.findByTestId('user-menu-logout');
      expect(logoutButton).toHaveClass('text-destructive');
    });
  });
});
