from typing import Protocol

import sentry_sdk
from django.db.models.functions import Now
from ninja.errors import HttpError
from ninja.security import HttpBearer

from authapp.auth_utils import hash_api_key, instance_for_api_key
from authapp.models import CFAPIKeys, Organizations, Subscriptions


class AuthenticatedRequest(Protocol):
    """Protocol for requests authenticated via API key middleware.

    These attributes are added by the ApiKey authentication class.
    """

    user: str  # user_id from the API key
    tier: str  # subscription tier
    api_key_id: int  # API key instance ID
    should_log_features: bool  # whether to log optimization features


async def check_subscription_status(user_id, tier, organization_id=None) -> bool:
    """Check if a user has a premium subscription that doesn't require feature logging.

    Args:
        user_id: The ID of the user to check
        tier: The user's tier if already available
        organization_id: The ID of the user's organization if available

    Returns:
        bool: False if features should not be logged (premium user or paid org), True otherwise

    """
    # If tier is already set, no need to check subscription
    if tier is not None:
        return False

    try:
        # Check if user belongs to a paid organization
        if organization_id:
            org = await Organizations.objects.filter(id=organization_id).afirst()
            if org and org.name == "codeflash-ai":
                return True
            if org and org.subscription:
                # Paid organization - don't log features
                return False

        subscription = await Subscriptions.objects.filter(user_id=user_id).afirst()
        if subscription and subscription.plan_type.lower() in ["pro", "enterprise"]:
            # Premium users for CF- don't log features
            return False
    except Exception as e:
        print(f"Error checking subscription: {e!s}")
        sentry_sdk.capture_exception(e)
        # Default to not logging
        return False
    return True


class AuthBearer(HttpBearer):
    async def authenticate(self, request, token):
        hashed_token = hash_api_key(token)
        try:
            num_users = await CFAPIKeys.objects.filter(key=hashed_token).aupdate(last_used=Now())
            if num_users == 0:
                raise HttpError(403, "Invalid API key")
            if num_users == 1:
                api_key_instance = await instance_for_api_key(hashed_token)
                if not api_key_instance:
                    print(f"Instance not found for api key {token}. Returning 403")
                    raise HttpError(403, "Invalid API key")
                request.user = api_key_instance.user_id
                request.tier = api_key_instance.tier
                request.api_key_id = api_key_instance.id
                request.organization_id = api_key_instance.organization_id
                request.should_log_features = await check_subscription_status(
                    user_id=request.user, tier=request.tier, organization_id=request.organization_id
                )
                return token

            print("THIS SHOULD NOT HAPPEN! More than one users found in the db with the same api key!")
            raise HttpError(403, "Invalid API key")
        except CFAPIKeys.DoesNotExist:
            raise HttpError(403, "Invalid API key")
