"""
Unit tests for platform/admin models.

Tests model methods without database access using mocks.
"""
from unittest.mock import Mock, patch, MagicMock
import pytest


class TestPlatformSettingsModel:
    """Test PlatformSettings model methods."""

    def test_str_method(self):
        """Test __str__ returns 'Platform Settings'."""
        from smoothschedule.platform.admin.models import PlatformSettings

        instance = PlatformSettings()
        result = str(instance)

        assert result == "Platform Settings"

    def test_delete_does_nothing(self):
        """Test delete method prevents deletion."""
        from smoothschedule.platform.admin.models import PlatformSettings

        instance = PlatformSettings()
        # Should not raise, should just do nothing
        instance.delete()

    def test_get_stripe_secret_key_from_instance(self):
        """Test get_stripe_secret_key returns instance value if set."""
        from smoothschedule.platform.admin.models import PlatformSettings

        instance = PlatformSettings()
        instance.stripe_secret_key = 'sk_test_123'

        result = instance.get_stripe_secret_key()

        assert result == 'sk_test_123'

    def test_get_stripe_publishable_key_from_instance(self):
        """Test get_stripe_publishable_key returns instance value if set."""
        from smoothschedule.platform.admin.models import PlatformSettings

        instance = PlatformSettings()
        instance.stripe_publishable_key = 'pk_test_123'

        result = instance.get_stripe_publishable_key()

        assert result == 'pk_test_123'

    def test_get_stripe_webhook_secret_from_instance(self):
        """Test get_stripe_webhook_secret returns instance value if set."""
        from smoothschedule.platform.admin.models import PlatformSettings

        instance = PlatformSettings()
        instance.stripe_webhook_secret = 'whsec_123'

        result = instance.get_stripe_webhook_secret()

        assert result == 'whsec_123'

    def test_has_stripe_keys_true(self):
        """Test has_stripe_keys returns True when keys are set."""
        from smoothschedule.platform.admin.models import PlatformSettings

        instance = PlatformSettings()
        instance.stripe_secret_key = 'sk_test_123'
        instance.stripe_publishable_key = 'pk_test_123'

        result = instance.has_stripe_keys()

        assert result is True

    def test_has_stripe_keys_false(self):
        """Test has_stripe_keys returns False when keys are not set."""
        from smoothschedule.platform.admin.models import PlatformSettings

        instance = PlatformSettings()
        instance.stripe_secret_key = ''
        instance.stripe_publishable_key = ''

        with patch.object(instance, 'get_stripe_secret_key', return_value=''):
            with patch.object(instance, 'get_stripe_publishable_key', return_value=''):
                result = instance.has_stripe_keys()

        assert result is False


class TestSubscriptionPlanModel:
    """Test SubscriptionPlan model methods."""

    def test_model_exists(self):
        """Test SubscriptionPlan model can be imported."""
        from smoothschedule.platform.admin.models import SubscriptionPlan

        assert SubscriptionPlan is not None

    def test_has_meta_class(self):
        """Test model has Meta class."""
        from smoothschedule.platform.admin.models import SubscriptionPlan

        assert hasattr(SubscriptionPlan, '_meta')


