import React from 'react';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { BrowserRouter } from 'react-router-dom';
import Register from '../Register';
import { AuthContext } from '../../context/AuthContext';

const mockedNavigate = jest.fn();
jest.mock('react-router-dom', () => ({
  ...jest.requireActual('react-router-dom'),
  Navigate: ({ to }) => {
    mockedNavigate(to);
    return null;
  },
}));

describe('Register Component', () => {
  const mockRegister = jest.fn();

  const mockAuthContext = {
    auth: {
      isAuthenticated: false,
      loading: false,
      user: null,
    },
    register: mockRegister,
  };

  const renderRegister = (contextValue = mockAuthContext) => {
    return render(
      <BrowserRouter>
        <AuthContext.Provider value={contextValue}>
          <Register />
        </AuthContext.Provider>
      </BrowserRouter>
    );
  };

  beforeEach(() => {
    mockRegister.mockClear();
    mockedNavigate.mockClear();
  });

  describe('Rendering', () => {
    it('should render registration form', () => {
      renderRegister();

      expect(screen.getByRole('heading', { name: /register/i })).toBeInTheDocument();
      expect(screen.getByPlaceholderText(/name/i)).toBeInTheDocument();
      expect(screen.getByPlaceholderText(/email/i)).toBeInTheDocument();
      expect(screen.getByPlaceholderText(/^password$/i)).toBeInTheDocument();
      expect(screen.getByPlaceholderText(/confirm password/i)).toBeInTheDocument();
      expect(screen.getByRole('button', { name: /register/i })).toBeInTheDocument();
    });

    it('should render all required input fields', () => {
      renderRegister();

      const nameInput = screen.getByPlaceholderText(/name/i);
      const emailInput = screen.getByPlaceholderText(/email/i);
      const passwordInput = screen.getByPlaceholderText(/^password$/i);
      const confirmPasswordInput = screen.getByPlaceholderText(/confirm password/i);

      expect(nameInput).toBeRequired();
      expect(emailInput).toBeRequired();
      expect(passwordInput).toBeRequired();
      expect(confirmPasswordInput).toBeRequired();
    });
  });

  describe('Form Input Changes', () => {
    it('should update name field on change', () => {
      renderRegister();

      const nameInput = screen.getByPlaceholderText(/name/i);
      fireEvent.change(nameInput, { target: { value: 'John Doe' } });

      expect(nameInput).toHaveValue('John Doe');
    });

    it('should update email field on change', () => {
      renderRegister();

      const emailInput = screen.getByPlaceholderText(/email/i);
      fireEvent.change(emailInput, { target: { value: 'john@example.com' } });

      expect(emailInput).toHaveValue('john@example.com');
    });

    it('should update password field on change', () => {
      renderRegister();

      const passwordInput = screen.getByPlaceholderText(/^password$/i);
      fireEvent.change(passwordInput, { target: { value: 'password123' } });

      expect(passwordInput).toHaveValue('password123');
    });

    it('should update confirm password field on change', () => {
      renderRegister();

      const confirmPasswordInput = screen.getByPlaceholderText(/confirm password/i);
      fireEvent.change(confirmPasswordInput, { target: { value: 'password123' } });

      expect(confirmPasswordInput).toHaveValue('password123');
    });
  });

  describe('Form Submission', () => {
    it('should call register function with valid data', async () => {
      mockRegister.mockResolvedValue({ success: true });
      renderRegister();

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

      fireEvent.change(nameInput, { target: { value: 'John Doe' } });
      fireEvent.change(emailInput, { target: { value: 'john@example.com' } });
      fireEvent.change(passwordInput, { target: { value: 'password123' } });
      fireEvent.change(confirmPasswordInput, { target: { value: 'password123' } });
      fireEvent.click(submitButton);

      await waitFor(() => {
        expect(mockRegister).toHaveBeenCalledWith('John Doe', 'john@example.com', 'password123');
      });
    });

    it('should disable form during submission', async () => {
      mockRegister.mockImplementation(() => new Promise(resolve => setTimeout(resolve, 100)));
      renderRegister();

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

      fireEvent.change(nameInput, { target: { value: 'John Doe' } });
      fireEvent.change(emailInput, { target: { value: 'john@example.com' } });
      fireEvent.change(passwordInput, { target: { value: 'password123' } });
      fireEvent.change(confirmPasswordInput, { target: { value: 'password123' } });
      fireEvent.click(submitButton);

      await waitFor(() => {
        expect(nameInput).toBeDisabled();
        expect(emailInput).toBeDisabled();
        expect(passwordInput).toBeDisabled();
        expect(confirmPasswordInput).toBeDisabled();
        expect(submitButton).toBeDisabled();
      });
    });
  });

  describe('Password Validation', () => {
    it('should validate minimum password length', async () => {
      renderRegister();

      const passwordInput = screen.getByPlaceholderText(/^password$/i);
      const confirmPasswordInput = screen.getByPlaceholderText(/confirm password/i);

      fireEvent.change(passwordInput, { target: { value: '12345' } });
      fireEvent.change(confirmPasswordInput, { target: { value: '12345' } });

      // Password should have minLength attribute
      expect(passwordInput).toHaveAttribute('minLength');
    });

    it('should show error when passwords do not match', async () => {
      renderRegister();

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

      fireEvent.change(nameInput, { target: { value: 'John Doe' } });
      fireEvent.change(emailInput, { target: { value: 'john@example.com' } });
      fireEvent.change(passwordInput, { target: { value: 'password123' } });
      fireEvent.change(confirmPasswordInput, { target: { value: 'different' } });
      fireEvent.click(submitButton);

      // Should not call register if passwords don't match
      expect(mockRegister).not.toHaveBeenCalled();
    });
  });

  describe('Authentication State', () => {
    it('should redirect to /map when already authenticated', () => {
      const authenticatedContext = {
        auth: {
          isAuthenticated: true,
          loading: false,
          user: { name: 'Test User' },
        },
        register: mockRegister,
      };

      renderRegister(authenticatedContext);

      expect(mockedNavigate).toHaveBeenCalledWith('/map');
    });

    it('should show loading spinner when auth is loading', () => {
      const loadingContext = {
        auth: {
          isAuthenticated: false,
          loading: true,
          user: null,
        },
        register: mockRegister,
      };

      renderRegister(loadingContext);

      expect(screen.getByRole('status')).toBeInTheDocument();
    });
  });

  describe('Field Types', () => {
    it('should have correct input types', () => {
      renderRegister();

      const emailInput = screen.getByPlaceholderText(/email/i);
      const passwordInput = screen.getByPlaceholderText(/^password$/i);
      const confirmPasswordInput = screen.getByPlaceholderText(/confirm password/i);

      expect(emailInput).toHaveAttribute('type', 'email');
      expect(passwordInput).toHaveAttribute('type', 'password');
      expect(confirmPasswordInput).toHaveAttribute('type', 'password');
    });
  });

  describe('Error Handling', () => {
    it('should handle registration errors', async () => {
      const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
      mockRegister.mockRejectedValue(new Error('Registration failed'));

      renderRegister();

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

      fireEvent.change(nameInput, { target: { value: 'John Doe' } });
      fireEvent.change(emailInput, { target: { value: 'john@example.com' } });
      fireEvent.change(passwordInput, { target: { value: 'password123' } });
      fireEvent.change(confirmPasswordInput, { target: { value: 'password123' } });
      fireEvent.click(submitButton);

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

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