"""Subscription guards — feature gating utilities.

Two entry points:

``require_feature(feature_key)``
    Decorator for DRF API views.  Raises HTTP 402 if the feature is not
    enabled for the current request's tenant.

``check_feature(tenant_id, feature_key)``
    Pure boolean check for use inside service / domain code.
"""

from __future__ import annotations

import functools
from typing import TYPE_CHECKING

from rest_framework.exceptions import PermissionDenied
from rest_framework.request import Request

if TYPE_CHECKING:
    pass


# ---------------------------------------------------------------------------
# HTTP 402 exception
# ---------------------------------------------------------------------------


class FeatureNotAvailable(PermissionDenied):
    """Raised when a tenant does not have access to a required feature.

    Returns HTTP 402 Payment Required with a structured body so the
    frontend can display an upgrade prompt.
    """

    status_code = 402
    default_code = "FEATURE_NOT_AVAILABLE"

    def __init__(self, feature_key: str) -> None:
        self.feature_key = feature_key
        super().__init__(
            detail={
                "code": "FEATURE_NOT_AVAILABLE",
                "feature": feature_key,
                "message": (
                    f"Your current plan does not include access to '{feature_key}'. "
                    "Please upgrade your subscription."
                ),
                "upgrade_url": "/subscription/plans/",
            }
        )


# ---------------------------------------------------------------------------
# check_feature — non-HTTP helper
# ---------------------------------------------------------------------------


def check_feature(tenant_id: int, feature_key: str) -> bool:
    """Return True if the feature is enabled for the given tenant.

    Delegates to the subscription Feature Resolution Algorithm.
    Safe to call from services, tasks, and management commands.
    """
    from simorgh.apps.subscription.selectors import is_feature_enabled
    from simorgh.apps.tenants.models import Tenant

    try:
        tenant = Tenant.objects.get(pk=tenant_id)
    except Tenant.DoesNotExist:
        return False

    return is_feature_enabled(tenant, feature_key)


# ---------------------------------------------------------------------------
# require_feature — DRF view decorator
# ---------------------------------------------------------------------------


def require_feature(feature_key: str):
    """Decorator that gates a DRF view behind a subscription feature check.

    Usage::

        @require_feature("crm.pipeline")
        class PipelineViewSet(ModelViewSet):
            ...

        # Or on a specific action:
        @require_feature("hr.payroll")
        @action(detail=False, methods=["get"])
        def payslips(self, request):
            ...

    Raises ``FeatureNotAvailable`` (HTTP 402) when the feature is not
    enabled for the current tenant.  The tenant is resolved from
    ``request.tenant`` (set by ``TenantResolverMiddleware``).
    """

    def decorator(view_func_or_class):
        # Handle both CBVs and plain functions / DRF actions.
        if isinstance(view_func_or_class, type):
            # Class-based view — wrap dispatch()
            original_dispatch = view_func_or_class.dispatch

            @functools.wraps(original_dispatch)
            def _dispatch(self_view, request: Request, *args, **kwargs):
                _assert_feature(request, feature_key)
                return original_dispatch(self_view, request, *args, **kwargs)

            view_func_or_class.dispatch = _dispatch
            return view_func_or_class
        else:
            # Function / DRF @action
            @functools.wraps(view_func_or_class)
            def _wrapper(self_or_request, *args, **kwargs):
                # Support both unbound method (self, request) and plain func (request)
                if isinstance(self_or_request, Request):
                    request = self_or_request
                    _assert_feature(request, feature_key)
                    return view_func_or_class(self_or_request, *args, **kwargs)
                else:
                    # self_or_request is `self` (ViewSet instance)
                    request = args[0] if args else kwargs.get("request")
                    _assert_feature(request, feature_key)
                    return view_func_or_class(self_or_request, *args, **kwargs)

            return _wrapper

    return decorator


def _assert_feature(request, feature_key: str) -> None:
    """Raise FeatureNotAvailable if the tenant's plan does not include the feature."""
    from simorgh.apps.subscription.selectors import is_feature_enabled

    tenant = getattr(request, "tenant", None)
    if tenant is None:
        # No tenant on request — let auth middleware handle it separately.
        return

    if not is_feature_enabled(tenant, feature_key):
        raise FeatureNotAvailable(feature_key)