class TestPlatformSettingsGetMethods:
    """Test PlatformSettings get_* methods that fall back to settings."""

    def test_get_stripe_secret_key_from_settings_when_empty(self):
        """Should fall back to settings when instance value is empty."""
        from smoothschedule.platform.admin.models import PlatformSettings

        instance = PlatformSettings()
        instance.stripe_secret_key = ''

        with patch('django.conf.settings', spec=['STRIPE_SECRET_KEY']) as mock_settings:
            mock_settings.STRIPE_SECRET_KEY = 'sk_from_settings'
            result = instance.get_stripe_secret_key()

        assert result == 'sk_from_settings'

    def test_get_stripe_publishable_key_from_settings_when_empty(self):
        """Should fall back to settings when instance value is empty."""
        from smoothschedule.platform.admin.models import PlatformSettings

        instance = PlatformSettings()
        instance.stripe_publishable_key = ''

        with patch('django.conf.settings', spec=['STRIPE_PUBLISHABLE_KEY']) as mock_settings:
            mock_settings.STRIPE_PUBLISHABLE_KEY = 'pk_from_settings'
            result = instance.get_stripe_publishable_key()

        assert result == 'pk_from_settings'

    def test_get_stripe_webhook_secret_from_settings_when_empty(self):
        """Should fall back to settings when instance value is empty."""
        from smoothschedule.platform.admin.models import PlatformSettings

        instance = PlatformSettings()
        instance.stripe_webhook_secret = ''

        with patch('django.conf.settings', spec=['STRIPE_WEBHOOK_SECRET']) as mock_settings:
            mock_settings.STRIPE_WEBHOOK_SECRET = 'whsec_from_settings'
            result = instance.get_stripe_webhook_secret()

        assert result == 'whsec_from_settings'

    def test_stripe_keys_from_env_true(self):
        """Should return True when keys come from environment."""
        from smoothschedule.platform.admin.models import PlatformSettings

        instance = PlatformSettings()
        instance.stripe_secret_key = ''

        with patch('django.conf.settings', spec=['STRIPE_SECRET_KEY']) as mock_settings:
            mock_settings.STRIPE_SECRET_KEY = 'sk_from_env'
            result = instance.stripe_keys_from_env()

        assert result is True

    def test_stripe_keys_from_env_false_when_in_db(self):
        """Should return False when keys are in database."""
        from smoothschedule.platform.admin.models import PlatformSettings

        instance = PlatformSettings()
        instance.stripe_secret_key = 'sk_in_db'

        result = instance.stripe_keys_from_env()

        assert result is False

    def test_mask_key_short_key(self):
        """Should mask entire key when too short."""
        from smoothschedule.platform.admin.models import PlatformSettings

        instance = PlatformSettings()
        result = instance.mask_key('short', visible_chars=8)

        assert result == '*****'

    def test_mask_key_long_key(self):
        """Should show first/last chars of long key."""
        from smoothschedule.platform.admin.models import PlatformSettings

        instance = PlatformSettings()
        result = instance.mask_key('sk_test_123456789012345678901234567890', visible_chars=8)

        assert result.startswith('sk_test_')
        assert result.endswith('...7890')

    def test_mask_key_empty(self):
        """Should return empty string for empty key."""
        from smoothschedule.platform.admin.models import PlatformSettings

        instance = PlatformSettings()
        result = instance.mask_key('')

        assert result == ''


