"""Bittensor wallet authentication helper for ORO SDK.

This module provides utilities to authenticate API requests using
Bittensor wallet signatures (SR25519).
"""

from __future__ import annotations

import time
import uuid
from typing import TYPE_CHECKING, Any
from urllib.parse import urlparse

import httpx
from attrs import define, field

from .retry import AsyncRetryTransport, RetryConfig, RetryTransport

if TYPE_CHECKING:
    from bittensor_wallet import Wallet


def is_public_endpoint(url: str | None) -> bool:
    """Check if a URL path is an unauthenticated endpoint that doesn't require signing.

    Unauthenticated endpoints include:
    - /v1/public/* - All public API routes
    - /v1/auth/* - Authentication flow endpoints (challenge, session, logout)
    - /health - Health check endpoint

    Args:
        url: The URL or path to check.

    Returns:
        True if the endpoint doesn't require signing, False otherwise.
    """
    if not url:
        return False

    # Extract pathname from full URL or use as-is if already a path
    try:
        parsed = urlparse(url)
        pathname = parsed.path or url
    except Exception:
        pathname = url

    # Check for unauthenticated endpoints
    return (
        "/v1/public/" in pathname
        or "/v1/auth/" in pathname
        or pathname == "/health"
        or pathname.endswith("/health")
    )


def generate_auth_headers(wallet: "Wallet", nonce: str | None = None) -> dict[str, str]:
    """Generate authentication headers for a Bittensor wallet.

    Args:
        wallet: Bittensor wallet with hotkey for signing.
        nonce: Optional nonce (defaults to UUID4).

    Returns:
        Dictionary with X-Hotkey, X-Signature, X-Nonce, X-Timestamp headers.

    Note:
        The message format is '{hotkey}:{timestamp}:{nonce}' to match
        the ORO Backend's expected signature format.
    """
    if nonce is None:
        nonce = str(uuid.uuid4())

    hotkey = wallet.hotkey.ss58_address
    timestamp = str(int(time.time()))
    message = f"{hotkey}:{timestamp}:{nonce}"

    # Sign the message with the hotkey
    signature = wallet.hotkey.sign(message.encode()).hex()

    return {
        "X-Hotkey": hotkey,
        "X-Signature": f"0x{signature}",
        "X-Nonce": nonce,
        "X-Timestamp": timestamp,
    }


class SigningTransport(httpx.BaseTransport):
    """httpx transport that signs authenticated requests with Bittensor wallet.

    This transport intercepts requests and adds fresh authentication
    headers for non-public endpoints before passing to the underlying transport.

    Public endpoints (/v1/public/*, /health) are passed through without signing.

    Example:
        >>> from oro_sdk import Client
        >>> from oro_sdk.bittensor_auth import SigningTransport
        >>>
        >>> transport = SigningTransport(wallet=wallet)
        >>> client = Client(base_url="https://api.oro.ai", httpx_args={"transport": transport})
    """

    def __init__(self, wallet: "Wallet", wrapped: httpx.BaseTransport | None = None):
        self._wallet = wallet
        self._wrapped = wrapped or httpx.HTTPTransport()

    def handle_request(self, request: httpx.Request) -> httpx.Response:
        # Skip signing for public endpoints
        if not is_public_endpoint(str(request.url)):
            auth_headers = generate_auth_headers(self._wallet)
            for key, value in auth_headers.items():
                request.headers[key] = value
        return self._wrapped.handle_request(request)


class AsyncSigningTransport(httpx.AsyncBaseTransport):
    """Async httpx transport that signs authenticated requests with Bittensor wallet.

    Public endpoints (/v1/public/*, /health) are passed through without signing.
    """

    def __init__(self, wallet: "Wallet", wrapped: httpx.AsyncBaseTransport | None = None):
        self._wallet = wallet
        self._wrapped = wrapped or httpx.AsyncHTTPTransport()

    async def handle_async_request(self, request: httpx.Request) -> httpx.Response:
        # Skip signing for public endpoints
        if not is_public_endpoint(str(request.url)):
            auth_headers = generate_auth_headers(self._wallet)
            for key, value in auth_headers.items():
                request.headers[key] = value
        return await self._wrapped.handle_async_request(request)


@define
class BittensorAuthClient:
    """HTTP client that automatically signs requests with a Bittensor wallet.

    This client is compatible with the generated API functions and adds fresh
    authentication headers to each request via a custom transport.

    Example:
        >>> from bittensor_wallet import Wallet
        >>> from oro_sdk import BittensorAuthClient
        >>> from oro_sdk.api.validator import claim_work
        >>>
        >>> wallet = Wallet(name="validator", hotkey="default")
        >>> client = BittensorAuthClient(
        ...     base_url="https://api.oro.ai",
        ...     wallet=wallet,
        ... )
        >>> work = claim_work.sync(client=client)
    """

    _base_url: str = field(alias="base_url")
    _wallet: "Wallet" = field(alias="wallet")
    _timeout: httpx.Timeout | None = field(default=None, kw_only=True, alias="timeout")
    _verify_ssl: bool = field(default=True, kw_only=True, alias="verify_ssl")
    _headers: dict[str, str] = field(factory=dict, kw_only=True, alias="headers")
    _retry_config: RetryConfig = field(factory=RetryConfig, kw_only=True, alias="retry_config")
    _client: httpx.Client | None = field(default=None, init=False)
    _async_client: httpx.AsyncClient | None = field(default=None, init=False)

    # Required for compatibility with generated API functions
    raise_on_unexpected_status: bool = field(default=False, kw_only=True)

    def get_httpx_client(self) -> httpx.Client:
        """Get the underlying httpx.Client with signing and retry transports.

        The returned client will automatically add Bittensor auth headers
        to every request and retry transient failures.
        """
        if self._client is None:
            signing = SigningTransport(self._wallet)
            transport = RetryTransport(self._retry_config, wrapped=signing)
            self._client = httpx.Client(
                base_url=self._base_url,
                timeout=self._timeout,
                verify=self._verify_ssl,
                headers=self._headers,
                transport=transport,
            )
        return self._client

    def get_async_httpx_client(self) -> httpx.AsyncClient:
        """Get the underlying httpx.AsyncClient with signing and retry transports.

        The returned client will automatically add Bittensor auth headers
        to every request and retry transient failures.
        """
        if self._async_client is None:
            signing = AsyncSigningTransport(self._wallet)
            transport = AsyncRetryTransport(self._retry_config, wrapped=signing)
            self._async_client = httpx.AsyncClient(
                base_url=self._base_url,
                timeout=self._timeout,
                verify=self._verify_ssl,
                headers=self._headers,
                transport=transport,
            )
        return self._async_client

    def __enter__(self) -> "BittensorAuthClient":
        self.get_httpx_client().__enter__()
        return self

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

    async def __aenter__(self) -> "BittensorAuthClient":
        await self.get_async_httpx_client().__aenter__()
        return self

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