"""سرویس Entitlement: بررسی دسترسی tenant به یک قابلیت.

قوانین بررسی (به ترتیب):
1. ابتدا Redis cache بررسی می‌شود.
2. اگر در cache نبود، از دیتابیس محاسبه می‌شود.
3. نتیجه با TTL مشخص در Redis ذخیره می‌شود.
4. اگر اشتراک tenant فعال نبود → دسترسی false.
5. اگر قابلیت در پلن فعال نبود → false.
6. اگر دسترسی سفارشی (custom access) وجود داشت، آن اولویت دارد.
"""
from __future__ import annotations

from dataclasses import dataclass
from uuid import UUID

from sqlalchemy.ext.asyncio import AsyncSession

from app.core.cache import cache, feature_access_key
from app.core.config import settings
from app.models.enums import SubscriptionStatus, TenantStatus
from app.repositories.plan import FeatureRepository, PlanFeatureRepository
from app.repositories.subscription import (
    SubscriptionRepository,
    TenantFeatureAccessRepository,
)
from app.repositories.tenant import TenantRepository

_ALLOWED_SUBSCRIPTION_STATUSES = {
    SubscriptionStatus.ACTIVE,
    SubscriptionStatus.TRIALING,
}


@dataclass
class AccessResult:
    has_access: bool
    reason: str


class EntitlementService:
    def __init__(self, session: AsyncSession) -> None:
        self.session = session
        self.tenant_repo = TenantRepository(session)
        self.feature_repo = FeatureRepository(session)
        self.plan_feature_repo = PlanFeatureRepository(session)
        self.subscription_repo = SubscriptionRepository(session)
        self.feature_access_repo = TenantFeatureAccessRepository(session)

    async def check_feature_access(
        self, tenant_id: UUID, feature_key: str
    ) -> bool:
        """بررسی ساده دسترسی؛ فقط مقدار boolean برمی‌گرداند."""
        result = await self.evaluate(tenant_id, feature_key)
        return result.has_access

    async def evaluate(self, tenant_id: UUID, feature_key: str) -> AccessResult:
        """بررسی کامل دسترسی همراه با دلیل، با استفاده از cache."""
        cache_key = feature_access_key(str(tenant_id), feature_key)

        cached = await cache.get(cache_key)
        if cached is not None:
            return AccessResult(has_access=cached == "1", reason="cache")

        result = await self._evaluate_from_db(tenant_id, feature_key)

        await cache.set(
            cache_key,
            "1" if result.has_access else "0",
            ttl=settings.feature_access_cache_ttl,
        )
        return result

    async def _evaluate_from_db(
        self, tenant_id: UUID, feature_key: str
    ) -> AccessResult:
        # ۱) tenant باید وجود داشته و فعال باشد.
        tenant = await self.tenant_repo.get(tenant_id)
        if tenant is None:
            return AccessResult(False, "tenant_not_found")
        if tenant.status != TenantStatus.ACTIVE:
            return AccessResult(False, "tenant_inactive")

        # ۲) قابلیت باید وجود داشته و فعال باشد.
        feature = await self.feature_repo.get_by_key(feature_key)
        if feature is None:
            return AccessResult(False, "feature_not_found")
        if not feature.is_active:
            return AccessResult(False, "feature_inactive")

        # ۳) بررسی override سفارشی tenant (بالاترین اولویت).
        custom = await self.feature_access_repo.get(tenant_id, feature.id)
        if custom is not None:
            if not custom.is_enabled:
                return AccessResult(False, "custom_disabled")
            # اگر سقف سفارشی تعریف شده و مصرف از آن گذشته باشد → عدم دسترسی.
            if (
                custom.custom_limit_value is not None
                and custom.used_value >= custom.custom_limit_value
            ):
                return AccessResult(False, "custom_limit_exceeded")
            return AccessResult(True, "custom_enabled")

        # ۴) اشتراک tenant باید فعال باشد.
        subscription = await self.subscription_repo.get_active_by_tenant(tenant_id)
        if subscription is None:
            return AccessResult(False, "no_subscription")
        if subscription.status not in _ALLOWED_SUBSCRIPTION_STATUSES:
            return AccessResult(False, "subscription_inactive")

        # ۵) قابلیت باید در پلن اشتراک فعال باشد.
        plan_feature = await self.plan_feature_repo.get(
            subscription.plan_id, feature.id
        )
        if plan_feature is None or not plan_feature.is_enabled:
            return AccessResult(False, "feature_not_in_plan")

        return AccessResult(True, "plan_enabled")
