"""
Unit tests for the User model methods and properties.

Tests cover:
1. Role classification methods (is_platform_user, is_tenant_user)
2. Permission checking methods (can_manage_users, can_access_billing, etc.)
3. Property methods (full_name)
4. Business logic in save() method

Uses mocks/instances without database where possible for fast testing.
Only uses @pytest.mark.django_db when testing actual database constraints.
"""
import pytest
from unittest.mock import Mock, patch

from smoothschedule.identity.users.models import User


def create_user_instance(role, permissions=None, **kwargs):
    """
    Helper to create a minimal User instance for testing without hitting the DB.

    Args:
        role: User.Role value
        permissions: Dict of permissions (default: empty dict)
        **kwargs: Additional user attributes

    Returns:
        User instance (not saved to DB)
    """
    defaults = {
        'username': 'testuser',
        'email': 'test@example.com',
        'first_name': '',
        'last_name': '',
        'permissions': permissions or {},
    }
    defaults.update(kwargs)

    # Create instance without saving
    user = User(role=role, **defaults)
    # Set pk to simulate it exists (for methods that check)
    user.pk = 1
    return user


# =============================================================================
# Role Classification Tests
# =============================================================================

class TestIsPlatformUser:
    """Test is_platform_user() method."""

    def test_returns_true_for_superuser(self):
        user = create_user_instance(User.Role.SUPERUSER)
        assert user.is_platform_user() is True

    def test_returns_true_for_platform_manager(self):
        user = create_user_instance(User.Role.PLATFORM_MANAGER)
        assert user.is_platform_user() is True

    def test_returns_true_for_platform_sales(self):
        user = create_user_instance(User.Role.PLATFORM_SALES)
        assert user.is_platform_user() is True

    def test_returns_true_for_platform_support(self):
        user = create_user_instance(User.Role.PLATFORM_SUPPORT)
        assert user.is_platform_user() is True

    def test_returns_false_for_tenant_owner(self):
        user = create_user_instance(User.Role.TENANT_OWNER)
        assert user.is_platform_user() is False

    def test_returns_false_for_tenant_manager(self):
        user = create_user_instance(User.Role.TENANT_MANAGER)
        assert user.is_platform_user() is False

    def test_returns_false_for_tenant_staff(self):
        user = create_user_instance(User.Role.TENANT_STAFF)
        assert user.is_platform_user() is False

    def test_returns_false_for_customer(self):
        user = create_user_instance(User.Role.CUSTOMER)
        assert user.is_platform_user() is False


class TestIsTenantUser:
    """Test is_tenant_user() method."""

    def test_returns_false_for_superuser(self):
        user = create_user_instance(User.Role.SUPERUSER)
        assert user.is_tenant_user() is False

    def test_returns_false_for_platform_manager(self):
        user = create_user_instance(User.Role.PLATFORM_MANAGER)
        assert user.is_tenant_user() is False

    def test_returns_false_for_platform_sales(self):
        user = create_user_instance(User.Role.PLATFORM_SALES)
        assert user.is_tenant_user() is False

    def test_returns_false_for_platform_support(self):
        user = create_user_instance(User.Role.PLATFORM_SUPPORT)
        assert user.is_tenant_user() is False

    def test_returns_true_for_tenant_owner(self):
        user = create_user_instance(User.Role.TENANT_OWNER)
        assert user.is_tenant_user() is True

    def test_returns_true_for_tenant_manager(self):
        user = create_user_instance(User.Role.TENANT_MANAGER)
        assert user.is_tenant_user() is True

    def test_returns_true_for_tenant_staff(self):
        user = create_user_instance(User.Role.TENANT_STAFF)
        assert user.is_tenant_user() is True

    def test_returns_true_for_customer(self):
        user = create_user_instance(User.Role.CUSTOMER)
        assert user.is_tenant_user() is True


# =============================================================================
# User Management Permission Tests
# =============================================================================

