import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { BrowserRouter } from 'react-router-dom';
import { Register } from './Register';
import { register } from '@/services/api/auth';
import { tokenManager } from '@/utils/token-manager';
import { useToast } from '@/hooks/useToast';

// Mock the registration API service
vi.mock('@/services/api/auth', () => ({
  register: vi.fn(),
}));

// Mock tokenManager
vi.mock('@/utils/token-manager', () => ({
  tokenManager: {
    setTokens: vi.fn(),
  },
}));

// Mock useToast
const mockShowSuccess = vi.fn();
const mockShowError = vi.fn();
vi.mock('@/hooks/useToast', () => ({
  useToast: () => ({
    success: mockShowSuccess,
    error: mockShowError,
  }),
}));

// Mock react-router-dom
const mockNavigate = vi.fn();
vi.mock('react-router-dom', async () => {
  const actual = await vi.importActual('react-router-dom');
  return {
    ...actual,
    useNavigate: () => mockNavigate,
  };
});

describe('Register', () => {
  const mockRegisterFn = vi.mocked(register);
  const mockSetTokens = vi.mocked(tokenManager.setTokens);

  beforeEach(() => {
    vi.clearAllMocks();
    mockShowSuccess.mockClear();
    mockShowError.mockClear();
    mockNavigate.mockClear();
    mockSetTokens.mockClear();
  });

  it('should render the registration form', () => {
    render(
      <BrowserRouter>
        <Register />
      </BrowserRouter>
    );

    expect(screen.getByText('Create an account')).toBeInTheDocument();
    expect(screen.getByLabelText(/^email$/i)).toBeInTheDocument();
    expect(screen.getByLabelText(/^password$/i)).toBeInTheDocument();
    expect(screen.getByLabelText(/confirm password/i)).toBeInTheDocument();
    expect(screen.getByRole('button', { name: /register/i })).toBeInTheDocument();
  });

  it('should display error message when registration fails', async () => {
    const user = userEvent.setup();
    mockRegisterFn.mockRejectedValue({
      message: 'Email already exists',
      code: '409',
    });

    render(
      <BrowserRouter>
        <Register />
      </BrowserRouter>
    );

    const emailInput = screen.getByLabelText(/^email$/i);
    const passwordInput = screen.getByLabelText(/^password$/i);
    const confirmPasswordInput = screen.getByLabelText(/confirm password/i);
    const submitButton = screen.getByRole('button', { name: /register/i });

    await user.type(emailInput, 'test@example.com');
    await user.type(passwordInput, 'SecurePass123!');
    await user.type(confirmPasswordInput, 'SecurePass123!');
    await user.click(submitButton);

    await waitFor(() => {
      expect(screen.getByText(/email already exists/i)).toBeInTheDocument();
    });
  });

  it('should have email input field', () => {
    render(
      <BrowserRouter>
        <Register />
      </BrowserRouter>
    );

    const emailInput = screen.getByLabelText(/email/i);
    expect(emailInput).toBeInTheDocument();
    expect(emailInput).toHaveAttribute('type', 'email');
  });

  it('should validate password minimum length', async () => {
    const user = userEvent.setup();
    render(
      <BrowserRouter>
        <Register />
      </BrowserRouter>
    );

    const emailInput = screen.getByLabelText(/email/i);
    const passwordInput = screen.getByLabelText(/^password$/i);
    const submitButton = screen.getByRole('button', { name: /register/i });

    await user.type(emailInput, 'test@example.com');
    await user.type(passwordInput, 'short');
    await user.click(submitButton);

    await waitFor(() => {
      expect(
        screen.getByText(/password must be at least 12 characters/i)
      ).toBeInTheDocument();
    });
  });

  it('should have password confirmation input field', () => {
    render(
      <BrowserRouter>
        <Register />
      </BrowserRouter>
    );

    const confirmPasswordInput = screen.getByLabelText(/confirm password/i);
    expect(confirmPasswordInput).toBeInTheDocument();
    expect(confirmPasswordInput).toHaveAttribute('type', 'password');
  });

  it('should submit form with valid data and redirect to dashboard', async () => {
    const user = userEvent.setup();
    const mockResponse = {
      user: {
        id: 1,
        email: 'test@example.com',
      },
      token: {
        access_token: 'test-access-token',
        refresh_token: 'test-refresh-token',
        expires_in: 900,
      },
    };
    mockRegisterFn.mockResolvedValue(mockResponse);

    render(
      <BrowserRouter>
        <Register />
      </BrowserRouter>
    );

    const emailInput = screen.getByLabelText(/^email$/i);
    const passwordInput = screen.getByLabelText(/^password$/i);
    const confirmPasswordInput = screen.getByLabelText(/confirm password/i);
    const submitButton = screen.getByRole('button', { name: /register/i });

    await user.type(emailInput, 'test@example.com');
    await user.type(passwordInput, 'SecurePass123!');
    await user.type(confirmPasswordInput, 'SecurePass123!');
    await user.click(submitButton);

    await waitFor(() => {
      expect(mockRegisterFn).toHaveBeenCalledWith({
        email: 'test@example.com',
        password: 'SecurePass123!',
        password_confirm: 'SecurePass123!',
      });
      expect(mockSetTokens).toHaveBeenCalledWith(
        'test-access-token',
        'test-refresh-token',
        false
      );
      expect(mockShowSuccess).toHaveBeenCalledWith('Registration successful! Welcome to Veza.');
      expect(mockNavigate).toHaveBeenCalledWith('/dashboard');
    });
  });

  it('should show loading state during submission', async () => {
    const user = userEvent.setup();
    let resolveRegister: (value: any) => void;
    const registerPromise = new Promise<any>((resolve) => {
      resolveRegister = resolve;
    });
    mockRegisterFn.mockReturnValue(registerPromise);

    render(
      <BrowserRouter>
        <Register />
      </BrowserRouter>
    );

    const emailInput = screen.getByLabelText(/email/i);
    const passwordInput = screen.getByLabelText(/^password$/i);
    const confirmPasswordInput = screen.getByLabelText(/confirm password/i);
    const submitButton = screen.getByRole('button', { name: /register/i });

    await user.type(emailInput, 'test@example.com');
    await user.type(passwordInput, 'SecurePass123!');
    await user.type(confirmPasswordInput, 'SecurePass123!');
    await user.click(submitButton);

    await waitFor(() => {
      expect(screen.getByText(/registering.../i)).toBeInTheDocument();
    });

    resolveRegister!({
      user: { id: 1, email: 'test@example.com' },
      token: { access_token: 'token', refresh_token: 'refresh', expires_in: 900 },
    });
    await waitFor(() => {
      expect(screen.queryByText(/registering.../i)).not.toBeInTheDocument();
    });
  });

  it('should display link to login page', () => {
    render(
      <BrowserRouter>
        <Register />
      </BrowserRouter>
    );

    const loginLink = screen.getByRole('link', { name: /sign in/i });
    expect(loginLink).toBeInTheDocument();
    expect(loginLink).toHaveAttribute('href', '/login');
  });

  it('should show real-time email validation indicator', async () => {
    const user = userEvent.setup();
    render(
      <BrowserRouter>
        <Register />
      </BrowserRouter>
    );

    const emailInput = screen.getByLabelText(/^email$/i);
    
    // Type invalid email
    await user.type(emailInput, 'invalid-email');
    
    await waitFor(() => {
      // Check for invalid indicator
      const invalidIcon = emailInput.parentElement?.querySelector('.text-red-500');
      expect(invalidIcon).toBeInTheDocument();
    });

    // Clear and type valid email
    await user.clear(emailInput);
    await user.type(emailInput, 'test@example.com');
    
    await waitFor(() => {
      // Check for valid indicator
      const validIcon = emailInput.parentElement?.querySelector('.text-green-500');
      expect(validIcon).toBeInTheDocument();
    });
  });

  it('should show email validation error message in real-time', async () => {
    const user = userEvent.setup();
    render(
      <BrowserRouter>
        <Register />
      </BrowserRouter>
    );

    const emailInput = screen.getByLabelText(/^email$/i);
    
    // Type invalid email - need to type enough characters to trigger validation
    await user.type(emailInput, 'invalid@');
    
    // Wait for validation to run after typing
    await waitFor(() => {
      // Check for either the specific error message or the validation indicator
      const errorMessage = screen.queryByText(/invalid email format/i);
      const invalidIcon = emailInput.parentElement?.querySelector('.text-red-500');
      expect(errorMessage || invalidIcon).toBeTruthy();
    }, { timeout: 2000 });
  });

  it('should show error toast when registration fails', async () => {
    const user = userEvent.setup();
    mockRegisterFn.mockRejectedValue({
      message: 'Network error',
      code: 'NETWORK_ERROR',
    });

    render(
      <BrowserRouter>
        <Register />
      </BrowserRouter>
    );

    const emailInput = screen.getByLabelText(/^email$/i);
    const passwordInput = screen.getByLabelText(/^password$/i);
    const confirmPasswordInput = screen.getByLabelText(/confirm password/i);
    const submitButton = screen.getByRole('button', { name: /register/i });

    await user.type(emailInput, 'test@example.com');
    await user.type(passwordInput, 'SecurePass123!');
    await user.type(confirmPasswordInput, 'SecurePass123!');
    await user.click(submitButton);

    await waitFor(() => {
      expect(mockShowError).toHaveBeenCalledWith('Network error');
      expect(mockNavigate).not.toHaveBeenCalled();
    });
  });

  it('should store tokens and show success message on successful registration', async () => {
    const user = userEvent.setup();
    const mockResponse = {
      user: {
        id: 1,
        email: 'test@example.com',
      },
      token: {
        access_token: 'new-access-token',
        refresh_token: 'new-refresh-token',
        expires_in: 900,
      },
    };
    mockRegisterFn.mockResolvedValue(mockResponse);

    render(
      <BrowserRouter>
        <Register />
      </BrowserRouter>
    );

    const emailInput = screen.getByLabelText(/^email$/i);
    const passwordInput = screen.getByLabelText(/^password$/i);
    const confirmPasswordInput = screen.getByLabelText(/confirm password/i);
    const submitButton = screen.getByRole('button', { name: /register/i });

    await user.type(emailInput, 'newuser@example.com');
    await user.type(passwordInput, 'SecurePass123!');
    await user.type(confirmPasswordInput, 'SecurePass123!');
    await user.click(submitButton);

    await waitFor(() => {
      expect(mockSetTokens).toHaveBeenCalledWith(
        'new-access-token',
        'new-refresh-token',
        false
      );
      expect(mockShowSuccess).toHaveBeenCalledWith('Registration successful! Welcome to Veza.');
      expect(mockNavigate).toHaveBeenCalledWith('/dashboard');
    });
  });
});

