/**
 * Unit tests for VerifyEmail component
 *
 * Tests all verification functionality including:
 * - Loading state while verifying
 * - Success message on verification
 * - Error state for invalid token
 * - Redirect after success
 * - Already verified state
 * - Missing token handling
 * - Navigation flows
 */

import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { MemoryRouter, Routes, Route } from 'react-router-dom';
import VerifyEmail from '../VerifyEmail';
import apiClient from '../../api/client';

// Mock the API client
vi.mock('../../api/client');

// Mock the cookies utility
vi.mock('../../utils/cookies', () => ({
  deleteCookie: vi.fn(),
}));

// Mock the domain utility
vi.mock('../../utils/domain', () => ({
  getBaseDomain: vi.fn(() => 'lvh.me'),
}));

// Helper to render component with router and search params
const renderWithRouter = (searchParams: string = '') => {
  const TestComponent = () => (
    <MemoryRouter initialEntries={[`/verify-email${searchParams}`]}>
      <Routes>
        <Route path="/verify-email" element={<VerifyEmail />} />
        <Route path="/login" element={<div>Login Page</div>} />
      </Routes>
    </MemoryRouter>
  );

  return render(<TestComponent />);
};

describe('VerifyEmail', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    // Reset location mock
    delete (window as any).location;
    (window as any).location = {
      protocol: 'http:',
      hostname: 'platform.lvh.me',
      port: '5173',
      href: 'http://platform.lvh.me:5173/verify-email',
    };
  });

  afterEach(() => {
    vi.resetAllMocks();
  });

  describe('Missing Token Handling', () => {
    it('should show error when no token is provided', () => {
      renderWithRouter('');

      expect(screen.getByText('Invalid Link')).toBeInTheDocument();
      expect(
        screen.getByText('No verification token was provided. Please check your email for the correct link.')
      ).toBeInTheDocument();
    });

    it('should render "Go to Login" button when token is missing', () => {
      renderWithRouter('');

      const loginButton = screen.getByRole('button', { name: /go to login/i });
      expect(loginButton).toBeInTheDocument();
    });

    it('should navigate to login when button clicked with missing token', async () => {
      renderWithRouter('');

      const loginButton = screen.getByRole('button', { name: /go to login/i });
      fireEvent.click(loginButton);

      // Wait for navigation to complete
      await waitFor(() => {
        expect(screen.getByText('Login Page')).toBeInTheDocument();
      });
    });

    it('should show error icon for missing token', () => {
      const { container } = renderWithRouter('');

      // Check for red error styling
      const errorIcon = container.querySelector('.bg-red-100');
      expect(errorIcon).toBeInTheDocument();
    });
  });

  describe('Pending State', () => {
    it('should show pending state with verify button when token is present', () => {
      renderWithRouter('?token=valid-token-123');

      expect(screen.getByText('Verify Your Email')).toBeInTheDocument();
      expect(
        screen.getByText('Click the button below to confirm your email address.')
      ).toBeInTheDocument();
      expect(screen.getByRole('button', { name: /confirm verification/i })).toBeInTheDocument();
    });

    it('should show shield icon in pending state', () => {
      const { container } = renderWithRouter('?token=valid-token-123');

      // Check for brand color styling
      const iconContainer = container.querySelector('.bg-brand-100');
      expect(iconContainer).toBeInTheDocument();
    });
  });

  describe('Loading State', () => {
    it('should show loading state while verifying', async () => {
      // Mock API to never resolve (simulating slow response)
      const mockPost = vi.mocked(apiClient.post);
      mockPost.mockImplementation(() => new Promise(() => {}));

      renderWithRouter('?token=valid-token-123');

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

      await waitFor(() => {
        expect(screen.getByText('Verifying Your Email')).toBeInTheDocument();
      });

      expect(
        screen.getByText('Please wait while we verify your email address...')
      ).toBeInTheDocument();
    });

    it('should show loading spinner during verification', async () => {
      const mockPost = vi.mocked(apiClient.post);
      mockPost.mockImplementation(() => new Promise(() => {}));

      const { container } = renderWithRouter('?token=valid-token-123');

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

      await waitFor(() => {
        const spinner = container.querySelector('.animate-spin');
        expect(spinner).toBeInTheDocument();
      });
    });

    it('should call API with correct token', async () => {
      const mockPost = vi.mocked(apiClient.post);
      mockPost.mockResolvedValue({ data: { detail: 'Email verified successfully.' } });

      renderWithRouter('?token=test-token-456');

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

      await waitFor(() => {
        expect(mockPost).toHaveBeenCalledWith('/auth/email/verify/', { token: 'test-token-456' });
      });
    });
  });

  describe('Success State', () => {
    it('should show success message after successful verification', async () => {
      const mockPost = vi.mocked(apiClient.post);
      mockPost.mockResolvedValue({ data: { detail: 'Email verified successfully.' } });

      renderWithRouter('?token=valid-token-123');

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

      await waitFor(() => {
        expect(screen.getByText('Email Verified!')).toBeInTheDocument();
      });

      expect(
        screen.getByText(
          'Your email address has been successfully verified. You can now sign in to your account.'
        )
      ).toBeInTheDocument();
    });

    it('should show success icon after verification', async () => {
      const mockPost = vi.mocked(apiClient.post);
      mockPost.mockResolvedValue({ data: { detail: 'Email verified successfully.' } });

      const { container } = renderWithRouter('?token=valid-token-123');

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

      await waitFor(() => {
        const successIcon = container.querySelector('.bg-green-100');
        expect(successIcon).toBeInTheDocument();
      });
    });

    it('should clear auth cookies after successful verification', async () => {
      const mockPost = vi.mocked(apiClient.post);
      mockPost.mockResolvedValue({ data: { detail: 'Email verified successfully.' } });

      const { deleteCookie } = await import('../../utils/cookies');

      renderWithRouter('?token=valid-token-123');

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

      await waitFor(() => {
        expect(deleteCookie).toHaveBeenCalledWith('access_token');
        expect(deleteCookie).toHaveBeenCalledWith('refresh_token');
      });
    });

    it('should redirect to login page on success button click', async () => {
      const mockPost = vi.mocked(apiClient.post);
      mockPost.mockResolvedValue({ data: { detail: 'Email verified successfully.' } });

      renderWithRouter('?token=valid-token-123');

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

      await waitFor(() => {
        expect(screen.getByText('Email Verified!')).toBeInTheDocument();
      });

      const loginButton = screen.getByRole('button', { name: /go to login/i });

      // Mock window.location.href to track the redirect
      let redirectUrl = '';
      Object.defineProperty((window as any).location, 'href', {
        set: (url: string) => {
          redirectUrl = url;
        },
        get: () => redirectUrl || 'http://platform.lvh.me:5173/verify-email',
      });

      fireEvent.click(loginButton);

      // Verify the redirect URL was set correctly
      expect(redirectUrl).toBe('http://lvh.me:5173/login');
    });

    it('should build correct redirect URL with base domain', async () => {
      const mockPost = vi.mocked(apiClient.post);
      mockPost.mockResolvedValue({ data: { detail: 'Email verified successfully.' } });

      renderWithRouter('?token=valid-token-123');

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

      await waitFor(() => {
        expect(screen.getByText('Email Verified!')).toBeInTheDocument();
      });

      const loginButton = screen.getByRole('button', { name: /go to login/i });

      // Mock window.location.href to track the redirect
      let redirectUrl = '';
      Object.defineProperty((window as any).location, 'href', {
        set: (url: string) => {
          redirectUrl = url;
        },
        get: () => redirectUrl || 'http://platform.lvh.me:5173/verify-email',
      });

      fireEvent.click(loginButton);

      // Verify the redirect URL uses the base domain
      expect(redirectUrl).toContain('lvh.me');
      expect(redirectUrl).toContain('/login');
    });
  });

  describe('Already Verified State', () => {
    it('should show already verified message when email is already verified', async () => {
      const mockPost = vi.mocked(apiClient.post);
      mockPost.mockResolvedValue({ data: { detail: 'Email already verified.' } });

      renderWithRouter('?token=already-verified-token');

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

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

      expect(
        screen.getByText(
          'This email address has already been verified. You can use it to sign in to your account.'
        )
      ).toBeInTheDocument();
    });

    it('should show mail icon for already verified state', async () => {
      const mockPost = vi.mocked(apiClient.post);
      mockPost.mockResolvedValue({ data: { detail: 'Email already verified.' } });

      const { container } = renderWithRouter('?token=already-verified-token');

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

      await waitFor(() => {
        const iconContainer = container.querySelector('.bg-blue-100');
        expect(iconContainer).toBeInTheDocument();
      });
    });

    it('should navigate to login from already verified state', async () => {
      const mockPost = vi.mocked(apiClient.post);
      mockPost.mockResolvedValue({ data: { detail: 'Email already verified.' } });

      renderWithRouter('?token=already-verified-token');

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

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

      const loginButton = screen.getByRole('button', { name: /go to login/i });
      expect(loginButton).toBeInTheDocument();
    });
  });

  describe('Error State', () => {
    it('should show error message for invalid token', async () => {
      const mockPost = vi.mocked(apiClient.post);
      mockPost.mockRejectedValue({
        response: {
          data: {
            error: 'Invalid verification token',
          },
        },
      });

      renderWithRouter('?token=invalid-token');

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

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

      expect(screen.getByText('Invalid verification token')).toBeInTheDocument();
    });

    it('should show default error message when no error detail provided', async () => {
      const mockPost = vi.mocked(apiClient.post);
      mockPost.mockRejectedValue({
        response: {
          data: {},
        },
      });

      renderWithRouter('?token=invalid-token');

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

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

      expect(screen.getByText('Failed to verify email')).toBeInTheDocument();
    });

    it('should show error icon for failed verification', async () => {
      const mockPost = vi.mocked(apiClient.post);
      mockPost.mockRejectedValue({
        response: {
          data: {
            error: 'Token expired',
          },
        },
      });

      const { container } = renderWithRouter('?token=expired-token');

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

      await waitFor(() => {
        const errorIcon = container.querySelector('.bg-red-100');
        expect(errorIcon).toBeInTheDocument();
      });
    });

    it('should show helpful message about requesting new link', async () => {
      const mockPost = vi.mocked(apiClient.post);
      mockPost.mockRejectedValue({
        response: {
          data: {
            error: 'Token expired',
          },
        },
      });

      renderWithRouter('?token=expired-token');

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

      await waitFor(() => {
        expect(
          screen.getByText(
            'If you need a new verification link, please sign in and request one from your profile settings.'
          )
        ).toBeInTheDocument();
      });
    });

    it('should navigate to login from error state', async () => {
      const mockPost = vi.mocked(apiClient.post);
      mockPost.mockRejectedValue({
        response: {
          data: {
            error: 'Token expired',
          },
        },
      });

      renderWithRouter('?token=expired-token');

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

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

      const loginButton = screen.getByRole('button', { name: /go to login/i });
      expect(loginButton).toBeInTheDocument();
    });

    it('should handle network errors gracefully', async () => {
      const mockPost = vi.mocked(apiClient.post);
      mockPost.mockRejectedValue(new Error('Network error'));

      renderWithRouter('?token=test-token');

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

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

      expect(screen.getByText('Failed to verify email')).toBeInTheDocument();
    });
  });

  describe('Edge Cases', () => {
    it('should handle empty token parameter', () => {
      renderWithRouter('?token=');

      expect(screen.getByText('Invalid Link')).toBeInTheDocument();
    });

    it('should handle multiple query parameters', () => {
      renderWithRouter('?token=test-token&utm_source=email&extra=param');

      expect(screen.getByText('Verify Your Email')).toBeInTheDocument();
      const verifyButton = screen.getByRole('button', { name: /confirm verification/i });
      expect(verifyButton).toBeInTheDocument();
    });

    it('should handle very long tokens', async () => {
      const longToken = 'a'.repeat(500);
      const mockPost = vi.mocked(apiClient.post);
      mockPost.mockResolvedValue({ data: { detail: 'Email verified successfully.' } });

      renderWithRouter(`?token=${longToken}`);

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

      await waitFor(() => {
        expect(mockPost).toHaveBeenCalledWith('/auth/email/verify/', { token: longToken });
      });
    });

    it('should handle special characters in token', async () => {
      const specialToken = 'token-with-special_chars.123+abc=xyz';
      const mockPost = vi.mocked(apiClient.post);
      mockPost.mockResolvedValue({ data: { detail: 'Email verified successfully.' } });

      renderWithRouter(`?token=${encodeURIComponent(specialToken)}`);

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

      await waitFor(() => {
        expect(mockPost).toHaveBeenCalled();
      });
    });

    it('should not allow multiple simultaneous verification attempts', async () => {
      const mockPost = vi.mocked(apiClient.post);
      let resolvePromise: (value: any) => void;
      const pendingPromise = new Promise((resolve) => {
        resolvePromise = resolve;
      });
      mockPost.mockReturnValue(pendingPromise as any);

      renderWithRouter('?token=test-token');

      const verifyButton = screen.getByRole('button', { name: /confirm verification/i });

      // Click multiple times
      fireEvent.click(verifyButton);
      fireEvent.click(verifyButton);
      fireEvent.click(verifyButton);

      await waitFor(() => {
        expect(screen.getByText('Verifying Your Email')).toBeInTheDocument();
      });

      // API should only be called once
      expect(mockPost).toHaveBeenCalledTimes(1);

      // Resolve the promise
      resolvePromise!({ data: { detail: 'Email verified successfully.' } });
    });
  });

  describe('Redirect URL Construction', () => {
    it('should use correct protocol in redirect URL', async () => {
      const mockPost = vi.mocked(apiClient.post);
      mockPost.mockResolvedValue({ data: { detail: 'Email verified successfully.' } });

      (window as any).location.protocol = 'https:';

      renderWithRouter('?token=test-token');

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

      await waitFor(() => {
        expect(screen.getByText('Email Verified!')).toBeInTheDocument();
      });

      // Protocol should be used in redirect construction
      expect((window as any).location.protocol).toBe('https:');
    });

    it('should include port in redirect URL when present', async () => {
      const mockPost = vi.mocked(apiClient.post);
      mockPost.mockResolvedValue({ data: { detail: 'Email verified successfully.' } });

      (window as any).location.port = '3000';

      renderWithRouter('?token=test-token');

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

      await waitFor(() => {
        expect(screen.getByText('Email Verified!')).toBeInTheDocument();
      });

      expect((window as any).location.port).toBe('3000');
    });

    it('should handle empty port correctly', async () => {
      const mockPost = vi.mocked(apiClient.post);
      mockPost.mockResolvedValue({ data: { detail: 'Email verified successfully.' } });

      (window as any).location.port = '';

      renderWithRouter('?token=test-token');

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

      await waitFor(() => {
        expect(screen.getByText('Email Verified!')).toBeInTheDocument();
      });

      expect((window as any).location.port).toBe('');
    });
  });

  describe('Complete User Flows', () => {
    it('should support complete successful verification flow', async () => {
      const mockPost = vi.mocked(apiClient.post);
      mockPost.mockResolvedValue({ data: { detail: 'Email verified successfully.' } });

      const { deleteCookie } = await import('../../utils/cookies');

      renderWithRouter('?token=complete-flow-token');

      // Initial state - show verify button
      expect(screen.getByText('Verify Your Email')).toBeInTheDocument();
      expect(screen.getByRole('button', { name: /confirm verification/i })).toBeInTheDocument();

      // User clicks verify
      const verifyButton = screen.getByRole('button', { name: /confirm verification/i });
      fireEvent.click(verifyButton);

      // Loading state
      await waitFor(() => {
        expect(screen.getByText('Verifying Your Email')).toBeInTheDocument();
      });

      // Success state
      await waitFor(() => {
        expect(screen.getByText('Email Verified!')).toBeInTheDocument();
      });

      // Verify cookies were cleared
      expect(deleteCookie).toHaveBeenCalledWith('access_token');
      expect(deleteCookie).toHaveBeenCalledWith('refresh_token');

      // User can navigate to login
      const loginButton = screen.getByRole('button', { name: /go to login/i });
      expect(loginButton).toBeInTheDocument();
    });

    it('should support complete failed verification flow', async () => {
      const mockPost = vi.mocked(apiClient.post);
      mockPost.mockRejectedValue({
        response: {
          data: {
            error: 'Verification token has expired',
          },
        },
      });

      renderWithRouter('?token=expired-flow-token');

      // Initial state
      expect(screen.getByText('Verify Your Email')).toBeInTheDocument();

      // User clicks verify
      const verifyButton = screen.getByRole('button', { name: /confirm verification/i });
      fireEvent.click(verifyButton);

      // Loading state
      await waitFor(() => {
        expect(screen.getByText('Verifying Your Email')).toBeInTheDocument();
      });

      // Error state
      await waitFor(() => {
        expect(screen.getByText('Verification Failed')).toBeInTheDocument();
      });

      expect(screen.getByText('Verification token has expired')).toBeInTheDocument();

      // User can navigate to login
      const loginButton = screen.getByRole('button', { name: /go to login/i });
      expect(loginButton).toBeInTheDocument();
    });

    it('should support already verified user flow', async () => {
      const mockPost = vi.mocked(apiClient.post);
      mockPost.mockResolvedValue({ data: { detail: 'Email already verified.' } });

      renderWithRouter('?token=already-verified-flow');

      // User clicks verify
      const verifyButton = screen.getByRole('button', { name: /confirm verification/i });
      fireEvent.click(verifyButton);

      // Already verified state
      await waitFor(() => {
        expect(screen.getByText('Already Verified')).toBeInTheDocument();
      });

      expect(
        screen.getByText(
          'This email address has already been verified. You can use it to sign in to your account.'
        )
      ).toBeInTheDocument();

      // User can navigate to login
      const loginButton = screen.getByRole('button', { name: /go to login/i });
      expect(loginButton).toBeInTheDocument();
    });
  });

  describe('Accessibility', () => {
    it('should have proper heading hierarchy', () => {
      renderWithRouter('?token=test-token');

      const heading = screen.getByRole('heading', { name: /verify your email/i });
      expect(heading).toBeInTheDocument();
      expect(heading.tagName).toBe('H1');
    });

    it('should have accessible buttons', () => {
      renderWithRouter('?token=test-token');

      const button = screen.getByRole('button', { name: /confirm verification/i });
      expect(button).toBeInTheDocument();
      expect(button).toHaveClass('bg-brand-500');
    });

    it('should maintain focus on interactive elements', async () => {
      const mockPost = vi.mocked(apiClient.post);
      mockPost.mockResolvedValue({ data: { detail: 'Email verified successfully.' } });

      renderWithRouter('?token=test-token');

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

      await waitFor(() => {
        expect(screen.getByText('Email Verified!')).toBeInTheDocument();
      });

      const loginButton = screen.getByRole('button', { name: /go to login/i });
      expect(loginButton).toBeInTheDocument();
    });

    it('should have visible text for all states', async () => {
      const mockPost = vi.mocked(apiClient.post);
      mockPost.mockResolvedValue({ data: { detail: 'Email verified successfully.' } });

      renderWithRouter('?token=test-token');

      // Pending state has text
      expect(screen.getByText('Verify Your Email')).toBeInTheDocument();

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

      // Loading state has text
      await waitFor(() => {
        expect(screen.getByText('Verifying Your Email')).toBeInTheDocument();
      });

      // Success state has text
      await waitFor(() => {
        expect(screen.getByText('Email Verified!')).toBeInTheDocument();
      });
    });
  });
});
