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

// Mock functions
const mockRegister = vi.fn();
const mockClearError = vi.fn();

// Mock des services
vi.mock('@/features/auth/store/authStore', () => ({
  useAuthStore: () => ({
    register: mockRegister,
    isLoading: false,
    error: null,
    clearError: mockClearError,
  }),
}));

const createTestQueryClient = () =>
  new QueryClient({
    defaultOptions: {
      queries: { retry: false },
      mutations: { retry: false },
    },
  });

const TestWrapper = ({ children }: { children: React.ReactNode }) => {
  const queryClient = createTestQueryClient();
  return (
    <QueryClientProvider client={queryClient}>
      <BrowserRouter>{children}</BrowserRouter>
    </QueryClientProvider>
  );
};

describe('RegisterForm Component', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    mockRegister.mockResolvedValue({
      user: { id: 1, email: 'test@example.com' },
      tokens: { accessToken: 'token123' },
    });
  });

  it('renders register form correctly', () => {
    render(
      <TestWrapper>
        <RegisterForm />
      </TestWrapper>,
    );

    expect(screen.getByLabelText(/prenom/i)).toBeInTheDocument();
    expect(screen.getByLabelText(/nom/i)).toBeInTheDocument();
    expect(screen.getByLabelText(/nom d'utilisateur/i)).toBeInTheDocument();
    expect(screen.getByLabelText(/email/i)).toBeInTheDocument();
    expect(screen.getByLabelText(/mot de passe/i)).toBeInTheDocument();
    expect(
      screen.getByLabelText(/confirmer le mot de passe/i),
    ).toBeInTheDocument();
    expect(
      screen.getByRole('button', { name: /créer le compte/i }),
    ).toBeInTheDocument();
  });

  it('shows validation error for short username', async () => {
    render(
      <TestWrapper>
        <RegisterForm />
      </TestWrapper>,
    );

    const usernameInput = screen.getByLabelText(/nom d'utilisateur/i);
    const submitButton = screen.getByRole('button', {
      name: /créer le compte/i,
    });

    fireEvent.change(usernameInput, { target: { value: 'ab' } });
    fireEvent.click(submitButton);

    await waitFor(() => {
      expect(
        screen.getByText(/doit contenir au moins 3 caractères/i),
      ).toBeInTheDocument();
    });
  });

  it('shows validation error for invalid email', async () => {
    render(
      <TestWrapper>
        <RegisterForm />
      </TestWrapper>,
    );

    const usernameInput = screen.getByLabelText(/nom d'utilisateur/i);
    const emailInput = screen.getByLabelText(/^email$/i);
    const passwordInput = screen.getByLabelText(/^mot de passe$/i);
    const confirmPasswordInput = screen.getByLabelText(
      /confirmer le mot de passe/i,
    );
    const submitButton = screen.getByRole('button', {
      name: /créer le compte/i,
    });

    fireEvent.change(usernameInput, { target: { value: 'testuser' } });
    fireEvent.change(emailInput, { target: { value: 'invalid-email' } });
    fireEvent.change(passwordInput, { target: { value: 'password123' } });
    fireEvent.change(confirmPasswordInput, {
      target: { value: 'password123' },
    });
    fireEvent.click(submitButton);

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

  it('shows validation error for short password', async () => {
    render(
      <TestWrapper>
        <RegisterForm />
      </TestWrapper>,
    );

    const passwordInput = screen.getByLabelText(/^mot de passe$/i);
    const submitButton = screen.getByRole('button', {
      name: /créer le compte/i,
    });

    fireEvent.change(passwordInput, { target: { value: '1234567' } });
    fireEvent.click(submitButton);

    await waitFor(() => {
      expect(
        screen.getByText(/doit contenir au moins 8 caractères/i),
      ).toBeInTheDocument();
    });
  });

  it('shows validation error when passwords do not match', async () => {
    render(
      <TestWrapper>
        <RegisterForm />
      </TestWrapper>,
    );

    const passwordInput = screen.getByLabelText(/^mot de passe$/i);
    const confirmPasswordInput = screen.getByLabelText(
      /confirmer le mot de passe/i,
    );
    const submitButton = screen.getByRole('button', {
      name: /créer le compte/i,
    });

    fireEvent.change(passwordInput, { target: { value: 'password123' } });
    fireEvent.change(confirmPasswordInput, {
      target: { value: 'password456' },
    });
    fireEvent.click(submitButton);

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

  it('submits form with valid data', async () => {
    render(
      <TestWrapper>
        <RegisterForm />
      </TestWrapper>,
    );

    const usernameInput = screen.getByLabelText(/nom d'utilisateur/i);
    const emailInput = screen.getByLabelText(/^email$/i);
    const passwordInput = screen.getByLabelText(/^mot de passe$/i);
    const confirmPasswordInput = screen.getByLabelText(
      /confirmer le mot de passe/i,
    );
    const submitButton = screen.getByRole('button', {
      name: /créer le compte/i,
    });

    fireEvent.change(usernameInput, { target: { value: 'testuser' } });
    fireEvent.change(emailInput, { target: { value: 'test@example.com' } });
    fireEvent.change(passwordInput, { target: { value: 'password123' } });
    fireEvent.change(confirmPasswordInput, {
      target: { value: 'password123' },
    });
    fireEvent.click(submitButton);

    await waitFor(() => {
      expect(mockRegister).toHaveBeenCalledWith(
        expect.objectContaining({
          username: 'testuser',
          email: 'test@example.com',
          password: 'password123',
        }),
      );
    });
  });

  it('calls clearError when form is submitted', async () => {
    render(
      <TestWrapper>
        <RegisterForm />
      </TestWrapper>,
    );

    const usernameInput = screen.getByLabelText(/nom d'utilisateur/i);
    const emailInput = screen.getByLabelText(/^email$/i);
    const passwordInput = screen.getByLabelText(/^mot de passe$/i);
    const confirmPasswordInput = screen.getByLabelText(
      /confirmer le mot de passe/i,
    );
    const submitButton = screen.getByRole('button', {
      name: /créer le compte/i,
    });

    fireEvent.change(usernameInput, { target: { value: 'testuser' } });
    fireEvent.change(emailInput, { target: { value: 'test@example.com' } });
    fireEvent.change(passwordInput, { target: { value: 'password123' } });
    fireEvent.change(confirmPasswordInput, {
      target: { value: 'password123' },
    });
    fireEvent.click(submitButton);

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

  it('navigates to login page when clicking login link', () => {
    render(
      <TestWrapper>
        <RegisterForm />
      </TestWrapper>,
    );

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

  it('toggles password visibility', () => {
    render(
      <TestWrapper>
        <RegisterForm />
      </TestWrapper>,
    );

    const passwordInput = screen.getByLabelText(/^mot de passe$/i);
    const passwordInputContainer = passwordInput.closest('.relative');
    const togglePasswordButton =
      passwordInputContainer?.querySelector('button');

    expect(passwordInput).toHaveAttribute('type', 'password');
    expect(togglePasswordButton).toBeInTheDocument();

    if (togglePasswordButton) {
      fireEvent.click(togglePasswordButton);
      expect(passwordInput).toHaveAttribute('type', 'text');

      fireEvent.click(togglePasswordButton);
      expect(passwordInput).toHaveAttribute('type', 'password');
    }
  });

  it('toggles confirm password visibility', () => {
    render(
      <TestWrapper>
        <RegisterForm />
      </TestWrapper>,
    );

    const confirmPasswordInput = screen.getByLabelText(
      /confirmer le mot de passe/i,
    );
    const confirmPasswordContainer = confirmPasswordInput.closest('.relative');
    const toggleConfirmPasswordButton =
      confirmPasswordContainer?.querySelector('button');

    expect(confirmPasswordInput).toHaveAttribute('type', 'password');
    expect(toggleConfirmPasswordButton).toBeInTheDocument();

    if (toggleConfirmPasswordButton) {
      fireEvent.click(toggleConfirmPasswordButton);
      expect(confirmPasswordInput).toHaveAttribute('type', 'text');

      fireEvent.click(toggleConfirmPasswordButton);
      expect(confirmPasswordInput).toHaveAttribute('type', 'password');
    }
  });
});
