"""OpenHedgeAgent — the main entry point for the OpenHedge Python SDK.

Instantiate with an API key and (optionally) a wallet private key to
interact with the OpenHedge protocol on Base L2.

Usage::

    async with OpenHedgeAgent(api_key="oh_...", wallet_private_key="0x...") as agent:
        vault = await agent.vault.get("0x...")
        result = await agent.trade.swap("0x...", "USDC", "WETH", 100_000_000)
"""

from __future__ import annotations

from typing import Optional

import httpx

from openhedge.constants import SDK_VERSION, get_chain_config
from openhedge.modules.agent import AgentModule
from openhedge.modules.leaderboard import LeaderboardModule
from openhedge.modules.market import MarketModule
from openhedge.modules.portfolio import PortfolioModule
from openhedge.modules.stream import StreamModule
from openhedge.modules.trade import TradeModule
from openhedge.modules.trade_raw import TradeRawModule
from openhedge.modules.vault import VaultModule


class OpenHedgeAgent:
    """Top-level client for the OpenHedge protocol.

    Parameters
    ----------
    api_key:
        Platform API key (``oh_...`` prefix).
    wallet_private_key:
        Hex-encoded private key for signing on-chain transactions.
        Required only for write operations (deploy, trade, etc.).
    network:
        Target network identifier.  ``'base-testnet'`` (default) or
        ``'base-mainnet'``.
    timeout:
        HTTP request timeout in seconds.
    max_retries:
        Maximum retry attempts for transient failures.
    """

    def __init__(
        self,
        api_key: str,
        *,
        wallet_private_key: Optional[str] = None,
        network: str = "base-testnet",
        timeout: float = 30.0,
        max_retries: int = 3,
    ) -> None:
        self._api_key = api_key
        self._wallet_private_key = wallet_private_key
        self._network = network
        self._max_retries = max_retries

        config = get_chain_config(network)
        self._chain_id = config["chain_id"]

        rpc_url = config["rpc_url"]
        api_gateway = config["api"]["gateway"]
        feed_url = config["api"]["feed"]

        if not rpc_url.startswith("https://"):
            raise ValueError(f"RPC URL must use HTTPS: {rpc_url}")
        if not api_gateway.startswith("https://"):
            raise ValueError(f"API gateway must use HTTPS: {api_gateway}")
        if not feed_url.startswith("wss://"):
            raise ValueError(f"Feed URL must use WSS: {feed_url}")

        self._rpc_url = rpc_url
        self._feed_url = feed_url

        self._http = httpx.AsyncClient(
            timeout=timeout,
            headers={
                "User-Agent": f"openhedge-python/{SDK_VERSION}",
            },
        )

        # Module cache (lazy-initialised via properties)
        self._agent_mod: Optional[AgentModule] = None
        self._vault_mod: Optional[VaultModule] = None
        self._market_mod: Optional[MarketModule] = None
        self._trade_mod: Optional[TradeModule] = None
        self._trade_raw_mod: Optional[TradeRawModule] = None
        self._portfolio_mod: Optional[PortfolioModule] = None
        self._leaderboard_mod: Optional[LeaderboardModule] = None
        self._stream_mod: Optional[StreamModule] = None

    # ------------------------------------------------------------------
    # Module accessors (lazy-initialised)
    # ------------------------------------------------------------------

    def _module_kwargs(self) -> dict:
        return dict(
            http=self._http,
            rpc_url=self._rpc_url,
            chain_id=self._chain_id,
            network=self._network,
            api_key=self._api_key,
            wallet_private_key=self._wallet_private_key,
            max_retries=self._max_retries,
        )

    @property
    def agent(self) -> AgentModule:
        """Agent registration and status (REST)."""
        if self._agent_mod is None:
            self._agent_mod = AgentModule(**self._module_kwargs())
        return self._agent_mod

    @property
    def vault(self) -> VaultModule:
        """Vault deployment, reads, and NAV queries (on-chain)."""
        if self._vault_mod is None:
            self._vault_mod = VaultModule(**self._module_kwargs())
        return self._vault_mod

    @property
    def market(self) -> MarketModule:
        """Pair metadata and gas estimates."""
        if self._market_mod is None:
            self._market_mod = MarketModule(**self._module_kwargs())
        return self._market_mod

    @property
    def trade(self) -> TradeModule:
        """High-level trade execution — swap, perps, lending (on-chain)."""
        if self._trade_mod is None:
            self._trade_mod = TradeModule(**self._module_kwargs())
        return self._trade_mod

    @property
    def trade_raw(self) -> TradeRawModule:
        """Low-level trade execution with raw adapter calldata."""
        if self._trade_raw_mod is None:
            self._trade_raw_mod = TradeRawModule(**self._module_kwargs())
        return self._trade_raw_mod

    @property
    def portfolio(self) -> PortfolioModule:
        """Portfolio reads and position enumeration (on-chain)."""
        if self._portfolio_mod is None:
            self._portfolio_mod = PortfolioModule(**self._module_kwargs())
        return self._portfolio_mod

    @property
    def leaderboard(self) -> LeaderboardModule:
        """Vault rankings and leaderboard (REST)."""
        if self._leaderboard_mod is None:
            self._leaderboard_mod = LeaderboardModule(**self._module_kwargs())
        return self._leaderboard_mod

    @property
    def stream(self) -> StreamModule:
        """Real-time WebSocket event feed."""
        if self._stream_mod is None:
            self._stream_mod = StreamModule(
                feed_url=self._feed_url,
                api_key=self._api_key,
            )
        return self._stream_mod

    # ------------------------------------------------------------------
    # Async context manager
    # ------------------------------------------------------------------

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

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

    async def close(self) -> None:
        """Release all resources — HTTP client and WebSocket connection."""
        self._wallet_private_key = None
        await self._http.aclose()
        if self._stream_mod is not None:
            await self._stream_mod.disconnect()

    # ------------------------------------------------------------------
    # Convenience
    # ------------------------------------------------------------------

    @property
    def address(self) -> Optional[str]:
        """The wallet address derived from the private key, if provided."""
        if self._wallet_private_key is None:
            return None
        from openhedge.utils.signing import get_account
        return get_account(self._wallet_private_key).address

    def __repr__(self) -> str:
        masked_key = self._api_key[:6] + "..." if len(self._api_key) > 6 else "***"
        return f"<OpenHedgeAgent network={self._network!r} key={masked_key}>"
