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

import logging

import httpx
from eth_account import Account
from x402 import x402Client
from x402.http.clients import x402HttpxClient
from x402.mechanisms.evm import EthAccountSigner
from x402.mechanisms.evm.exact.register import register_exact_evm_client

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"


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

    Args:
        private_key: Hex-encoded Ethereum private key (with or without 0x prefix).
        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
        account = Account.from_key(private_key)
        self._x402 = x402Client()
        signer = EthAccountSigner(account)
        register_exact_evm_client(self._x402, signer)

        self._wallet_address = account.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()
