"""Tab API client for agent payments."""

from __future__ import annotations

import httpx
from typing import Any

from .exceptions import (
    TabError,
    InsufficientBalanceError,
    InvalidPaymentError,
    AuthenticationError,
)


class TabClient:
    """Client for interacting with the Tab payments API.

    Example:
        >>> client = TabClient(api_key="tab_live_xxx")
        >>> balance = client.balance()
        >>> print(f"Balance: {balance['balance_formatted']}")
    """

    DEFAULT_BASE_URL = "https://api.usetab.dev/v1"

    def __init__(
        self,
        api_key: str,
        base_url: str | None = None,
        timeout: float = 30.0,
    ):
        """Initialize the Tab client.

        Args:
            api_key: Your Tab API key (starts with 'tab_live_')
            base_url: API base URL (defaults to https://api.usetab.dev/v1)
            timeout: Request timeout in seconds
        """
        self.api_key = api_key
        self.base_url = (base_url or self.DEFAULT_BASE_URL).rstrip("/")
        self.timeout = timeout
        self._client = httpx.Client(
            base_url=self.base_url,
            headers={"Authorization": f"Bearer {api_key}"},
            timeout=timeout,
        )

    def __enter__(self) -> "TabClient":
        return self

    def __exit__(self, *args: Any) -> None:
        self.close()

    def close(self) -> None:
        """Close the HTTP client."""
        self._client.close()

    def _handle_error(self, response: httpx.Response) -> None:
        """Handle API error responses."""
        if response.status_code == 401:
            raise AuthenticationError("Invalid API key", status_code=401)

        if response.status_code >= 400:
            try:
                data = response.json()
                detail = data.get("detail", str(data))
            except Exception:
                detail = response.text

            if "insufficient balance" in str(detail).lower():
                raise InsufficientBalanceError(detail, status_code=response.status_code)
            elif "invalid" in str(detail).lower() and "payment" in str(detail).lower():
                raise InvalidPaymentError(detail, status_code=response.status_code)
            else:
                raise TabError(detail, status_code=response.status_code)

    def balance(self) -> dict:
        """Get the current agent's balance.

        Returns:
            dict with keys:
                - agent_id: Your agent ID
                - balance_microcents: Balance in microcents (1 cent = 1,000,000 microcents)
                - balance_formatted: Human-readable balance (e.g., "$1.50")

        Example:
            >>> balance = client.balance()
            >>> print(balance['balance_formatted'])
            '$10.00'
        """
        response = self._client.get("/balance")
        self._handle_error(response)
        return response.json()

    def pay(
        self,
        to: str,
        amount_microcents: int,
        memo: str | None = None,
    ) -> dict:
        """Transfer microcents to another agent.

        Args:
            to: Recipient agent ID (e.g., "agent_xxx" or just the UUID)
            amount_microcents: Amount to transfer (1 cent = 1,000,000 microcents)
            memo: Optional memo for the transaction

        Returns:
            dict with keys:
                - id: Transaction ID
                - payment_token: Token to prove payment (share with tool provider)
                - amount_microcents: Amount transferred
                - balance_after_microcents: Your new balance

        Example:
            >>> payment = client.pay(
            ...     to="agent_abc123",
            ...     amount_microcents=5_000_000,  # $0.05
            ...     memo="Tool usage fee"
            ... )
            >>> print(payment['payment_token'])
            'tab_pay_xxx'
        """
        payload = {
            "to": to,
            "amount_microcents": amount_microcents,
        }
        if memo:
            payload["memo"] = memo

        response = self._client.post("/pay", json=payload)
        self._handle_error(response)
        return response.json()

    def verify(
        self,
        payment_token: str,
        expected_amount_microcents: int | None = None,
    ) -> dict:
        """Verify a payment token (for tool providers).

        Use this to verify that an agent actually paid you before
        providing access to your tool or service.

        Args:
            payment_token: The payment token to verify (starts with 'tab_pay_')
            expected_amount_microcents: Optionally verify the amount matches

        Returns:
            dict with keys:
                - valid: True if payment is valid
                - from_agent: ID of the agent who paid
                - amount_microcents: Amount that was paid

        Example:
            >>> result = client.verify(payment_token="tab_pay_xxx")
            >>> if result['valid']:
            ...     print(f"Payment verified from {result['from_agent']}")
        """
        payload = {"payment_token": payment_token}
        if expected_amount_microcents is not None:
            payload["expected_amount_microcents"] = expected_amount_microcents

        response = self._client.post("/verify", json=payload)
        self._handle_error(response)
        return response.json()


class AsyncTabClient:
    """Async client for interacting with the Tab payments API.

    Example:
        >>> async with AsyncTabClient(api_key="tab_live_xxx") as client:
        ...     balance = await client.balance()
    """

    DEFAULT_BASE_URL = "https://api.usetab.dev/v1"

    def __init__(
        self,
        api_key: str,
        base_url: str | None = None,
        timeout: float = 30.0,
    ):
        self.api_key = api_key
        self.base_url = (base_url or self.DEFAULT_BASE_URL).rstrip("/")
        self.timeout = timeout
        self._client = httpx.AsyncClient(
            base_url=self.base_url,
            headers={"Authorization": f"Bearer {api_key}"},
            timeout=timeout,
        )

    async def __aenter__(self) -> "AsyncTabClient":
        return self

    async def __aexit__(self, *args: Any) -> None:
        await self.close()

    async def close(self) -> None:
        await self._client.aclose()

    def _handle_error(self, response: httpx.Response) -> None:
        if response.status_code == 401:
            raise AuthenticationError("Invalid API key", status_code=401)

        if response.status_code >= 400:
            try:
                data = response.json()
                detail = data.get("detail", str(data))
            except Exception:
                detail = response.text

            if "insufficient balance" in str(detail).lower():
                raise InsufficientBalanceError(detail, status_code=response.status_code)
            elif "invalid" in str(detail).lower() and "payment" in str(detail).lower():
                raise InvalidPaymentError(detail, status_code=response.status_code)
            else:
                raise TabError(detail, status_code=response.status_code)

    async def balance(self) -> dict:
        """Get the current agent's balance."""
        response = await self._client.get("/balance")
        self._handle_error(response)
        return response.json()

    async def pay(
        self,
        to: str,
        amount_microcents: int,
        memo: str | None = None,
    ) -> dict:
        """Transfer microcents to another agent."""
        payload = {"to": to, "amount_microcents": amount_microcents}
        if memo:
            payload["memo"] = memo

        response = await self._client.post("/pay", json=payload)
        self._handle_error(response)
        return response.json()

    async def verify(
        self,
        payment_token: str,
        expected_amount_microcents: int | None = None,
    ) -> dict:
        """Verify a payment token."""
        payload = {"payment_token": payment_token}
        if expected_amount_microcents is not None:
            payload["expected_amount_microcents"] = expected_amount_microcents

        response = await self._client.post("/verify", json=payload)
        self._handle_error(response)
        return response.json()
