# tests/unit/services/test_usage_service.py
"""Unit tests for UsageService."""

import pytest

from app.modules.analytics.services.usage_service import UsageService, usage_service
from app.modules.catalog.models import Product
from app.modules.billing.models import SubscriptionTier, VendorSubscription
from app.modules.tenancy.models import VendorUser


@pytest.mark.unit
@pytest.mark.usage
class TestUsageServiceGetUsage:
    """Test suite for get_vendor_usage operation."""

    def setup_method(self):
        """Initialize service instance before each test."""
        self.service = UsageService()

    def test_get_vendor_usage_basic(self, db, test_vendor_with_subscription):
        """Test getting basic usage data."""
        vendor_id = test_vendor_with_subscription.id
        usage = self.service.get_vendor_usage(db, vendor_id)

        assert usage.tier.code == "essential"
        assert usage.tier.name == "Essential"
        assert len(usage.usage) == 3

    def test_get_vendor_usage_metrics(self, db, test_vendor_with_subscription):
        """Test usage metrics are calculated correctly."""
        vendor_id = test_vendor_with_subscription.id
        usage = self.service.get_vendor_usage(db, vendor_id)

        orders_metric = next((m for m in usage.usage if m.name == "orders"), None)
        assert orders_metric is not None
        assert orders_metric.current == 10
        assert orders_metric.limit == 100
        assert orders_metric.percentage == 10.0
        assert orders_metric.is_unlimited is False

    def test_get_vendor_usage_at_limit(self, db, test_vendor_at_limit):
        """Test usage shows at limit correctly."""
        vendor_id = test_vendor_at_limit.id
        usage = self.service.get_vendor_usage(db, vendor_id)

        orders_metric = next((m for m in usage.usage if m.name == "orders"), None)
        assert orders_metric.is_at_limit is True
        assert usage.has_limits_reached is True

    def test_get_vendor_usage_approaching_limit(self, db, test_vendor_approaching_limit):
        """Test usage shows approaching limit correctly."""
        vendor_id = test_vendor_approaching_limit.id
        usage = self.service.get_vendor_usage(db, vendor_id)

        orders_metric = next((m for m in usage.usage if m.name == "orders"), None)
        assert orders_metric.is_approaching_limit is True
        assert usage.has_limits_approaching is True

    def test_get_vendor_usage_upgrade_available(
        self, db, test_vendor_with_subscription, test_professional_tier
    ):
        """Test upgrade info when not on highest tier."""
        vendor_id = test_vendor_with_subscription.id
        usage = self.service.get_vendor_usage(db, vendor_id)

        assert usage.upgrade_available is True
        assert usage.upgrade_tier is not None
        assert usage.upgrade_tier.code == "professional"

    def test_get_vendor_usage_highest_tier(self, db, test_vendor_on_professional):
        """Test no upgrade when on highest tier."""
        vendor_id = test_vendor_on_professional.id
        usage = self.service.get_vendor_usage(db, vendor_id)

        assert usage.tier.is_highest_tier is True
        assert usage.upgrade_available is False
        assert usage.upgrade_tier is None


@pytest.mark.unit
@pytest.mark.usage
class TestUsageServiceCheckLimit:
    """Test suite for check_limit operation."""

    def setup_method(self):
        """Initialize service instance before each test."""
        self.service = UsageService()

    def test_check_orders_limit_can_proceed(self, db, test_vendor_with_subscription):
        """Test checking orders limit when under limit."""
        vendor_id = test_vendor_with_subscription.id
        result = self.service.check_limit(db, vendor_id, "orders")

        assert result.can_proceed is True
        assert result.current == 10
        assert result.limit == 100

    def test_check_products_limit(self, db, test_vendor_with_products):
        """Test checking products limit."""
        vendor_id = test_vendor_with_products.id
        result = self.service.check_limit(db, vendor_id, "products")

        assert result.can_proceed is True
        assert result.current == 5
        assert result.limit == 500

    def test_check_team_members_limit(self, db, test_vendor_with_team):
        """Test checking team members limit when at limit."""
        vendor_id = test_vendor_with_team.id
        result = self.service.check_limit(db, vendor_id, "team_members")

        # At limit (2/2) - can_proceed should be False
        assert result.can_proceed is False
        assert result.current == 2
        assert result.limit == 2
        assert result.percentage == 100.0

    def test_check_unknown_limit_type(self, db, test_vendor_with_subscription):
        """Test checking unknown limit type."""
        vendor_id = test_vendor_with_subscription.id
        result = self.service.check_limit(db, vendor_id, "unknown")

        assert result.can_proceed is True
        assert "Unknown limit type" in result.message

    def test_check_limit_upgrade_info_when_blocked(self, db, test_vendor_at_limit):
        """Test upgrade info is provided when at limit."""
        vendor_id = test_vendor_at_limit.id
        result = self.service.check_limit(db, vendor_id, "orders")

        assert result.can_proceed is False
        assert result.upgrade_tier_code == "professional"
        assert result.upgrade_tier_name == "Professional"