class TestTenantInvitationModel:
    """Test TenantInvitation model methods."""

    def test_model_exists(self):
        """Test TenantInvitation model can be imported."""
        from smoothschedule.platform.admin.models import TenantInvitation

        assert TenantInvitation is not None

    def test_has_is_valid_method(self):
        """Test model has is_valid method."""
        from smoothschedule.platform.admin.models import TenantInvitation

        assert hasattr(TenantInvitation, 'is_valid')

    def test_has_status_choices(self):
        """Test model has Status choices."""
        from smoothschedule.platform.admin.models import TenantInvitation

        assert hasattr(TenantInvitation, 'Status')

    def test_is_valid_returns_true_when_pending_and_not_expired(self):
        """Should return True when status is PENDING and not expired."""
        from smoothschedule.platform.admin.models import TenantInvitation
        from datetime import timedelta
        from django.utils import timezone

        invitation = TenantInvitation()
        invitation.status = TenantInvitation.Status.PENDING
        invitation.expires_at = timezone.now() + timedelta(days=1)

        assert invitation.is_valid() is True

    def test_is_valid_returns_false_when_not_pending(self):
        """Should return False when status is not PENDING."""
        from smoothschedule.platform.admin.models import TenantInvitation
        from datetime import timedelta
        from django.utils import timezone

        invitation = TenantInvitation()
        invitation.status = TenantInvitation.Status.ACCEPTED
        invitation.expires_at = timezone.now() + timedelta(days=1)

        assert invitation.is_valid() is False

    def test_is_valid_returns_false_when_expired(self):
        """Should return False when invitation is expired."""
        from smoothschedule.platform.admin.models import TenantInvitation
        from datetime import timedelta
        from django.utils import timezone

        invitation = TenantInvitation()
        invitation.status = TenantInvitation.Status.PENDING
        invitation.expires_at = timezone.now() - timedelta(hours=1)

        assert invitation.is_valid() is False

    def test_str_method(self):
        """Should return email and status display."""
        from smoothschedule.platform.admin.models import TenantInvitation

        invitation = TenantInvitation()
        invitation.email = 'test@example.com'
        invitation.status = TenantInvitation.Status.PENDING

        result = str(invitation)

        assert 'test@example.com' in result
        assert 'Pending' in result

    def test_get_effective_max_users_with_custom_value(self):
        """Should return custom value when set."""
        from smoothschedule.platform.admin.models import TenantInvitation

        invitation = TenantInvitation()
        invitation.custom_max_users = 100
        invitation.subscription_tier = 'STARTER'

        result = invitation.get_effective_max_users()

        assert result == 100

    def test_get_effective_max_users_with_tier_default_free(self):
        """Should return tier default when custom not set - FREE."""
        from smoothschedule.platform.admin.models import TenantInvitation

        invitation = TenantInvitation()
        invitation.custom_max_users = None
        invitation.subscription_tier = 'FREE'

        result = invitation.get_effective_max_users()

        assert result == 2

    def test_get_effective_max_users_with_tier_default_professional(self):
        """Should return tier default when custom not set - PROFESSIONAL."""
        from smoothschedule.platform.admin.models import TenantInvitation

        invitation = TenantInvitation()
        invitation.custom_max_users = None
        invitation.subscription_tier = 'PROFESSIONAL'

        result = invitation.get_effective_max_users()

        assert result == 15

    def test_get_effective_max_users_with_tier_default_enterprise(self):
        """Should return tier default when custom not set - ENTERPRISE."""
        from smoothschedule.platform.admin.models import TenantInvitation

        invitation = TenantInvitation()
        invitation.custom_max_users = None
        invitation.subscription_tier = 'ENTERPRISE'

        result = invitation.get_effective_max_users()

        assert result == 50

    def test_get_effective_max_users_with_unknown_tier(self):
        """Should return fallback when tier is unknown."""
        from smoothschedule.platform.admin.models import TenantInvitation

        invitation = TenantInvitation()
        invitation.custom_max_users = None
        invitation.subscription_tier = 'UNKNOWN'

        result = invitation.get_effective_max_users()

        assert result == 5  # Fallback default

    def test_get_effective_max_resources_with_custom_value(self):
        """Should return custom value when set."""
        from smoothschedule.platform.admin.models import TenantInvitation

        invitation = TenantInvitation()
        invitation.custom_max_resources = 200
        invitation.subscription_tier = 'STARTER'

        result = invitation.get_effective_max_resources()

        assert result == 200

    def test_get_effective_max_resources_with_tier_default_free(self):
        """Should return tier default when custom not set - FREE."""
        from smoothschedule.platform.admin.models import TenantInvitation

        invitation = TenantInvitation()
        invitation.custom_max_resources = None
        invitation.subscription_tier = 'FREE'

        result = invitation.get_effective_max_resources()

        assert result == 3

    def test_get_effective_max_resources_with_tier_default_starter(self):
        """Should return tier default when custom not set - STARTER."""
        from smoothschedule.platform.admin.models import TenantInvitation

        invitation = TenantInvitation()
        invitation.custom_max_resources = None
        invitation.subscription_tier = 'STARTER'

        result = invitation.get_effective_max_resources()

        assert result == 10

    def test_get_effective_max_resources_with_tier_default_professional(self):
        """Should return tier default when custom not set - PROFESSIONAL."""
        from smoothschedule.platform.admin.models import TenantInvitation

        invitation = TenantInvitation()
        invitation.custom_max_resources = None
        invitation.subscription_tier = 'PROFESSIONAL'

        result = invitation.get_effective_max_resources()

        assert result == 30

    def test_get_effective_max_resources_with_tier_default_enterprise(self):
        """Should return tier default when custom not set - ENTERPRISE."""
        from smoothschedule.platform.admin.models import TenantInvitation

        invitation = TenantInvitation()
        invitation.custom_max_resources = None
        invitation.subscription_tier = 'ENTERPRISE'

        result = invitation.get_effective_max_resources()

        assert result == 100

    def test_cancel_changes_status_when_pending(self):
        """Should change status to CANCELLED when pending."""
        from smoothschedule.platform.admin.models import TenantInvitation

        invitation = TenantInvitation()
        invitation.status = TenantInvitation.Status.PENDING

        with patch.object(invitation, 'save'):
            invitation.cancel()

        assert invitation.status == TenantInvitation.Status.CANCELLED

    def test_cancel_does_nothing_when_not_pending(self):
        """Should not change status when not pending."""
        from smoothschedule.platform.admin.models import TenantInvitation

        invitation = TenantInvitation()
        invitation.status = TenantInvitation.Status.ACCEPTED

        with patch.object(invitation, 'save') as mock_save:
            invitation.cancel()

        mock_save.assert_not_called()
        assert invitation.status == TenantInvitation.Status.ACCEPTED

    def test_accept_updates_status_and_timestamp(self):
        """Should update status and accepted_at on accept."""
        from smoothschedule.platform.admin.models import TenantInvitation

        invitation = TenantInvitation()
        invitation.status = TenantInvitation.Status.PENDING

        # Use fields_cache to bypass FK validation
        mock_tenant = Mock()
        mock_user = Mock()

        with patch.object(invitation, 'save'):
            # Patch the FK assignments to bypass Django validation
            with patch.object(type(invitation), 'created_tenant', create=True):
                with patch.object(type(invitation), 'created_user', create=True):
                    invitation.accept(mock_tenant, mock_user)

        assert invitation.status == TenantInvitation.Status.ACCEPTED
        assert invitation.accepted_at is not None


