/**
 * Unit tests for OAuthButtons component
 *
 * Tests OAuth provider buttons for social login.
 * Covers:
 * - Rendering providers from API
 * - Button clicks and OAuth initiation
 * - Loading states (initial load and button clicks)
 * - Provider-specific styling (colors, icons)
 * - Disabled state
 * - Error handling
 * - Empty state (no providers)
 */

import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import OAuthButtons from '../OAuthButtons';

// Mock hooks
const mockUseOAuthProviders = vi.fn();
const mockUseInitiateOAuth = vi.fn();

vi.mock('../../hooks/useOAuth', () => ({
  useOAuthProviders: () => mockUseOAuthProviders(),
  useInitiateOAuth: () => mockUseInitiateOAuth(),
}));

// Helper to wrap component with QueryClient
const createWrapper = () => {
  const queryClient = new QueryClient({
    defaultOptions: {
      queries: { retry: false },
      mutations: { retry: false },
    },
  });
  return ({ children }: { children: React.ReactNode }) => (
    <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
  );
};

describe('OAuthButtons', () => {
  const mockMutate = vi.fn();
  const mockOnSuccess = vi.fn();

  beforeEach(() => {
    vi.clearAllMocks();
    mockUseInitiateOAuth.mockReturnValue({
      mutate: mockMutate,
      isPending: false,
      variables: null,
    });
  });

  describe('Loading State', () => {
    it('should show loading spinner while fetching providers', () => {
      mockUseOAuthProviders.mockReturnValue({
        data: undefined,
        isLoading: true,
      });

      const { container } = render(<OAuthButtons />, { wrapper: createWrapper() });

      // Look for the spinner SVG element with animate-spin class
      const spinner = container.querySelector('.animate-spin');
      expect(spinner).toBeInTheDocument();
    });

    it('should not show providers while loading', () => {
      mockUseOAuthProviders.mockReturnValue({
        data: undefined,
        isLoading: true,
      });

      render(<OAuthButtons />, { wrapper: createWrapper() });

      expect(screen.queryByRole('button', { name: /continue with/i })).not.toBeInTheDocument();
    });
  });

  describe('Empty State', () => {
    it('should render nothing when no providers are available', () => {
      mockUseOAuthProviders.mockReturnValue({
        data: [],
        isLoading: false,
      });

      const { container } = render(<OAuthButtons />, { wrapper: createWrapper() });

      expect(container.firstChild).toBeNull();
      expect(screen.queryByRole('button')).not.toBeInTheDocument();
    });

    it('should render nothing when providers data is null', () => {
      mockUseOAuthProviders.mockReturnValue({
        data: null,
        isLoading: false,
      });

      const { container } = render(<OAuthButtons />, { wrapper: createWrapper() });

      expect(container.firstChild).toBeNull();
    });
  });

  describe('Provider Rendering', () => {
    it('should render Google provider button', () => {
      mockUseOAuthProviders.mockReturnValue({
        data: [{ name: 'google', display_name: 'Google' }],
        isLoading: false,
      });

      render(<OAuthButtons />, { wrapper: createWrapper() });

      const button = screen.getByRole('button', { name: /continue with google/i });
      expect(button).toBeInTheDocument();
    });

    it('should render multiple provider buttons', () => {
      mockUseOAuthProviders.mockReturnValue({
        data: [
          { name: 'google', display_name: 'Google' },
          { name: 'facebook', display_name: 'Facebook' },
          { name: 'apple', display_name: 'Apple' },
        ],
        isLoading: false,
      });

      render(<OAuthButtons />, { wrapper: createWrapper() });

      expect(screen.getByRole('button', { name: /continue with google/i })).toBeInTheDocument();
      expect(screen.getByRole('button', { name: /continue with facebook/i })).toBeInTheDocument();
      expect(screen.getByRole('button', { name: /continue with apple/i })).toBeInTheDocument();
    });

    it('should apply Google-specific styling (white bg, border)', () => {
      mockUseOAuthProviders.mockReturnValue({
        data: [{ name: 'google', display_name: 'Google' }],
        isLoading: false,
      });

      render(<OAuthButtons />, { wrapper: createWrapper() });

      const button = screen.getByRole('button', { name: /continue with google/i });
      expect(button).toHaveClass('bg-white', 'text-gray-900', 'border-gray-300');
    });

    it('should apply Apple-specific styling (black bg)', () => {
      mockUseOAuthProviders.mockReturnValue({
        data: [{ name: 'apple', display_name: 'Apple' }],
        isLoading: false,
      });

      render(<OAuthButtons />, { wrapper: createWrapper() });

      const button = screen.getByRole('button', { name: /continue with apple/i });
      expect(button).toHaveClass('bg-black', 'text-white');
    });

    it('should apply Facebook-specific styling (blue bg)', () => {
      mockUseOAuthProviders.mockReturnValue({
        data: [{ name: 'facebook', display_name: 'Facebook' }],
        isLoading: false,
      });

      render(<OAuthButtons />, { wrapper: createWrapper() });

      const button = screen.getByRole('button', { name: /continue with facebook/i });
      expect(button).toHaveClass('bg-[#1877F2]', 'text-white');
    });

    it('should apply LinkedIn-specific styling', () => {
      mockUseOAuthProviders.mockReturnValue({
        data: [{ name: 'linkedin', display_name: 'LinkedIn' }],
        isLoading: false,
      });

      render(<OAuthButtons />, { wrapper: createWrapper() });

      const button = screen.getByRole('button', { name: /continue with linkedin/i });
      expect(button).toHaveClass('bg-[#0A66C2]', 'text-white');
    });

    it('should render unknown provider with fallback styling', () => {
      mockUseOAuthProviders.mockReturnValue({
        data: [{ name: 'custom_provider', display_name: 'Custom Provider' }],
        isLoading: false,
      });

      render(<OAuthButtons />, { wrapper: createWrapper() });

      const button = screen.getByRole('button', { name: /continue with custom provider/i });
      expect(button).toBeInTheDocument();
      expect(button).toHaveClass('bg-gray-600', 'text-white');
    });

    it('should display provider icons', () => {
      mockUseOAuthProviders.mockReturnValue({
        data: [
          { name: 'google', display_name: 'Google' },
          { name: 'facebook', display_name: 'Facebook' },
        ],
        isLoading: false,
      });

      render(<OAuthButtons />, { wrapper: createWrapper() });

      // Icons should be present (rendered as text in config)
      expect(screen.getByText('G')).toBeInTheDocument(); // Google icon
      expect(screen.getByText('f')).toBeInTheDocument(); // Facebook icon
    });
  });

  describe('Button Clicks', () => {
    it('should call OAuth initiation when button is clicked', () => {
      mockUseOAuthProviders.mockReturnValue({
        data: [{ name: 'google', display_name: 'Google' }],
        isLoading: false,
      });

      render(<OAuthButtons />, { wrapper: createWrapper() });

      const button = screen.getByRole('button', { name: /continue with google/i });
      fireEvent.click(button);

      expect(mockMutate).toHaveBeenCalledWith('google', expect.any(Object));
    });

    it('should call onSuccess callback after successful OAuth initiation', () => {
      mockUseOAuthProviders.mockReturnValue({
        data: [{ name: 'google', display_name: 'Google' }],
        isLoading: false,
      });

      mockMutate.mockImplementation((provider, { onSuccess }) => {
        onSuccess?.();
      });

      render(<OAuthButtons onSuccess={mockOnSuccess} />, { wrapper: createWrapper() });

      const button = screen.getByRole('button', { name: /continue with google/i });
      fireEvent.click(button);

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

    it('should handle multiple provider clicks', () => {
      mockUseOAuthProviders.mockReturnValue({
        data: [
          { name: 'google', display_name: 'Google' },
          { name: 'facebook', display_name: 'Facebook' },
        ],
        isLoading: false,
      });

      render(<OAuthButtons />, { wrapper: createWrapper() });

      const googleButton = screen.getByRole('button', { name: /continue with google/i });
      const facebookButton = screen.getByRole('button', { name: /continue with facebook/i });

      fireEvent.click(googleButton);
      expect(mockMutate).toHaveBeenCalledWith('google', expect.any(Object));

      fireEvent.click(facebookButton);
      expect(mockMutate).toHaveBeenCalledWith('facebook', expect.any(Object));
    });

    it('should not initiate OAuth when button is disabled', () => {
      mockUseOAuthProviders.mockReturnValue({
        data: [{ name: 'google', display_name: 'Google' }],
        isLoading: false,
      });

      render(<OAuthButtons disabled={true} />, { wrapper: createWrapper() });

      const button = screen.getByRole('button', { name: /continue with google/i });
      fireEvent.click(button);

      expect(mockMutate).not.toHaveBeenCalled();
    });

    it('should not initiate OAuth when another button is pending', () => {
      mockUseOAuthProviders.mockReturnValue({
        data: [{ name: 'google', display_name: 'Google' }],
        isLoading: false,
      });

      mockUseInitiateOAuth.mockReturnValue({
        mutate: mockMutate,
        isPending: true,
        variables: 'google',
      });

      render(<OAuthButtons />, { wrapper: createWrapper() });

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

      // Should not call mutate again
      expect(mockMutate).not.toHaveBeenCalled();
    });
  });

  describe('Loading State During OAuth', () => {
    it('should show loading state on clicked button', () => {
      mockUseOAuthProviders.mockReturnValue({
        data: [{ name: 'google', display_name: 'Google' }],
        isLoading: false,
      });

      mockUseInitiateOAuth.mockReturnValue({
        mutate: mockMutate,
        isPending: true,
        variables: 'google',
      });

      render(<OAuthButtons />, { wrapper: createWrapper() });

      expect(screen.getByText(/connecting/i)).toBeInTheDocument();
      expect(screen.queryByText(/continue with google/i)).not.toBeInTheDocument();
    });

    it('should show spinner icon during loading', () => {
      mockUseOAuthProviders.mockReturnValue({
        data: [{ name: 'google', display_name: 'Google' }],
        isLoading: false,
      });

      mockUseInitiateOAuth.mockReturnValue({
        mutate: mockMutate,
        isPending: true,
        variables: 'google',
      });

      const { container } = render(<OAuthButtons />, { wrapper: createWrapper() });

      // Loader2 icon should be rendered
      const spinner = container.querySelector('.animate-spin');
      expect(spinner).toBeInTheDocument();
    });

    it('should only show loading on the clicked button', () => {
      mockUseOAuthProviders.mockReturnValue({
        data: [
          { name: 'google', display_name: 'Google' },
          { name: 'facebook', display_name: 'Facebook' },
        ],
        isLoading: false,
      });

      mockUseInitiateOAuth.mockReturnValue({
        mutate: mockMutate,
        isPending: true,
        variables: 'google',
      });

      render(<OAuthButtons />, { wrapper: createWrapper() });

      // Google button should show loading
      expect(screen.getByText(/connecting/i)).toBeInTheDocument();

      // Facebook button should still show normal text
      expect(screen.getByText(/continue with facebook/i)).toBeInTheDocument();
    });
  });

  describe('Disabled State', () => {
    it('should disable all buttons when disabled prop is true', () => {
      mockUseOAuthProviders.mockReturnValue({
        data: [
          { name: 'google', display_name: 'Google' },
          { name: 'facebook', display_name: 'Facebook' },
        ],
        isLoading: false,
      });

      render(<OAuthButtons disabled={true} />, { wrapper: createWrapper() });

      const googleButton = screen.getByRole('button', { name: /continue with google/i });
      const facebookButton = screen.getByRole('button', { name: /continue with facebook/i });

      expect(googleButton).toBeDisabled();
      expect(facebookButton).toBeDisabled();
    });

    it('should apply disabled styling when disabled', () => {
      mockUseOAuthProviders.mockReturnValue({
        data: [{ name: 'google', display_name: 'Google' }],
        isLoading: false,
      });

      render(<OAuthButtons disabled={true} />, { wrapper: createWrapper() });

      const button = screen.getByRole('button', { name: /continue with google/i });
      expect(button).toHaveClass('disabled:opacity-50', 'disabled:cursor-not-allowed');
    });

    it('should disable all buttons during OAuth pending', () => {
      mockUseOAuthProviders.mockReturnValue({
        data: [
          { name: 'google', display_name: 'Google' },
          { name: 'facebook', display_name: 'Facebook' },
        ],
        isLoading: false,
      });

      mockUseInitiateOAuth.mockReturnValue({
        mutate: mockMutate,
        isPending: true,
        variables: 'google',
      });

      render(<OAuthButtons />, { wrapper: createWrapper() });

      const buttons = screen.getAllByRole('button');
      buttons.forEach(button => {
        expect(button).toBeDisabled();
      });
    });
  });

  describe('Error Handling', () => {
    it('should log error on OAuth initiation failure', () => {
      const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});

      mockUseOAuthProviders.mockReturnValue({
        data: [{ name: 'google', display_name: 'Google' }],
        isLoading: false,
      });

      const error = new Error('OAuth error');
      mockMutate.mockImplementation((provider, { onError }) => {
        onError?.(error);
      });

      render(<OAuthButtons />, { wrapper: createWrapper() });

      const button = screen.getByRole('button', { name: /continue with google/i });
      fireEvent.click(button);

      expect(consoleErrorSpy).toHaveBeenCalledWith('OAuth initiation error:', error);

      consoleErrorSpy.mockRestore();
    });
  });

  describe('Provider Variants', () => {
    it('should render Microsoft provider', () => {
      mockUseOAuthProviders.mockReturnValue({
        data: [{ name: 'microsoft', display_name: 'Microsoft' }],
        isLoading: false,
      });

      render(<OAuthButtons />, { wrapper: createWrapper() });

      const button = screen.getByRole('button', { name: /continue with microsoft/i });
      expect(button).toBeInTheDocument();
      expect(button).toHaveClass('bg-[#00A4EF]');
    });

    it('should render X (Twitter) provider', () => {
      mockUseOAuthProviders.mockReturnValue({
        data: [{ name: 'x', display_name: 'X' }],
        isLoading: false,
      });

      render(<OAuthButtons />, { wrapper: createWrapper() });

      const button = screen.getByRole('button', { name: /continue with x/i });
      expect(button).toBeInTheDocument();
      expect(button).toHaveClass('bg-black');
    });

    it('should render Twitch provider', () => {
      mockUseOAuthProviders.mockReturnValue({
        data: [{ name: 'twitch', display_name: 'Twitch' }],
        isLoading: false,
      });

      render(<OAuthButtons />, { wrapper: createWrapper() });

      const button = screen.getByRole('button', { name: /continue with twitch/i });
      expect(button).toBeInTheDocument();
      expect(button).toHaveClass('bg-[#9146FF]');
    });
  });

  describe('Button Styling', () => {
    it('should have consistent button styling', () => {
      mockUseOAuthProviders.mockReturnValue({
        data: [{ name: 'google', display_name: 'Google' }],
        isLoading: false,
      });

      render(<OAuthButtons />, { wrapper: createWrapper() });

      const button = screen.getByRole('button', { name: /continue with google/i });
      expect(button).toHaveClass(
        'w-full',
        'flex',
        'items-center',
        'justify-center',
        'rounded-lg',
        'shadow-sm'
      );
    });

    it('should have hover transition styles', () => {
      mockUseOAuthProviders.mockReturnValue({
        data: [{ name: 'google', display_name: 'Google' }],
        isLoading: false,
      });

      render(<OAuthButtons />, { wrapper: createWrapper() });

      const button = screen.getByRole('button', { name: /continue with google/i });
      expect(button).toHaveClass('transition-all', 'duration-200');
    });

    it('should have focus ring styles', () => {
      mockUseOAuthProviders.mockReturnValue({
        data: [{ name: 'google', display_name: 'Google' }],
        isLoading: false,
      });

      render(<OAuthButtons />, { wrapper: createWrapper() });

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

  describe('Accessibility', () => {
    it('should have button role for all providers', () => {
      mockUseOAuthProviders.mockReturnValue({
        data: [
          { name: 'google', display_name: 'Google' },
          { name: 'facebook', display_name: 'Facebook' },
        ],
        isLoading: false,
      });

      render(<OAuthButtons />, { wrapper: createWrapper() });

      const buttons = screen.getAllByRole('button');
      expect(buttons).toHaveLength(2);
    });

    it('should have descriptive button text', () => {
      mockUseOAuthProviders.mockReturnValue({
        data: [{ name: 'google', display_name: 'Google' }],
        isLoading: false,
      });

      render(<OAuthButtons />, { wrapper: createWrapper() });

      expect(screen.getByRole('button', { name: /continue with google/i })).toBeInTheDocument();
    });

    it('should indicate loading state to screen readers', () => {
      mockUseOAuthProviders.mockReturnValue({
        data: [{ name: 'google', display_name: 'Google' }],
        isLoading: false,
      });

      mockUseInitiateOAuth.mockReturnValue({
        mutate: mockMutate,
        isPending: true,
        variables: 'google',
      });

      render(<OAuthButtons />, { wrapper: createWrapper() });

      expect(screen.getByText(/connecting/i)).toBeInTheDocument();
    });
  });
});