class TestCanManageUsers:
    """Test can_manage_users() method."""

    def test_returns_true_for_superuser(self):
        user = create_user_instance(User.Role.SUPERUSER)
        assert user.can_manage_users() is True

    def test_returns_true_for_platform_manager(self):
        user = create_user_instance(User.Role.PLATFORM_MANAGER)
        assert user.can_manage_users() is True

    def test_returns_true_for_tenant_owner(self):
        user = create_user_instance(User.Role.TENANT_OWNER)
        assert user.can_manage_users() is True

    def test_returns_true_for_tenant_manager(self):
        user = create_user_instance(User.Role.TENANT_MANAGER)
        assert user.can_manage_users() is True

    def test_returns_false_for_platform_sales(self):
        user = create_user_instance(User.Role.PLATFORM_SALES)
        assert user.can_manage_users() is False

    def test_returns_false_for_platform_support(self):
        user = create_user_instance(User.Role.PLATFORM_SUPPORT)
        assert user.can_manage_users() is False

    def test_returns_false_for_tenant_staff(self):
        user = create_user_instance(User.Role.TENANT_STAFF)
        assert user.can_manage_users() is False

    def test_returns_false_for_customer(self):
        user = create_user_instance(User.Role.CUSTOMER)
        assert user.can_manage_users() is False


# =============================================================================
# Billing Access Permission Tests
# =============================================================================

class TestCanAccessBilling:
    """Test can_access_billing() method."""

    def test_returns_true_for_superuser(self):
        user = create_user_instance(User.Role.SUPERUSER)
        assert user.can_access_billing() is True

    def test_returns_true_for_platform_manager(self):
        user = create_user_instance(User.Role.PLATFORM_MANAGER)
        assert user.can_access_billing() is True

    def test_returns_true_for_tenant_owner(self):
        user = create_user_instance(User.Role.TENANT_OWNER)
        assert user.can_access_billing() is True

    def test_returns_false_for_platform_sales(self):
        user = create_user_instance(User.Role.PLATFORM_SALES)
        assert user.can_access_billing() is False

    def test_returns_false_for_platform_support(self):
        user = create_user_instance(User.Role.PLATFORM_SUPPORT)
        assert user.can_access_billing() is False

    def test_returns_false_for_tenant_manager(self):
        user = create_user_instance(User.Role.TENANT_MANAGER)
        assert user.can_access_billing() is False

    def test_returns_false_for_tenant_staff(self):
        user = create_user_instance(User.Role.TENANT_STAFF)
        assert user.can_access_billing() is False

    def test_returns_false_for_customer(self):
        user = create_user_instance(User.Role.CUSTOMER)
        assert user.can_access_billing() is False


# =============================================================================
# Staff Invitation Permission Tests
# =============================================================================

class TestCanInviteStaff:
    """Test can_invite_staff() method."""

    def test_returns_true_for_tenant_owner(self):
        user = create_user_instance(User.Role.TENANT_OWNER)
        assert user.can_invite_staff() is True

    def test_returns_true_for_manager_with_permission(self):
        user = create_user_instance(
            User.Role.TENANT_MANAGER,
            permissions={'can_invite_staff': True}
        )
        assert user.can_invite_staff() is True

    def test_returns_false_for_manager_without_permission(self):
        user = create_user_instance(User.Role.TENANT_MANAGER)
        assert user.can_invite_staff() is False

    def test_returns_false_for_manager_with_explicit_false_permission(self):
        user = create_user_instance(
            User.Role.TENANT_MANAGER,
            permissions={'can_invite_staff': False}
        )
        assert user.can_invite_staff() is False

    def test_returns_false_for_superuser(self):
        user = create_user_instance(User.Role.SUPERUSER)
        assert user.can_invite_staff() is False

    def test_returns_false_for_platform_manager(self):
        user = create_user_instance(User.Role.PLATFORM_MANAGER)
        assert user.can_invite_staff() is False

    def test_returns_false_for_tenant_staff(self):
        user = create_user_instance(User.Role.TENANT_STAFF)
        assert user.can_invite_staff() is False

    def test_returns_false_for_customer(self):
        user = create_user_instance(User.Role.CUSTOMER)
        assert user.can_invite_staff() is False


# =============================================================================
# Ticket Access Permission Tests
# =============================================================================

