"""
EntitlementService - Central service for resolving effective entitlements.

Resolution order (highest to lowest precedence):
1. TenantCustomTier (if exists and is_active) - replaces plan features entirely
2. Base PlanVersion features from Subscription (if no custom tier)
3. Active, non-expired SubscriptionAddOn features (ADDED on top)

For integer features (limits):
- Add-on values are ADDED to the base (custom tier or plan) value
- Stackable add-on values are multiplied by quantity
- Example: Base (10) + add-on (5) x qty 3 = 25 total
"""

from __future__ import annotations

from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from smoothschedule.identity.core.models import Tenant


class EntitlementService:
    """Central service for resolving effective entitlements for a business."""

    @staticmethod
    def get_effective_entitlements(business: Tenant) -> dict[str, bool | int | None]:
        """
        Returns all effective entitlements for a business.

        Resolution order:
        1. TenantCustomTier (if exists and is_active) - replaces plan features
        2. Base PlanVersion features from Subscription (if no custom tier)
        3. Active SubscriptionAddOn features (ADDED on top of base)

        For integer features:
        - Add-on values are ADDED to the base (custom tier or plan) value
        - Stackable add-on values are multiplied by their quantity
        - Example: Base (10) + AddOn (5) x 3 qty = 25 total

        For boolean features:
        - True from any source wins

        Args:
            business: The Tenant to get entitlements for

        Returns:
            dict mapping feature codes to their values (bool or int)
        """
        # Check if business has an active subscription
        subscription = getattr(business, "billing_subscription", None)
        if subscription is None:
            return {}

        if not subscription.is_active:
            return {}

        result: dict[str, bool | int | None] = {}

        # Layer 1: Custom tier OR plan features (base layer)
        custom_tier = getattr(business, "custom_tier", None)
        if custom_tier and custom_tier.is_active:
            # Use custom tier features (complete replacement of plan features)
            result = custom_tier.features.copy()
        else:
            # Fall back to plan features
            plan_features = subscription.plan_version.features.select_related(
                "feature"
            ).all()
            for pf in plan_features:
                result[pf.feature.code] = pf.get_value()

        # Layer 2: Add-on features (ADDED to base values for integers)
        # For boolean: any True wins
        # For integer: values are ADDED to base value (multiplied by quantity)
        active_addons = subscription.addons.filter(status="active").select_related(
            "addon"
        ).all()
        for subscription_addon in active_addons:
            if not subscription_addon.is_active:
                continue

            quantity = subscription_addon.quantity if subscription_addon.addon.is_stackable else 1

            for af in subscription_addon.addon.features.select_related("feature").all():
                feature_code = af.feature.code
                addon_value = af.get_value()

                if addon_value is None:
                    continue

                if af.feature.feature_type == "integer":
                    # For integer features: ADD to current value (multiplied by quantity)
                    effective_addon_value = addon_value * quantity
                    current = result.get(feature_code)
                    if current is None:
                        result[feature_code] = effective_addon_value
                    elif isinstance(current, int):
                        result[feature_code] = current + effective_addon_value
                    else:
                        # Current value is not an int (shouldn't happen), set it
                        result[feature_code] = effective_addon_value
                elif af.feature.feature_type == "boolean":
                    # For boolean features, True wins over False
                    if addon_value is True:
                        result[feature_code] = True

        return result

    @staticmethod
    def has_feature(business: Tenant, feature_code: str) -> bool:
        """
        Check if business has a boolean feature enabled.

        For boolean features, returns the value directly.
        For integer features, returns True if value > 0.

        Args:
            business: The Tenant to check
            feature_code: The feature code to check

        Returns:
            True if feature is enabled/granted, False otherwise
        """
        entitlements = EntitlementService.get_effective_entitlements(business)
        value = entitlements.get(feature_code)

        if value is None:
            return False

        if isinstance(value, bool):
            return value

        if isinstance(value, int):
            return value > 0

        return False

    @staticmethod
    def get_limit(business: Tenant, feature_code: str) -> int | None:
        """
        Get the limit value for an integer feature.

        Args:
            business: The Tenant to check
            feature_code: The feature code to get limit for

        Returns:
            The integer limit value, or None if not found or not an integer feature
        """
        entitlements = EntitlementService.get_effective_entitlements(business)
        value = entitlements.get(feature_code)

        if value is None:
            return None

        # Use type() instead of isinstance() because bool is a subclass of int
        # We want to return None for boolean features
        if type(value) is int:
            return value

        # Boolean features don't have limits
        return None
