"""STTClient — main entry point for the dtelecom-stt SDK."""

import re
import logging

import httpx
from x402 import x402Client
from x402.http.clients import x402HttpxClient

from .exceptions import ConnectionError, PaymentError, STTError
from .stream import Stream
from .types import PricingInfo, SessionInfo

logger = logging.getLogger("dtelecom_stt")

DEFAULT_URL = "https://x402stt.dtelecom.org"

_HEX_KEY_RE = re.compile(r"^[0-9a-fA-F]{64}$")


def _is_evm_key(key: str) -> bool:
    """Check if key looks like an EVM private key (hex-encoded, 32 bytes)."""
    k = key[2:] if key.startswith("0x") else key
    return bool(_HEX_KEY_RE.match(k))


class STTClient:
    """Client for dTelecom real-time speech-to-text with x402 micropayments.

    Accepts either an EVM private key (hex, 0x-prefixed) or a Solana private
    key (base58-encoded keypair).  The wallet type is detected automatically.

    Args:
        private_key: EVM hex key (``0x...``) or Solana base58 key.
        url: Base URL of the STT proxy. Defaults to https://x402stt.dtelecom.org.
    """

    def __init__(self, private_key: str, url: str = DEFAULT_URL):
        self._url = url.rstrip("/")
        self._ws_url = self._url.replace("https://", "wss://").replace("http://", "ws://")

        # x402 payment setup — detect key type
        self._x402 = x402Client()

        if _is_evm_key(private_key):
            from eth_account import Account
            from x402.mechanisms.evm import EthAccountSigner
            from x402.mechanisms.evm.exact.register import register_exact_evm_client

            account = Account.from_key(private_key)
            signer = EthAccountSigner(account)
            register_exact_evm_client(self._x402, signer)
            self._wallet_address = account.address
        else:
            from x402.mechanisms.svm.signers import KeypairSigner
            from x402.mechanisms.svm.exact.register import register_exact_svm_client

            signer = KeypairSigner.from_base58(private_key)
            register_exact_svm_client(self._x402, signer)
            self._wallet_address = signer.address

        logger.info("STTClient initialized, wallet=%s", self._wallet_address)

    def session(
        self,
        minutes: int = 5,
        language: str = "en",
        auto_extend: bool = True,
    ) -> "_SessionContext":
        """Create a paid STT session.

        Returns an async context manager that yields a Stream.

        Args:
            minutes: Session duration in minutes (5-120).
            language: Language code (e.g. "en", "ru", "de").
            auto_extend: Automatically buy more time when session is expiring.
        """
        return _SessionContext(self, minutes, language, auto_extend)

    async def _create_session(self, minutes: int, language: str) -> SessionInfo:
        """Buy a session via x402 payment."""
        async with x402HttpxClient(self._x402) as http:
            resp = await http.post(
                f"{self._url}/v1/session",
                json={"minutes": minutes, "language": language},
            )
            await resp.aread()

            if resp.status_code == 402:
                raise PaymentError(
                    f"Payment failed: {resp.json().get('message', resp.text)}"
                )
            if resp.status_code != 200:
                raise STTError(
                    f"Session creation failed ({resp.status_code}): {resp.text}"
                )

            data = resp.json()
            return SessionInfo(
                session_id=data["session_id"],
                session_key=data["session_key"],
                ws_url=data.get("ws_url", f"{self._ws_url}/v1/stream"),
                remaining_seconds=data["remaining_seconds"],
                minutes=data["minutes"],
                price_usd=data["price_usd"],
            )

    async def _extend_session(self, session_id: str, minutes: int = 5) -> dict:
        """Extend session with additional paid minutes."""
        async with x402HttpxClient(self._x402) as http:
            resp = await http.post(
                f"{self._url}/v1/session/extend",
                json={"session_id": session_id, "minutes": minutes},
            )
            await resp.aread()

            if resp.status_code != 200:
                raise PaymentError(
                    f"Extend failed ({resp.status_code}): {resp.text}"
                )
            return resp.json()

    async def pricing(self) -> PricingInfo:
        """Get pricing information (no payment required)."""
        async with httpx.AsyncClient() as http:
            try:
                resp = await http.get(f"{self._url}/pricing", timeout=10)
            except httpx.RequestError as e:
                raise ConnectionError(f"Cannot reach server: {e}") from e

            if resp.status_code != 200:
                raise STTError(f"Pricing request failed ({resp.status_code})")

            data = resp.json()
            return PricingInfo(
                price_per_minute_usd=data["price_per_minute_usd"],
                min_minutes=data["min_minutes"],
                max_minutes=data["max_minutes"],
                min_price_usd=data.get(
                    "min_price_usd",
                    data["price_per_minute_usd"] * data["min_minutes"],
                ),
                currency=data["currency"],
                network=data["network"],
            )

    async def health(self) -> dict:
        """Check server health (no payment required)."""
        async with httpx.AsyncClient() as http:
            try:
                resp = await http.get(f"{self._url}/health", timeout=10)
            except httpx.RequestError as e:
                raise ConnectionError(f"Cannot reach server: {e}") from e
            return resp.json()


class _SessionContext:
    """Async context manager that creates a session and returns a Stream."""

    def __init__(self, client: STTClient, minutes: int, language: str, auto_extend: bool):
        self._client = client
        self._minutes = minutes
        self._language = language
        self._auto_extend = auto_extend
        self._stream: Stream | None = None

    async def __aenter__(self) -> Stream:
        info = await self._client._create_session(self._minutes, self._language)
        logger.info(
            "Session created: id=%s, %ds remaining, $%s",
            info.session_id[:8],
            info.remaining_seconds,
            info.price_usd,
        )

        ws_url = f"{self._client._ws_url}/v1/stream"
        self._stream = Stream(
            ws_url=ws_url,
            session_info=info,
            client=self._client,
            language=self._language,
            auto_extend=self._auto_extend,
        )
        await self._stream._connect()
        return self._stream

    async def __aexit__(self, exc_type, exc_val, exc_tb):
        if self._stream:
            await self._stream.close()