class TestCanAccessTickets:
    """Test can_access_tickets() method."""

    def test_returns_true_for_superuser(self):
        user = create_user_instance(User.Role.SUPERUSER)
        assert user.can_access_tickets() is True

    def test_returns_true_for_platform_manager(self):
        user = create_user_instance(User.Role.PLATFORM_MANAGER)
        assert user.can_access_tickets() is True

    def test_returns_true_for_platform_sales(self):
        user = create_user_instance(User.Role.PLATFORM_SALES)
        assert user.can_access_tickets() is True

    def test_returns_true_for_platform_support(self):
        user = create_user_instance(User.Role.PLATFORM_SUPPORT)
        assert user.can_access_tickets() is True

    def test_returns_true_for_tenant_owner(self):
        user = create_user_instance(User.Role.TENANT_OWNER)
        assert user.can_access_tickets() is True

    def test_returns_true_for_tenant_manager(self):
        user = create_user_instance(User.Role.TENANT_MANAGER)
        assert user.can_access_tickets() is True

    def test_returns_true_for_staff_with_permission(self):
        user = create_user_instance(
            User.Role.TENANT_STAFF,
            permissions={'can_access_tickets': True}
        )
        assert user.can_access_tickets() is True

    def test_returns_false_for_staff_without_permission(self):
        user = create_user_instance(User.Role.TENANT_STAFF)
        assert user.can_access_tickets() is False

    def test_returns_false_for_staff_with_explicit_false_permission(self):
        user = create_user_instance(
            User.Role.TENANT_STAFF,
            permissions={'can_access_tickets': False}
        )
        assert user.can_access_tickets() is False

    def test_returns_true_for_customer(self):
        user = create_user_instance(User.Role.CUSTOMER)
        assert user.can_access_tickets() is True


# =============================================================================
# Plugin Approval Permission Tests
# =============================================================================

class TestCanApprovePlugins:
    """Test can_approve_plugins() method."""

    def test_returns_true_for_superuser(self):
        user = create_user_instance(User.Role.SUPERUSER)
        assert user.can_approve_plugins() is True

    def test_returns_true_for_platform_manager_with_permission(self):
        user = create_user_instance(
            User.Role.PLATFORM_MANAGER,
            permissions={'can_approve_plugins': True}
        )
        assert user.can_approve_plugins() is True

    def test_returns_false_for_platform_manager_without_permission(self):
        user = create_user_instance(User.Role.PLATFORM_MANAGER)
        assert user.can_approve_plugins() is False

    def test_returns_true_for_platform_support_with_permission(self):
        user = create_user_instance(
            User.Role.PLATFORM_SUPPORT,
            permissions={'can_approve_plugins': True}
        )
        assert user.can_approve_plugins() is True

    def test_returns_false_for_platform_support_without_permission(self):
        user = create_user_instance(User.Role.PLATFORM_SUPPORT)
        assert user.can_approve_plugins() is False

    def test_returns_false_for_platform_sales(self):
        user = create_user_instance(User.Role.PLATFORM_SALES)
        assert user.can_approve_plugins() is False

    def test_returns_false_for_tenant_owner(self):
        user = create_user_instance(User.Role.TENANT_OWNER)
        assert user.can_approve_plugins() is False

    def test_returns_false_for_tenant_manager(self):
        user = create_user_instance(User.Role.TENANT_MANAGER)
        assert user.can_approve_plugins() is False

    def test_returns_false_for_customer(self):
        user = create_user_instance(User.Role.CUSTOMER)
        assert user.can_approve_plugins() is False


# =============================================================================
# URL Whitelist Permission Tests
# =============================================================================

class TestCanWhitelistUrls:
    """Test can_whitelist_urls() method."""

    def test_returns_true_for_superuser(self):
        user = create_user_instance(User.Role.SUPERUSER)
        assert user.can_whitelist_urls() is True

    def test_returns_true_for_platform_manager_with_permission(self):
        user = create_user_instance(
            User.Role.PLATFORM_MANAGER,
            permissions={'can_whitelist_urls': True}
        )
        assert user.can_whitelist_urls() is True

    def test_returns_false_for_platform_manager_without_permission(self):
        user = create_user_instance(User.Role.PLATFORM_MANAGER)
        assert user.can_whitelist_urls() is False

    def test_returns_true_for_platform_support_with_permission(self):
        user = create_user_instance(
            User.Role.PLATFORM_SUPPORT,
            permissions={'can_whitelist_urls': True}
        )
        assert user.can_whitelist_urls() is True

    def test_returns_false_for_platform_support_without_permission(self):
        user = create_user_instance(User.Role.PLATFORM_SUPPORT)
        assert user.can_whitelist_urls() is False

    def test_returns_false_for_platform_sales(self):
        user = create_user_instance(User.Role.PLATFORM_SALES)
        assert user.can_whitelist_urls() is False

    def test_returns_false_for_tenant_owner(self):
        user = create_user_instance(User.Role.TENANT_OWNER)
        assert user.can_whitelist_urls() is False

    def test_returns_false_for_customer(self):
        user = create_user_instance(User.Role.CUSTOMER)
        assert user.can_whitelist_urls() is False


# =============================================================================
# Time Off Self-Approval Tests
# =============================================================================

