"""Credit manager for subscription credit tracking.

Uses the AiVibe platform API (api.aivibe.cloud) for atomic credit deduction.
All credit data is stored in PostgreSQL at db.aivibe.cloud.
Admin status and credits come from the platform API — no hardcoded bypasses.
"""

import threading
from typing import TYPE_CHECKING

from aicippy.billing.models import CreditTransaction
from aicippy.config import get_settings
from aicippy.exceptions import ConfigurationError, ErrorContext
from aicippy.utils.async_utils import run_sync
from aicippy.utils.logging import get_logger

if TYPE_CHECKING:
    from aicippy.billing.plan_validator import PlanValidator
    from aicippy.platform import PlatformClient

logger = get_logger(__name__)


class CreditManager:
    """Manages credit checking and deduction for AiCippy usage.

    Uses the platform API (api.aivibe.cloud) for atomic credit operations.
    All data stored in PostgreSQL (db.aivibe.cloud, aicippy_com_* tables).
    Admin status comes from the platform API is_admin field.

    Args:
        plan_validator: PlanValidator instance for plan lookups.
        platform_client: PlatformClient for api.aivibe.cloud operations.

    """

    def __init__(
        self,
        plan_validator: PlanValidator,
        platform_client: PlatformClient | None = None,
    ) -> None:
        """Initialize the CreditManager."""
        self._validator = plan_validator
        self._platform_client = platform_client
        self._settings = get_settings()
        self._session_credits_used: int = 0
        self._session_lock = threading.Lock()

    def _get_credits_for_agent(
        self,
        agent_name: str | None = None,
        is_ultrathinking: bool = False,
        is_web_research: bool = False,
        tokens: int = 0,
    ) -> int:
        """Get the credit cost for a given agent and feature set."""
        cost = 0
        agent = agent_name or self._settings.default_model
        if agent == "odin":
            cost = self._settings.credits_per_invocation_odin
        elif agent == "arjuna":
            cost = self._settings.credits_per_invocation_arjuna
        else:
            cost = self._settings.credits_per_invocation_odin

        if is_ultrathinking:
            cost += self._settings.credits_per_ultrathinking
        if is_web_research:
            cost += self._settings.credits_per_web_research

        # Dynamic token-based pricing
        cost += tokens // 10000

        return max(1, cost)

    def check_credits(self, email: str, agent_name: str | None = None, tokens: int = 0) -> tuple[bool, int]:
        """Check if the user has sufficient credits for a given agent.

        All users (including admin) have real credit balances that are
        checked and deducted via the platform API.

        Args:
            email: User email address.
            agent_name: The agent being used ('odin' or 'arjuna').
            tokens: Estimated tokens.

        Returns:
            Tuple of (has_credits, remaining_count).

        """
        plan_info = self._validator.validate_or_cached(email)
        credits_required = self._get_credits_for_agent(agent_name, tokens=tokens)

        has_credits = plan_info.credits_remaining >= credits_required
        return has_credits, plan_info.credits_remaining

    def deduct_credit(
        self,
        email: str,
        agent_name: str | None = None,
        action: str = "aicippy_chat",
        is_ultrathinking: bool = False,
        is_web_research: bool = False,
        tokens: int = 0,
    ) -> CreditTransaction:
        """Deduct credits for an AiCippy invocation via platform API."""
        return run_sync(self.deduct_credit_async(email, agent_name, action, is_ultrathinking, is_web_research, tokens))

    async def check_credits_async(self, email: str, agent_name: str | None = None, tokens: int = 0) -> tuple[bool, int]:
        """Check if the user has sufficient credits (async version).

        All users (including admin) have real credit balances.

        Args:
            email: User email address.
            agent_name: The agent being used ('odin' or 'arjuna').
            tokens: Estimated tokens.

        Returns:
            Tuple of (has_credits, remaining_count).

        """
        plan_info = await self._validator.validate_or_cached_async(email)
        credits_required = self._get_credits_for_agent(agent_name, tokens=tokens)

        has_credits = plan_info.credits_remaining >= credits_required
        return has_credits, plan_info.credits_remaining

    async def deduct_credit_async(
        self,
        email: str,
        agent_name: str | None = None,
        action: str = "aicippy_chat",
        is_ultrathinking: bool = False,
        is_web_research: bool = False,
        tokens: int = 0,
    ) -> CreditTransaction:
        """Deduct credits for an AiCippy invocation via platform API (async version)."""
        plan_info = await self._validator.validate_or_cached_async(email)
        credits_to_deduct = self._get_credits_for_agent(agent_name, is_ultrathinking, is_web_research, tokens=tokens)

        if not self._platform_client:
            raise ConfigurationError(
                message="Platform client not initialized",
                context=ErrorContext(operation="deduct_credits"),
            )
        try:
            result = await self._platform_client.deduct_credits(
                amount=credits_to_deduct,
            )

            # Updated balance returned in the mutation result
            new_balance = result.get("balance", 0)

            self._validator.clear_cache()
            with self._session_lock:
                self._session_credits_used += credits_to_deduct

            logger.info(
                "credit_deducted",
                amount=credits_to_deduct,
                remaining=new_balance,
                action=action,
            )

            return CreditTransaction(
                success=True,
                credits_remaining=int(new_balance),
                transaction_id=result.get("id"),
                error=None,
            )

        except Exception as e:
            logger.exception(
                "credit_deduction_platform_error",
                error=str(e),
            )
            return CreditTransaction(
                success=False,
                credits_remaining=plan_info.credits_remaining,
                error=f"Credit deduction failed: {e}",
            )

    @property
    def session_credits_used(self) -> int:
        """Total credits used in this session."""
        with self._session_lock:
            return self._session_credits_used

    def get_remaining_credits(self, email: str) -> int:
        """Get the current remaining credit count from the platform API."""
        plan_info = self._validator.validate_or_cached(email)
        return plan_info.credits_remaining