class TestSubscriptionPlanModelMethods:
    """Test SubscriptionPlan model methods."""

    def test_str_method(self):
        """Should return name with plan type."""
        from smoothschedule.platform.admin.models import SubscriptionPlan

        plan = SubscriptionPlan()
        plan.name = 'Professional'
        plan.plan_type = SubscriptionPlan.PlanType.BASE

        result = str(plan)

        assert 'Professional' in result
        assert 'Base' in result

    def test_str_method_addon(self):
        """Should show Add-on for addon plans."""
        from smoothschedule.platform.admin.models import SubscriptionPlan

        plan = SubscriptionPlan()
        plan.name = 'Extra Storage'
        plan.plan_type = SubscriptionPlan.PlanType.ADDON

        result = str(plan)

        assert 'Extra Storage' in result
        assert 'Add-on' in result

    def test_plan_type_choices(self):
        """Should have BASE and ADDON choices."""
        from smoothschedule.platform.admin.models import SubscriptionPlan

        assert SubscriptionPlan.PlanType.BASE == 'base'
        assert SubscriptionPlan.PlanType.ADDON == 'addon'

    def test_meta_ordering(self):
        """Should order by price_monthly then name."""
        from smoothschedule.platform.admin.models import SubscriptionPlan

        assert SubscriptionPlan._meta.ordering == ['price_monthly', 'name']