class TestCanSelfApproveTimeOff:
    """Test can_self_approve_time_off() method."""

    def test_returns_true_for_tenant_owner(self):
        user = create_user_instance(User.Role.TENANT_OWNER)
        assert user.can_self_approve_time_off() is True

    def test_returns_true_for_tenant_manager(self):
        user = create_user_instance(User.Role.TENANT_MANAGER)
        assert user.can_self_approve_time_off() is True

    def test_returns_true_for_staff_with_permission(self):
        user = create_user_instance(
            User.Role.TENANT_STAFF,
            permissions={'can_self_approve_time_off': True}
        )
        assert user.can_self_approve_time_off() is True

    def test_returns_false_for_staff_without_permission(self):
        user = create_user_instance(User.Role.TENANT_STAFF)
        assert user.can_self_approve_time_off() is False

    def test_returns_false_for_staff_with_explicit_false_permission(self):
        user = create_user_instance(
            User.Role.TENANT_STAFF,
            permissions={'can_self_approve_time_off': False}
        )
        assert user.can_self_approve_time_off() is False

    def test_returns_false_for_superuser(self):
        user = create_user_instance(User.Role.SUPERUSER)
        assert user.can_self_approve_time_off() is False

    def test_returns_false_for_customer(self):
        user = create_user_instance(User.Role.CUSTOMER)
        assert user.can_self_approve_time_off() is False


# =============================================================================
# Time Off Review Permission Tests
# =============================================================================

class TestCanReviewTimeOffRequests:
    """Test can_review_time_off_requests() method."""

    def test_returns_true_for_tenant_owner(self):
        user = create_user_instance(User.Role.TENANT_OWNER)
        assert user.can_review_time_off_requests() is True

    def test_returns_true_for_tenant_manager(self):
        user = create_user_instance(User.Role.TENANT_MANAGER)
        assert user.can_review_time_off_requests() is True

    def test_returns_false_for_superuser(self):
        user = create_user_instance(User.Role.SUPERUSER)
        assert user.can_review_time_off_requests() is False

    def test_returns_false_for_platform_manager(self):
        user = create_user_instance(User.Role.PLATFORM_MANAGER)
        assert user.can_review_time_off_requests() is False

    def test_returns_false_for_tenant_staff(self):
        user = create_user_instance(User.Role.TENANT_STAFF)
        assert user.can_review_time_off_requests() is False

    def test_returns_false_for_customer(self):
        user = create_user_instance(User.Role.CUSTOMER)
        assert user.can_review_time_off_requests() is False


# =============================================================================
# Accessible Tenants Tests
# =============================================================================

class TestGetAccessibleTenants:
    """Test get_accessible_tenants() method."""

    def test_returns_all_tenants_for_platform_user(self):
        # Arrange
        user = create_user_instance(User.Role.SUPERUSER)

        # Patch where Tenant is imported (inside the method)
        with patch('smoothschedule.identity.core.models.Tenant') as mock_tenant_model:
            mock_queryset = Mock()
            mock_tenant_model.objects.all.return_value = mock_queryset

            # Act
            result = user.get_accessible_tenants()

            # Assert
            mock_tenant_model.objects.all.assert_called_once()
            assert result == mock_queryset

    @pytest.mark.django_db
    def test_returns_single_tenant_for_tenant_user(self, shared_tenant):
        # Use shared fixture to avoid ~40s migration overhead per tenant
        # Create user with that tenant
        user = create_user_instance(User.Role.TENANT_OWNER)
        user.tenant = shared_tenant

        # Act
        result = user.get_accessible_tenants()

        # Assert
        assert result.count() == 1
        assert list(result)[0] == shared_tenant

    def test_returns_empty_queryset_for_tenant_user_without_tenant(self):
        # Arrange
        user = create_user_instance(User.Role.TENANT_OWNER)
        user.tenant = None

        # Patch where Tenant is imported
        with patch('smoothschedule.identity.core.models.Tenant') as mock_tenant_model:
            mock_queryset = Mock()
            mock_tenant_model.objects.none.return_value = mock_queryset

            # Act
            result = user.get_accessible_tenants()

            # Assert
            mock_tenant_model.objects.none.assert_called_once()
            assert result == mock_queryset


# =============================================================================
# Full Name Property Tests
# =============================================================================

