import React from 'react';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import '@testing-library/jest-dom';
import AvatarDropdown from '../AvatarDropdown';
import { User, UserRole } from '../../../types';
import { AccountStatus } from '../../../services/auth/auth.constants';

// Mock user data
const mockRegularUser: User = {
  _id: '1',
  _rev: '1-abc123',
  username: 'John Doe',
  email: 'john@example.com',
  role: UserRole.USER,
  status: AccountStatus.ACTIVE,
  createdAt: new Date(),
};

const mockAdminUser: User = {
  _id: '2',
  _rev: '1-def456',
  username: 'Admin User',
  email: 'admin@example.com',
  role: UserRole.ADMIN,
  status: AccountStatus.ACTIVE,
  createdAt: new Date(),
};

const mockUserWithAvatar: User = {
  ...mockRegularUser,
  avatar: 'https://example.com/avatar.jpg',
};

const mockUserWithPassword: User = {
  ...mockRegularUser,
  password: 'hashed-password',
};

describe('AvatarDropdown', () => {
  const mockOnLogout = jest.fn();
  const mockOnAdmin = jest.fn();
  const mockOnChangePassword = jest.fn();

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

  describe('rendering', () => {
    test('should render avatar button with user initials', () => {
      render(<AvatarDropdown user={mockRegularUser} onLogout={mockOnLogout} />);

      const button = screen.getByRole('button', { name: /user menu/i });
      expect(button).toBeInTheDocument();
      expect(button).toHaveTextContent('J');
    });

    test('should render avatar image when user has avatar', () => {
      render(
        <AvatarDropdown user={mockUserWithAvatar} onLogout={mockOnLogout} />
      );

      const avatar = screen.getByAltText('User avatar');
      expect(avatar).toBeInTheDocument();
      expect(avatar).toHaveAttribute('src', 'https://example.com/avatar.jpg');
    });

    test('should render fallback character for empty username', () => {
      const userWithEmptyName = { ...mockRegularUser, username: '' };
      render(
        <AvatarDropdown user={userWithEmptyName} onLogout={mockOnLogout} />
      );

      const button = screen.getByRole('button', { name: /user menu/i });
      expect(button).toHaveTextContent('?');
    });

    test('should not render dropdown menu initially', () => {
      render(<AvatarDropdown user={mockRegularUser} onLogout={mockOnLogout} />);

      expect(screen.queryByText('Signed in as')).not.toBeInTheDocument();
    });
  });

  describe('dropdown functionality', () => {
    test('should open dropdown when avatar button is clicked', () => {
      render(<AvatarDropdown user={mockRegularUser} onLogout={mockOnLogout} />);

      const button = screen.getByRole('button', { name: /user menu/i });
      fireEvent.click(button);

      expect(screen.getByText('Signed in as')).toBeInTheDocument();
      expect(screen.getByText('John Doe')).toBeInTheDocument();
    });

    test('should close dropdown when avatar button is clicked again', () => {
      render(<AvatarDropdown user={mockRegularUser} onLogout={mockOnLogout} />);

      const button = screen.getByRole('button', { name: /user menu/i });

      // Open dropdown
      fireEvent.click(button);
      expect(screen.getByText('Signed in as')).toBeInTheDocument();

      // Close dropdown
      fireEvent.click(button);
      expect(screen.queryByText('Signed in as')).not.toBeInTheDocument();
    });

    test('should close dropdown when clicking outside', async () => {
      render(
        <div>
          <AvatarDropdown user={mockRegularUser} onLogout={mockOnLogout} />
          <div data-testid='outside'>Outside element</div>
        </div>
      );

      const button = screen.getByRole('button', { name: /user menu/i });
      const outside = screen.getByTestId('outside');

      // Open dropdown
      fireEvent.click(button);
      expect(screen.getByText('Signed in as')).toBeInTheDocument();

      // Click outside
      fireEvent.mouseDown(outside);

      await waitFor(() => {
        expect(screen.queryByText('Signed in as')).not.toBeInTheDocument();
      });
    });
  });

  describe('user information display', () => {
    test('should display username in dropdown', () => {
      render(<AvatarDropdown user={mockRegularUser} onLogout={mockOnLogout} />);

      fireEvent.click(screen.getByRole('button', { name: /user menu/i }));
      expect(screen.getByText('John Doe')).toBeInTheDocument();
    });

    test('should display administrator badge for admin users', () => {
      render(<AvatarDropdown user={mockAdminUser} onLogout={mockOnLogout} />);

      fireEvent.click(screen.getByRole('button', { name: /user menu/i }));
      expect(screen.getByText('Administrator')).toBeInTheDocument();
    });

    test('should not display administrator badge for regular users', () => {
      render(<AvatarDropdown user={mockRegularUser} onLogout={mockOnLogout} />);

      fireEvent.click(screen.getByRole('button', { name: /user menu/i }));
      expect(screen.queryByText('Administrator')).not.toBeInTheDocument();
    });

    test('should truncate long usernames', () => {
      const userWithLongName = {
        ...mockRegularUser,
        username: 'Very Long Username That Should Be Truncated',
      };

      render(
        <AvatarDropdown user={userWithLongName} onLogout={mockOnLogout} />
      );

      fireEvent.click(screen.getByRole('button', { name: /user menu/i }));
      const usernameElement = screen.getByText(
        'Very Long Username That Should Be Truncated'
      );
      expect(usernameElement).toHaveClass('truncate');
    });
  });

  describe('logout functionality', () => {
    test('should call onLogout when logout button is clicked', () => {
      render(<AvatarDropdown user={mockRegularUser} onLogout={mockOnLogout} />);

      fireEvent.click(screen.getByRole('button', { name: /user menu/i }));
      fireEvent.click(screen.getByText('Logout'));

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

    test('should close dropdown after logout is clicked', () => {
      render(<AvatarDropdown user={mockRegularUser} onLogout={mockOnLogout} />);

      fireEvent.click(screen.getByRole('button', { name: /user menu/i }));
      fireEvent.click(screen.getByText('Logout'));

      expect(screen.queryByText('Signed in as')).not.toBeInTheDocument();
    });

    test('should always display logout button', () => {
      render(<AvatarDropdown user={mockRegularUser} onLogout={mockOnLogout} />);

      fireEvent.click(screen.getByRole('button', { name: /user menu/i }));
      expect(screen.getByText('Logout')).toBeInTheDocument();
    });
  });

  describe('admin functionality', () => {
    test('should display admin interface button for admin users when onAdmin provided', () => {
      render(
        <AvatarDropdown
          user={mockAdminUser}
          onLogout={mockOnLogout}
          onAdmin={mockOnAdmin}
        />
      );

      fireEvent.click(screen.getByRole('button', { name: /user menu/i }));
      expect(screen.getByText('Admin Interface')).toBeInTheDocument();
    });

    test('should not display admin interface button for regular users', () => {
      render(
        <AvatarDropdown
          user={mockRegularUser}
          onLogout={mockOnLogout}
          onAdmin={mockOnAdmin}
        />
      );

      fireEvent.click(screen.getByRole('button', { name: /user menu/i }));
      expect(screen.queryByText('Admin Interface')).not.toBeInTheDocument();
    });

    test('should not display admin interface button when onAdmin not provided', () => {
      render(<AvatarDropdown user={mockAdminUser} onLogout={mockOnLogout} />);

      fireEvent.click(screen.getByRole('button', { name: /user menu/i }));
      expect(screen.queryByText('Admin Interface')).not.toBeInTheDocument();
    });

    test('should call onAdmin when admin interface button is clicked', () => {
      render(
        <AvatarDropdown
          user={mockAdminUser}
          onLogout={mockOnLogout}
          onAdmin={mockOnAdmin}
        />
      );

      fireEvent.click(screen.getByRole('button', { name: /user menu/i }));
      fireEvent.click(screen.getByText('Admin Interface'));

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

    test('should close dropdown after admin interface is clicked', () => {
      render(
        <AvatarDropdown
          user={mockAdminUser}
          onLogout={mockOnLogout}
          onAdmin={mockOnAdmin}
        />
      );

      fireEvent.click(screen.getByRole('button', { name: /user menu/i }));
      fireEvent.click(screen.getByText('Admin Interface'));

      expect(screen.queryByText('Signed in as')).not.toBeInTheDocument();
    });
  });

  describe('change password functionality', () => {
    test('should display change password button for users with password when onChangePassword provided', () => {
      render(
        <AvatarDropdown
          user={mockUserWithPassword}
          onLogout={mockOnLogout}
          onChangePassword={mockOnChangePassword}
        />
      );

      fireEvent.click(screen.getByRole('button', { name: /user menu/i }));
      expect(screen.getByText('Change Password')).toBeInTheDocument();
    });

    test('should not display change password button for users without password', () => {
      render(
        <AvatarDropdown
          user={mockRegularUser}
          onLogout={mockOnLogout}
          onChangePassword={mockOnChangePassword}
        />
      );

      fireEvent.click(screen.getByRole('button', { name: /user menu/i }));
      expect(screen.queryByText('Change Password')).not.toBeInTheDocument();
    });

    test('should not display change password button when onChangePassword not provided', () => {
      render(
        <AvatarDropdown user={mockUserWithPassword} onLogout={mockOnLogout} />
      );

      fireEvent.click(screen.getByRole('button', { name: /user menu/i }));
      expect(screen.queryByText('Change Password')).not.toBeInTheDocument();
    });

    test('should call onChangePassword when change password button is clicked', () => {
      render(
        <AvatarDropdown
          user={mockUserWithPassword}
          onLogout={mockOnLogout}
          onChangePassword={mockOnChangePassword}
        />
      );

      fireEvent.click(screen.getByRole('button', { name: /user menu/i }));
      fireEvent.click(screen.getByText('Change Password'));

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

    test('should close dropdown after change password is clicked', () => {
      render(
        <AvatarDropdown
          user={mockUserWithPassword}
          onLogout={mockOnLogout}
          onChangePassword={mockOnChangePassword}
        />
      );

      fireEvent.click(screen.getByRole('button', { name: /user menu/i }));
      fireEvent.click(screen.getByText('Change Password'));

      expect(screen.queryByText('Signed in as')).not.toBeInTheDocument();
    });
  });

  describe('getInitials function', () => {
    test('should return first character uppercase for regular names', () => {
      const userWithLowercase = { ...mockRegularUser, username: 'john doe' };
      render(
        <AvatarDropdown user={userWithLowercase} onLogout={mockOnLogout} />
      );

      const button = screen.getByRole('button', { name: /user menu/i });
      expect(button).toHaveTextContent('J');
    });

    test('should return question mark for empty string', () => {
      const userWithEmptyName = { ...mockRegularUser, username: '' };
      render(
        <AvatarDropdown user={userWithEmptyName} onLogout={mockOnLogout} />
      );

      const button = screen.getByRole('button', { name: /user menu/i });
      expect(button).toHaveTextContent('?');
    });

    test('should handle single character names', () => {
      const userWithSingleChar = { ...mockRegularUser, username: 'x' };
      render(
        <AvatarDropdown user={userWithSingleChar} onLogout={mockOnLogout} />
      );

      const button = screen.getByRole('button', { name: /user menu/i });
      expect(button).toHaveTextContent('X');
    });

    test('should handle special characters', () => {
      const userWithSpecialChar = { ...mockRegularUser, username: '@john' };
      render(
        <AvatarDropdown user={userWithSpecialChar} onLogout={mockOnLogout} />
      );

      const button = screen.getByRole('button', { name: /user menu/i });
      expect(button).toHaveTextContent('@');
    });
  });

  describe('accessibility', () => {
    test('should have proper aria-label for avatar button', () => {
      render(<AvatarDropdown user={mockRegularUser} onLogout={mockOnLogout} />);

      const button = screen.getByRole('button', { name: /user menu/i });
      expect(button).toHaveAttribute('aria-label', 'User menu');
    });

    test('should be keyboard accessible', () => {
      render(<AvatarDropdown user={mockRegularUser} onLogout={mockOnLogout} />);

      const button = screen.getByRole('button', { name: /user menu/i });
      button.focus();
      expect(button).toHaveFocus();
    });

    test('should have proper focus styles', () => {
      render(<AvatarDropdown user={mockRegularUser} onLogout={mockOnLogout} />);

      const button = screen.getByRole('button', { name: /user menu/i });
      expect(button).toHaveClass('focus:outline-none', 'focus:ring-2');
    });
  });

  describe('styling and theming', () => {
    test('should apply dark mode classes', () => {
      render(<AvatarDropdown user={mockRegularUser} onLogout={mockOnLogout} />);

      const button = screen.getByRole('button', { name: /user menu/i });
      expect(button).toHaveClass('dark:bg-slate-700', 'dark:text-slate-300');

      fireEvent.click(button);
      const dropdown = screen
        .getByText('Signed in as')
        .closest('div')?.parentElement;
      expect(dropdown).toHaveClass(
        'dark:bg-slate-800',
        'dark:border-slate-700'
      );
    });

    test('should apply hover styles to menu items', () => {
      render(<AvatarDropdown user={mockRegularUser} onLogout={mockOnLogout} />);

      fireEvent.click(screen.getByRole('button', { name: /user menu/i }));
      const logoutButton = screen.getByText('Logout');
      expect(logoutButton).toHaveClass(
        'hover:bg-slate-100',
        'dark:hover:bg-slate-700'
      );
    });
  });

  describe('edge cases', () => {
    test('should handle clicking outside when dropdown is closed', async () => {
      render(
        <div>
          <AvatarDropdown user={mockRegularUser} onLogout={mockOnLogout} />
          <div data-testid='outside'>Outside element</div>
        </div>
      );

      const outside = screen.getByTestId('outside');
      fireEvent.mouseDown(outside);

      // Should not throw any errors
      expect(screen.queryByText('Signed in as')).not.toBeInTheDocument();
    });

    test('should handle rapid clicking', () => {
      render(<AvatarDropdown user={mockRegularUser} onLogout={mockOnLogout} />);

      const button = screen.getByRole('button', { name: /user menu/i });

      // Rapid clicks - odd number should end up open
      fireEvent.click(button);
      fireEvent.click(button);
      fireEvent.click(button);

      // Should end up open (3 clicks = open)
      expect(screen.getByText('Signed in as')).toBeInTheDocument();
    });

    test('should cleanup event listeners on unmount', () => {
      const removeEventListenerSpy = jest.spyOn(
        document,
        'removeEventListener'
      );

      const { unmount } = render(
        <AvatarDropdown user={mockRegularUser} onLogout={mockOnLogout} />
      );

      unmount();

      expect(removeEventListenerSpy).toHaveBeenCalledWith(
        'mousedown',
        expect.any(Function)
      );

      removeEventListenerSpy.mockRestore();
    });
  });
});
