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 { useAuthStore } from '@/features/auth/store/authStore';
import { useToast } from '@/hooks/useToast';

// Mock useAuthStore
const mockRegister = vi.fn();
vi.mock('@/features/auth/store/authStore', () => ({
  useAuthStore: 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 mockUseAuthStore = vi.mocked(useAuthStore);

  beforeEach(() => {
    vi.clearAllMocks();
    mockShowSuccess.mockClear();
    mockShowError.mockClear();
    mockNavigate.mockClear();
    mockRegister.mockClear();
    mockUseAuthStore.mockReturnValue({
      register: mockRegister,
      error: null,
    } as any);
  });

  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();
    mockRegister.mockRejectedValue({
      message: 'Email already exists',
      code: '409',
    });

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

    const emailInput = screen.getByLabelText(/^email$/i);
    const usernameInput = screen.getByLabelText(/username/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(usernameInput, 'testuser');
    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,
      },
    };
    mockRegister.mockResolvedValue(undefined);

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

    const emailInput = screen.getByLabelText(/^email$/i);
    const usernameInput = screen.getByLabelText(/username/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(usernameInput, 'testuser');
    await user.type(passwordInput, 'SecurePass123!');
    await user.type(confirmPasswordInput, 'SecurePass123!');
    await user.click(submitButton);

    await waitFor(() => {
      expect(mockRegister).toHaveBeenCalledWith({
        email: 'test@example.com',
        username: 'testuser',
        password: 'SecurePass123!',
        password_confirm: 'SecurePass123!',
      });
      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;
    });
    mockRegister.mockReturnValue(registerPromise);

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

    const emailInput = screen.getByLabelText(/email/i);
    const usernameInput = screen.getByLabelText(/username/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(usernameInput, 'testuser');
    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 - either error message or aria-invalid
      const errorMessage = screen.queryByText(/invalid email/i);
      const hasInvalidAria = emailInput.getAttribute('aria-invalid') === 'true';
      expect(errorMessage || hasInvalidAria).toBeTruthy();
    });

    // Clear and type valid email
    await user.clear(emailInput);
    await user.type(emailInput, 'test@example.com');

    await waitFor(() => {
      // Check that validation passes - aria-invalid should be false or not set
      const ariaInvalid = emailInput.getAttribute('aria-invalid');
      expect(ariaInvalid === 'false' || ariaInvalid === null).toBe(true);
    });
  });

  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 aria-invalid attribute
        const errorMessage = screen.queryByText(/invalid email/i);
        const hasInvalidAria =
          emailInput.getAttribute('aria-invalid') === 'true';
        expect(errorMessage || hasInvalidAria).toBeTruthy();
      },
      { timeout: 2000 },
    );
  });

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

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

    const emailInput = screen.getByLabelText(/^email$/i);
    const usernameInput = screen.getByLabelText(/username/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(usernameInput, 'testuser');
    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();
    mockRegister.mockResolvedValue(undefined);

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

    const emailInput = screen.getByLabelText(/^email$/i);
    const usernameInput = screen.getByLabelText(/username/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(usernameInput, 'newuser');
    await user.type(passwordInput, 'SecurePass123!');
    await user.type(confirmPasswordInput, 'SecurePass123!');
    await user.click(submitButton);

    await waitFor(() => {
      expect(mockRegister).toHaveBeenCalled();
      expect(mockShowSuccess).toHaveBeenCalledWith(
        'Registration successful! Welcome to Veza.',
      );
      expect(mockNavigate).toHaveBeenCalledWith('/dashboard');
    });
  });
});