class TestFullNameProperty:
    """Test full_name property."""

    def test_returns_first_and_last_name_combined(self):
        user = create_user_instance(
            User.Role.CUSTOMER,
            first_name='John',
            last_name='Doe'
        )
        assert user.full_name == 'John Doe'

    def test_returns_first_name_only_when_no_last_name(self):
        user = create_user_instance(
            User.Role.CUSTOMER,
            first_name='John',
            last_name=''
        )
        assert user.full_name == 'John'

    def test_returns_last_name_only_when_no_first_name(self):
        user = create_user_instance(
            User.Role.CUSTOMER,
            first_name='',
            last_name='Doe'
        )
        assert user.full_name == 'Doe'

    def test_returns_username_when_no_names(self):
        user = create_user_instance(
            User.Role.CUSTOMER,
            first_name='',
            last_name='',
            username='johndoe'
        )
        assert user.full_name == 'johndoe'

    def test_returns_email_when_no_names_or_username(self):
        user = create_user_instance(
            User.Role.CUSTOMER,
            first_name='',
            last_name='',
            username='',
            email='john@example.com'
        )
        assert user.full_name == 'john@example.com'

    def test_strips_whitespace_from_combined_name(self):
        user = create_user_instance(
            User.Role.CUSTOMER,
            first_name='  John  ',
            last_name='  Doe  '
        )
        # The implementation combines with space then strips
        assert user.full_name == 'John     Doe'


# =============================================================================
# Save Method Validation Tests
# =============================================================================

class TestSaveMethodValidation:
    """Test the save() method's business logic validation."""

    @pytest.mark.django_db
    def test_sets_role_to_superuser_when_is_superuser_flag_set(self):
        # Test Django's create_superuser command compatibility
        import uuid
        unique_id = str(uuid.uuid4())[:8]

        user = User(
            username=f'admin_user_{unique_id}',
            email=f'admin_user_{unique_id}@example.com',
            is_superuser=True,
            role=User.Role.CUSTOMER  # Wrong role, should be corrected
        )
        user.save()

        assert user.role == User.Role.SUPERUSER
        assert user.is_staff is True
        assert user.is_superuser is True

    @pytest.mark.django_db
    def test_sets_is_staff_and_is_superuser_for_superuser_role(self):
        user = User(
            username='admin2',
            email='admin2@example.com',
            role=User.Role.SUPERUSER,
            is_staff=False,
            is_superuser=False
        )
        user.save()

        assert user.is_staff is True
        assert user.is_superuser is True

    @pytest.mark.django_db
    def test_clears_tenant_for_platform_users(self, shared_tenant):
        # Use shared fixture to avoid ~40s migration overhead per tenant
        import uuid
        unique_id = str(uuid.uuid4())[:8]

        user = User(
            username=f'platformuser{unique_id}',
            email=f'platform{unique_id}@example.com',
            role=User.Role.PLATFORM_MANAGER,
            tenant=shared_tenant  # Should be cleared
        )
        user.save()

        assert user.tenant is None

    @pytest.mark.django_db
    def test_raises_error_for_tenant_user_without_tenant(self):
        user = User(
            username='tenantuser',
            email='tenant@example.com',
            role=User.Role.TENANT_OWNER,
            tenant=None  # Missing required tenant
        )

        with pytest.raises(ValueError) as exc_info:
            user.save()

        assert 'must be assigned to a tenant' in str(exc_info.value)

    @pytest.mark.django_db
    def test_allows_tenant_user_with_tenant(self, shared_tenant):
        # Use shared fixture to avoid ~40s migration overhead per tenant
        import uuid
        unique_id = str(uuid.uuid4())[:8]

        user = User(
            username=f'owner{unique_id}',
            email=f'owner{unique_id}@testbiz.com',
            role=User.Role.TENANT_OWNER,
            tenant=shared_tenant
        )
        user.save()

        assert user.tenant == shared_tenant
        assert user.id is not None

    @pytest.mark.django_db
    def test_allows_customer_with_tenant(self, shared_tenant):
        # Use shared fixture to avoid ~40s migration overhead per tenant
        import uuid
        unique_id = str(uuid.uuid4())[:8]

        user = User(
            username=f'customer{unique_id}',
            email=f'customer{unique_id}@example.com',
            role=User.Role.CUSTOMER,
            tenant=shared_tenant
        )
        user.save()

        assert user.tenant == shared_tenant


# =============================================================================
# String Representation Tests
# =============================================================================

class TestUserStringRepresentation:
    """Test the __str__() method."""

    def test_returns_email_and_role_display(self):
        user = create_user_instance(
            User.Role.PLATFORM_MANAGER,
            email='john@example.com'
        )
        result = str(user)
        # Should contain email and role
        assert 'john@example.com' in result
        assert 'Platform Manager' in result
