"""
Tests for User Model.

Test Coverage:
- User creation
- Email validation
- Password hashing
- User manager methods
- User properties
"""
import pytest
from django.db import IntegrityError
from django.contrib.auth import get_user_model

User = get_user_model()


@pytest.mark.django_db
class TestUserModel:
    """Tests for the User model."""

    def test_create_user_with_email(self, create_user):
        """Test creating a user with email is successful."""
        email = 'user@example.com'
        password = 'testpass123'
        
        user = create_user(email=email, password=password)
        
        assert user.email == email
        assert user.check_password(password)
        assert user.is_active
        assert not user.is_staff
        assert not user.is_superuser

    def test_user_email_normalized(self, create_user):
        """Test email is normalized for new users."""
        sample_emails = [
            ('test1@EXAMPLE.com', 'test1@example.com'),
            ('Test2@Example.com', 'Test2@example.com'),
            ('TEST3@EXAMPLE.COM', 'TEST3@example.com'),
            ('test4@example.COM', 'test4@example.com'),
        ]
        
        for original, expected in sample_emails:
            user = create_user(email=original)
            assert user.email == expected
            user.delete()

    def test_user_email_unique(self, create_user):
        """Test that duplicate emails raise error."""
        email = 'duplicate@example.com'
        create_user(email=email)
        
        with pytest.raises(IntegrityError):
            create_user(email=email)

    def test_create_user_without_email_raises_error(self):
        """Test creating a user without email or phone raises ValueError."""
        with pytest.raises(ValueError, match='email or phone'):
            User.objects.create_user(email='', password='testpass123')

    def test_create_superuser(self):
        """Test creating a superuser."""
        user = User.objects.create_superuser(
            email='admin@example.com',
            password='adminpass123',
        )
        
        assert user.is_superuser
        assert user.is_staff

    def test_create_superuser_without_is_staff_raises_error(self):
        """Test creating superuser with is_staff=False raises error."""
        with pytest.raises(ValueError, match='is_staff=True'):
            User.objects.create_superuser(
                email='admin@example.com',
                password='adminpass123',
                is_staff=False,
            )

    def test_create_superuser_without_is_superuser_raises_error(self):
        """Test creating superuser with is_superuser=False raises error."""
        with pytest.raises(ValueError, match='is_superuser=True'):
            User.objects.create_superuser(
                email='admin@example.com',
                password='adminpass123',
                is_superuser=False,
            )

    def test_user_string_representation(self, create_user):
        """Test the user string representation."""
        user = create_user(email='string@example.com')
        assert str(user) == 'string@example.com'

    def test_user_full_name(self, create_user):
        """Test getting user's full name."""
        user = create_user(
            email='fullname@example.com',
            first_name='علی',
            last_name='احمدی',
        )
        assert user.get_full_name() == 'علی احمدی'

    def test_user_short_name(self, create_user):
        """Test getting user's short name."""
        user = create_user(
            email='shortname@example.com',
            first_name='علی',
            last_name='احمدی',
        )
        assert user.get_short_name() == 'علی'

    def test_user_password_is_hashed(self, create_user):
        """Test that password is hashed when user is created."""
        password = 'plainpassword123'
        user = create_user(email='hashed@example.com', password=password)
        
        # Password should not be stored as plain text
        assert user.password != password
        # But should verify correctly
        assert user.check_password(password)

    def test_inactive_user_cannot_login(self, create_user):
        """Test that inactive user is created but cannot authenticate."""
        user = create_user(
            email='inactive@example.com',
            password='testpass123',
            is_active=False,
        )
        
        assert not user.is_active
        # User exists but is inactive
        assert User.objects.filter(email='inactive@example.com').exists()


@pytest.mark.django_db
class TestUserQuerySet:
    """Tests for User QuerySet and Manager."""

    def test_get_user_by_email(self, create_user):
        """Test retrieving user by email."""
        email = 'getbyemail@example.com'
        created_user = create_user(email=email)
        
        retrieved_user = User.objects.get(email=email)
        
        assert retrieved_user.id == created_user.id

    def test_filter_active_users(self, create_user):
        """Test filtering active users."""
        create_user(email='active1@example.com', is_active=True)
        create_user(email='active2@example.com', is_active=True)
        create_user(email='inactive@example.com', is_active=False)
        
        active_users = User.objects.filter(is_active=True)
        
        assert active_users.count() >= 2
        assert not active_users.filter(email='inactive@example.com').exists()

    def test_filter_staff_users(self, create_user):
        """Test filtering staff users."""
        create_user(email='staff@example.com', is_staff=True)
        create_user(email='regular@example.com', is_staff=False)
        
        staff_users = User.objects.filter(is_staff=True)
        
        assert staff_users.filter(email='staff@example.com').exists()
        assert not staff_users.filter(email='regular@example.com').exists()