# ==================== Fixtures ====================


@pytest.fixture
def test_essential_tier(db):
    """Create essential subscription tier."""
    tier = SubscriptionTier(
        code="essential",
        name="Essential",
        description="Essential plan",
        price_monthly_cents=4900,
        price_annual_cents=49000,
        orders_per_month=100,
        products_limit=500,
        team_members=2,
        features=["basic_reports"],
        is_active=True,
        display_order=1,
    )
    db.add(tier)
    db.commit()
    db.refresh(tier)
    return tier


@pytest.fixture
def test_professional_tier(db, test_essential_tier):
    """Create professional subscription tier."""
    tier = SubscriptionTier(
        code="professional",
        name="Professional",
        description="Professional plan",
        price_monthly_cents=9900,
        price_annual_cents=99000,
        orders_per_month=500,
        products_limit=2000,
        team_members=10,
        features=["basic_reports", "api_access", "analytics_dashboard"],
        is_active=True,
        display_order=2,
    )
    db.add(tier)
    db.commit()
    db.refresh(tier)
    return tier


@pytest.fixture
def test_vendor_with_subscription(db, test_vendor, test_essential_tier):
    """Create vendor with active subscription."""
    from datetime import datetime, timezone

    now = datetime.now(timezone.utc)
    subscription = VendorSubscription(
        vendor_id=test_vendor.id,
        tier="essential",
        tier_id=test_essential_tier.id,
        status="active",
        period_start=now,
        period_end=now.replace(month=now.month + 1 if now.month < 12 else 1),
        orders_this_period=10,
    )
    db.add(subscription)
    db.commit()
    db.refresh(test_vendor)
    return test_vendor


@pytest.fixture
def test_vendor_at_limit(db, test_vendor, test_essential_tier, test_professional_tier):
    """Create vendor at order limit."""
    from datetime import datetime, timezone

    now = datetime.now(timezone.utc)
    subscription = VendorSubscription(
        vendor_id=test_vendor.id,
        tier="essential",
        tier_id=test_essential_tier.id,
        status="active",
        period_start=now,
        period_end=now.replace(month=now.month + 1 if now.month < 12 else 1),
        orders_this_period=100,  # At limit
    )
    db.add(subscription)
    db.commit()
    db.refresh(test_vendor)
    return test_vendor


@pytest.fixture
def test_vendor_approaching_limit(db, test_vendor, test_essential_tier):
    """Create vendor approaching order limit (>=80%)."""
    from datetime import datetime, timezone

    now = datetime.now(timezone.utc)
    subscription = VendorSubscription(
        vendor_id=test_vendor.id,
        tier="essential",
        tier_id=test_essential_tier.id,
        status="active",
        period_start=now,
        period_end=now.replace(month=now.month + 1 if now.month < 12 else 1),
        orders_this_period=85,  # 85% of 100
    )
    db.add(subscription)
    db.commit()
    db.refresh(test_vendor)
    return test_vendor


@pytest.fixture
def test_vendor_on_professional(db, test_vendor, test_professional_tier):
    """Create vendor on highest tier."""
    from datetime import datetime, timezone

    now = datetime.now(timezone.utc)
    subscription = VendorSubscription(
        vendor_id=test_vendor.id,
        tier="professional",
        tier_id=test_professional_tier.id,
        status="active",
        period_start=now,
        period_end=now.replace(month=now.month + 1 if now.month < 12 else 1),
        orders_this_period=50,
    )
    db.add(subscription)
    db.commit()
    db.refresh(test_vendor)
    return test_vendor


@pytest.fixture
def test_vendor_with_products(db, test_vendor_with_subscription, marketplace_product_factory):
    """Create vendor with products."""
    for i in range(5):
        # Create marketplace product first
        mp = marketplace_product_factory(db, title=f"Test Product {i}")
        product = Product(
            vendor_id=test_vendor_with_subscription.id,
            marketplace_product_id=mp.id,
            price_cents=1000,
            is_active=True,
        )
        db.add(product)
    db.commit()
    return test_vendor_with_subscription


@pytest.fixture
def test_vendor_with_team(db, test_vendor_with_subscription, test_user, other_user):
    """Create vendor with team members (owner + team member = 2)."""
    from app.modules.tenancy.models import VendorUserType

    # Add owner
    owner = VendorUser(
        vendor_id=test_vendor_with_subscription.id,
        user_id=test_user.id,
        user_type=VendorUserType.OWNER.value,
        is_active=True,
    )
    db.add(owner)

    # Add team member
    team_member = VendorUser(
        vendor_id=test_vendor_with_subscription.id,
        user_id=other_user.id,
        user_type=VendorUserType.TEAM_MEMBER.value,
        is_active=True,
    )
    db.add(team_member)
    db.commit()
    return test_vendor_with_subscription
