"""Main ZenOTC client classes."""

from __future__ import annotations

from typing import Optional, Union

from zenotc.clients.algo import AlgoClient
from zenotc.clients.execution import ExecutionClient
from zenotc.clients.market_data import MarketDataClient
from zenotc.clients.market_making import MarketMakingClient
from zenotc.clients.market_making_strategy import MarketMakingStrategyClient
from zenotc.clients.monitoring import MonitoringClient
from zenotc.clients.portfolio import PortfolioClient
from zenotc.clients.risk import RiskClient
from zenotc.clients.versioning import VersioningClient
from zenotc.config import Config, Environment
from zenotc.utils.http import HTTPClient
from zenotc.utils.rate_limiter import RateLimiter
from zenotc.utils.websocket import WebSocketClient


class ZenOTCClient:
    """ZenOTC client - convenience alias for AsyncZenOTCClient.

    This is an async client that provides access to all ZenOTC API endpoints.
    All methods are asynchronous and must be awaited.

    Example:
        async with ZenOTCClient(api_key="...", api_secret="...") as client:
            order = await client.execution.create_order(
                side="buy",
                asset="BTC",
                quantity=0.1,
                price=50000,
            )
    """

    def __init__(
        self,
        api_key: Optional[str] = None,
        api_secret: Optional[str] = None,
        environment: Union[str, Environment] = Environment.PRODUCTION,
        config: Optional[Config] = None,
        **kwargs,
    ) -> None:
        self._async_client = AsyncZenOTCClient(
            api_key=api_key,
            api_secret=api_secret,
            environment=environment,
            config=config,
            **kwargs,
        )

    @property
    def execution(self) -> ExecutionClient:
        """Access order execution endpoints."""
        return self._async_client.execution

    @property
    def market_making(self) -> MarketMakingClient:
        """Access market making (quote) endpoints."""
        return self._async_client.market_making

    @property
    def market_data(self) -> MarketDataClient:
        """Access market data endpoints."""
        return self._async_client.market_data

    @property
    def portfolio(self) -> PortfolioClient:
        """Access portfolio endpoints."""
        return self._async_client.portfolio

    @property
    def risk(self) -> RiskClient:
        """Access risk management endpoints."""
        return self._async_client.risk

    @property
    def algo(self) -> AlgoClient:
        """Access algorithmic execution endpoints (TWAP, VWAP, Iceberg, Conditional)."""
        return self._async_client.algo

    @property
    def mm_strategy(self) -> MarketMakingStrategyClient:
        """Access market making strategy endpoints for automated quoting."""
        return self._async_client.mm_strategy

    @property
    def monitoring(self) -> MonitoringClient:
        """Access execution monitoring endpoints for performance analysis."""
        return self._async_client.monitoring

    @property
    def versioning(self) -> VersioningClient:
        """Access versioning and deprecation management endpoints."""
        return self._async_client.versioning

    @property
    def config(self) -> Config:
        """Access the client configuration."""
        return self._async_client.config

    async def close(self) -> None:
        """Close the client and release resources."""
        await self._async_client.close()

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

    async def __aexit__(self, exc_type, exc_val, exc_tb) -> None:
        await self.close()

    def __repr__(self) -> str:
        return repr(self._async_client)


class AsyncZenOTCClient:
    """Async ZenOTC client - main entry point for the SDK."""

    def __init__(
        self,
        api_key: Optional[str] = None,
        api_secret: Optional[str] = None,
        environment: Union[str, Environment] = Environment.PRODUCTION,
        config: Optional[Config] = None,
        **kwargs,
    ) -> None:
        if config is not None:
            self._config = config
        elif api_key is not None and api_secret is not None:
            if isinstance(environment, str):
                environment = Environment(environment.lower())
            self._config = Config(
                api_key=api_key,
                api_secret=api_secret,
                environment=environment,
                **kwargs,
            )
        else:
            self._config = Config.from_env()

        self._http = HTTPClient(self._config)
        self._rate_limiter = RateLimiter(
            rate=self._config.rate_limit_per_second,
            per_seconds=1.0,
        )
        self._websocket = WebSocketClient(self._config)

        self._execution = ExecutionClient(self._http, self._rate_limiter, self._websocket)
        self._market_making = MarketMakingClient(self._http, self._rate_limiter, self._websocket)
        self._market_data = MarketDataClient(self._http, self._rate_limiter, self._websocket)
        self._portfolio = PortfolioClient(self._http, self._rate_limiter, self._websocket)
        self._risk = RiskClient(self._http, self._rate_limiter, self._websocket)
        self._algo = AlgoClient(self._http, self._rate_limiter, self._websocket)
        self._mm_strategy = MarketMakingStrategyClient(self._http, self._rate_limiter, self._websocket)
        self._monitoring = MonitoringClient(self._http, self._rate_limiter, self._websocket)
        self._versioning = VersioningClient(self._http)

    @property
    def execution(self) -> ExecutionClient:
        return self._execution

    @property
    def market_making(self) -> MarketMakingClient:
        return self._market_making

    @property
    def market_data(self) -> MarketDataClient:
        return self._market_data

    @property
    def portfolio(self) -> PortfolioClient:
        return self._portfolio

    @property
    def risk(self) -> RiskClient:
        return self._risk

    @property
    def algo(self) -> AlgoClient:
        return self._algo

    @property
    def mm_strategy(self) -> MarketMakingStrategyClient:
        return self._mm_strategy

    @property
    def monitoring(self) -> MonitoringClient:
        return self._monitoring

    @property
    def versioning(self) -> VersioningClient:
        return self._versioning

    @property
    def config(self) -> Config:
        return self._config

    async def close(self) -> None:
        await self._http.close()
        await self._websocket.disconnect()

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

    async def __aexit__(self, exc_type, exc_val, exc_tb) -> None:
        await self.close()

    def __repr__(self) -> str:
        return (
            f"AsyncZenOTCClient("
            f"environment={self._config.environment.value}, "
            f"base_url={self._config.effective_base_url})"
        )