class TestPlatformEmailAddressModel:
    """Test PlatformEmailAddress model methods."""

    def test_str_method(self):
        """Should return display name and email address."""
        from smoothschedule.platform.admin.models import PlatformEmailAddress

        email = PlatformEmailAddress()
        email.display_name = 'Support'
        email.local_part = 'support'
        email.domain = 'smoothschedule.com'

        result = str(email)

        assert 'Support' in result
        assert 'support@smoothschedule.com' in result

    def test_email_address_property(self):
        """Should return full email address."""
        from smoothschedule.platform.admin.models import PlatformEmailAddress

        email = PlatformEmailAddress()
        email.local_part = 'billing'
        email.domain = 'talova.net'

        result = email.email_address

        assert result == 'billing@talova.net'

    def test_effective_sender_name_with_assigned_user(self):
        """Should use assigned user's full name."""
        from smoothschedule.platform.admin.models import PlatformEmailAddress

        email = PlatformEmailAddress()
        email.display_name = 'Support'
        email.sender_name = 'Custom Sender'

        mock_user = Mock()
        mock_user.get_full_name.return_value = 'John Smith'
        email._state.fields_cache['assigned_user'] = mock_user

        result = email.effective_sender_name

        assert result == 'John Smith'

    def test_effective_sender_name_with_assigned_user_no_name(self):
        """Should fall back to sender_name when user has no full name."""
        from smoothschedule.platform.admin.models import PlatformEmailAddress

        email = PlatformEmailAddress()
        email.display_name = 'Support'
        email.sender_name = 'Custom Sender'

        mock_user = Mock()
        mock_user.get_full_name.return_value = ''  # Empty name
        email._state.fields_cache['assigned_user'] = mock_user

        result = email.effective_sender_name

        assert result == 'Custom Sender'

    def test_effective_sender_name_with_sender_name(self):
        """Should use sender_name when no assigned user."""
        from smoothschedule.platform.admin.models import PlatformEmailAddress

        email = PlatformEmailAddress()
        email.display_name = 'Support'
        email.sender_name = 'SmoothSchedule Support'
        email._state.fields_cache['assigned_user'] = None

        result = email.effective_sender_name

        assert result == 'SmoothSchedule Support'

    def test_effective_sender_name_falls_back_to_display_name(self):
        """Should fall back to display_name."""
        from smoothschedule.platform.admin.models import PlatformEmailAddress

        email = PlatformEmailAddress()
        email.display_name = 'Billing Department'
        email.sender_name = ''
        email._state.fields_cache['assigned_user'] = None

        result = email.effective_sender_name

        assert result == 'Billing Department'

    def test_get_imap_settings(self):
        """Should return IMAP connection settings."""
        from smoothschedule.platform.admin.models import PlatformEmailAddress

        email = PlatformEmailAddress()
        email.local_part = 'support'
        email.domain = 'smoothschedule.com'
        email.password = 'secret123'

        result = email.get_imap_settings()

        assert result['host'] == 'mail.talova.net'
        assert result['port'] == 993
        assert result['use_ssl'] is True
        assert result['username'] == 'support@smoothschedule.com'
        assert result['password'] == 'secret123'
        assert result['folder'] == 'INBOX'

    def test_get_smtp_settings(self):
        """Should return SMTP connection settings."""
        from smoothschedule.platform.admin.models import PlatformEmailAddress

        email = PlatformEmailAddress()
        email.local_part = 'billing'
        email.domain = 'talova.net'
        email.password = 'secret456'

        result = email.get_smtp_settings()

        assert result['host'] == 'mail.talova.net'
        assert result['port'] == 587
        assert result['use_tls'] is True
        assert result['use_ssl'] is False
        assert result['username'] == 'billing@talova.net'
        assert result['password'] == 'secret456'

    def test_domain_choices(self):
        """Should have domain choices."""
        from smoothschedule.platform.admin.models import PlatformEmailAddress

        assert PlatformEmailAddress.Domain.SMOOTHSCHEDULE == 'smoothschedule.com'
        assert PlatformEmailAddress.Domain.TALOVA == 'talova.net'

    def test_mail_server_constants(self):
        """Should have mail server constants defined."""
        from smoothschedule.platform.admin.models import PlatformEmailAddress

        assert PlatformEmailAddress.MAIL_SERVER_HOST == 'mail.talova.net'
        assert PlatformEmailAddress.IMAP_HOST == 'mail.talova.net'
        assert PlatformEmailAddress.IMAP_PORT == 993
        assert PlatformEmailAddress.SMTP_HOST == 'mail.talova.net'
        assert PlatformEmailAddress.SMTP_PORT == 587


class TestPlatformSettingsGetInstance:
    """Test PlatformSettings.get_instance class method."""

    def test_returns_cached_value(self):
        """Should return cached instance if available."""
        from smoothschedule.platform.admin.models import PlatformSettings

        cached_instance = PlatformSettings()

        with patch('smoothschedule.platform.admin.models.cache') as mock_cache:
            mock_cache.get.return_value = cached_instance

            result = PlatformSettings.get_instance()

            mock_cache.get.assert_called_once_with('platform_settings')
            assert result == cached_instance

    def test_creates_instance_when_not_cached(self):
        """Should create and cache instance when not cached."""
        from smoothschedule.platform.admin.models import PlatformSettings

        mock_instance = PlatformSettings()

        with patch('smoothschedule.platform.admin.models.cache') as mock_cache:
            mock_cache.get.return_value = None
            with patch.object(PlatformSettings.objects, 'get_or_create', return_value=(mock_instance, True)):
                result = PlatformSettings.get_instance()

            mock_cache.set.assert_called_once()
            assert result == mock_instance
