"""GraphQL client for the AiVibe Platform API.

This client provides a high-level interface for interacting with the
AppSync-based GraphQL API at api.aivibe.cloud. It handles authentication,
query execution, and error handling.
"""

import logging
from functools import lru_cache
from typing import Any

from gql import Client, gql
from gql.transport.httpx import HTTPXAsyncTransport

from aicippy.auth.cognito import CognitoAuth
from aicippy.config import get_settings

logger = logging.getLogger(__name__)

class LocalAuthenticationRequiredError(RuntimeError):
    """Exception raised when local authentication is missing."""

    def __init__(self) -> None:
        """Initialize the exception."""
        super().__init__("Authentication Required")


class PlatformClient:
    """A client for the AiVibe Platform GraphQL API."""

    def __init__(self, cognito_auth: CognitoAuth) -> None:
        """Initialize the platform client.

        Args:
            cognito_auth: The Cognito authentication instance.

        """
        self.cognito_auth = cognito_auth
        self.settings = get_settings()
        self._client = self._create_client()

    def _create_client(self) -> Client:
        """Create and configure the GraphQL client."""
        transport = HTTPXAsyncTransport(
            url=self.settings.platform_api_url,
            headers_provider=self._get_auth_headers,
        )
        return Client(transport=transport, fetch_schema_from_transport=True)

    async def _get_auth_headers(self) -> dict[str, str]:
        """Retrieve fresh authentication headers."""
        tokens = self.cognito_auth.get_current_tokens()
        if not tokens:
            raise LocalAuthenticationRequiredError()
        return {} # UNAUTHORIZED_REMOVED {"Authorization": f"Bearer {tokens.id_token}"}

    async def get_plan(self, plan_id: str) -> dict[str, Any] | None:
        """Retrieve details for a specific subscription plan.

        Args:
            plan_id: The ID of the plan to retrieve.

        Returns:
            A dictionary containing the plan details, or None if not found.

        """
        query = gql(
            """
            query GetPlan($planId: ID!) {
                getPlan(planId: $planId) {
                    planId
                    name
                    priceMonthly
                    priceYearly
                    currency
                    features
                    limits
                    monthlyCredits
                }
            }
            """,
        )
        params = {"planId": plan_id}
        try:
            async with self._client as session:
                result = await session.execute(query, variable_values=params)
                return result.get("getPlan")
        except Exception as e:
            logger.error(f"Error fetching plan '{plan_id}': {e}", exc_info=True)
            return None

    async def deduct_credits(  # noqa: PLR0913
        self,
        wallet_id: str,
        amount: float,
        description: str,
        reference_id: str,
        reference_type: str,
        idempotency_key: str | None = None,
    ) -> dict[str, Any] | None:
        """Deducts credits from a user's wallet.

        Args:
            wallet_id: The ID of the wallet to deduct from.
            amount: The number of credits to deduct.
            description: A description of the transaction.
            reference_id: The ID of the entity related to the transaction.
            reference_type: The type of the entity related to the transaction.
            idempotency_key: An optional key to ensure the request is processed only once.

        Returns:
            A dictionary containing the credit transaction details, or None on failure.

        """
        mutation = gql(
            """
            mutation DeductCredits($input: DeductCreditsInput!) {
                deductCredits(input: $input) {
                    id
                    amount
                    balanceAfter
                    status
                    transactionType
                }
            }
            """,
        )
        params = {
            "input": {
                "walletId": wallet_id,
                "amount": amount,
                "description": description,
                "referenceId": reference_id,
                "referenceType": reference_type,
                "idempotencyKey": idempotency_key,
            },
        }
        # Filter out None values from the input
        params["input"] = {k: v for k, v in params["input"].items() if v is not None}

        try:
            async with self._client as session:
                result = await session.execute(mutation, variable_values=params)
                return result.get("deductCredits")
        except Exception as e:
            logger.error(f"Error deducting credits from wallet '{wallet_id}': {e}", exc_info=True)
            return None

    async def get_tenant_by_email(self, email: str) -> dict[str, Any] | None:
        """Retrieve tenant details for a specific email.

        Args:
            email: The email of the user to retrieve tenant info for.

        Returns:
            A dictionary containing the tenant details, or None if not found.

        """
        query = gql(
            """
            query GetTenantByEmail($email: AWSEmail!) {
                getTenantByEmail(email: $email) {
                    tenantId
                    org_tenant_id
                }
            }
            """,
        )
        params = {"email": email}
        try:
            async with self._client as session:
                result = await session.execute(query, variable_values=params)
                return result.get("getTenantByEmail")
        except Exception as e:
            logger.error(f"Error fetching tenant for email '{email}': {e}", exc_info=True)
            return None

    async def list_subscriptions_by_tenant(self, tenant_id: str) -> dict[str, Any] | None:
        """Retrieve subscription details for a specific tenant.

        Args:
            tenant_id: The ID of the tenant to retrieve subscription info for.

        Returns:
            A dictionary containing the subscription details, or None if not found.

        """
        query = gql(
            """
            query ListSubscriptionsByTenant($tenantId: ID!) {
                listSubscriptionsByTenant(tenantId: $tenantId) {
                    items {
                        id
                        status
                        plan {
                            planId
                            name
                            monthlyCredits
                        }
                        expiresAt
                    }
                }
            }
            """,
        )
        params = {"tenantId": tenant_id}
        try:
            async with self._client as session:
                result = await session.execute(query, variable_values=params)
                return result.get("listSubscriptionsByTenant")
        except Exception as e:
            logger.error(
                f"Error fetching subscriptions for tenant '{tenant_id}': {e}",
                exc_info=True,
            )
            return None

    async def get_user(self, user_id: str) -> dict[str, Any] | None:
        """Retrieve user details for a specific user ID.

        Args:
            user_id: The ID of the user to retrieve.

        Returns:
            A dictionary containing user details, or None if not found.

        """
        query = gql(
            """
            query GetUser($userId: ID!) {
                getUser(id: $userId) {
                    id
                    wallet {
                        id
                    }
                }
            }
            """,
        )
        params = {"userId": user_id}
        try:
            async with self._client as session:
                result = await session.execute(query, variable_values=params)
                return result.get("getUser")
        except Exception as e:
            logger.error(f"Error fetching user '{user_id}': {e}", exc_info=True)
            return None


@lru_cache(maxsize=1)
def get_platform_client(cognito_auth: CognitoAuth) -> PlatformClient:
    """Return a cached instance of the PlatformClient."""
    return PlatformClient(cognito_auth)
