"""
CMDC 主客户端类。

所有 SDK 功能的入口。
"""

from __future__ import annotations

from contextlib import asynccontextmanager, contextmanager
from typing import Any, AsyncGenerator, Generator

import httpx

from cmdc._http import raise_for_status
from cmdc.session import AsyncSession, Session
from cmdc.types import HealthInfo, SessionOptions


class CMDC:
    """CMDC Python SDK 主客户端。

    管理 HTTP 连接池，提供创建/管理 Session 的接口。

    用法（同步）::

        client = CMDC(base_url="http://localhost:4000", api_key="sk-abc123")
        session = client.create_session(model="deepseek:deepseek-chat")
        try:
            for event in session.prompt("你好"):
                if event.type == "message_delta":
                    print(event.delta, end="")
        finally:
            session.stop()

    用法（Context Manager，推荐）::

        with CMDC(base_url="...", api_key="...") as client:
            with client.session(model="deepseek:deepseek-chat") as session:
                for event in session.prompt("你好"):
                    print(event.delta or "")
    """

    def __init__(
        self,
        base_url: str,
        api_key: str,
        *,
        timeout: float = 30.0,
        connect_timeout: float = 10.0,
        limits: httpx.Limits | None = None,
    ) -> None:
        """初始化客户端。

        Args:
            base_url: Gateway 服务地址，如 ``"http://localhost:4000"``。
            api_key: API Key，对应 X-API-Key 请求头。
            timeout: 请求超时时间（秒），默认 30 秒。SSE 流使用独立 ``timeout=None``。
            connect_timeout: 连接超时时间（秒），默认 10 秒。
            limits: httpx 连接池配置，默认使用 httpx 的默认值。
        """
        self._base_url = base_url.rstrip("/")
        self._api_key = api_key
        self._headers = {
            "X-API-Key": api_key,
            "Content-Type": "application/json",
            "Accept": "application/json",
        }

        _limits = limits or httpx.Limits(max_connections=20, max_keepalive_connections=10)
        self._sync_client = httpx.Client(
            timeout=httpx.Timeout(timeout=timeout, connect=connect_timeout),
            limits=_limits,
        )
        self._async_client = httpx.AsyncClient(
            timeout=httpx.Timeout(timeout=timeout, connect=connect_timeout),
            limits=_limits,
        )

    # ------------------------------------------------------------------
    # 健康检查
    # ------------------------------------------------------------------

    def health(self) -> HealthInfo:
        """检查 Gateway 健康状态（无需认证）。"""
        resp = self._sync_client.get(f"{self._base_url}/healthz")
        raise_for_status(resp)
        body = resp.json()
        return HealthInfo(
            status=body["status"],
            version=body["version"],
            active_sessions=body.get("sessions", {}).get("active", 0),
            tracked_keys=body.get("meter", {}).get("tracked_keys", 0),
            timestamp=body["timestamp"],
        )

    async def ahealth(self) -> HealthInfo:
        """异步版健康检查。"""
        resp = await self._async_client.get(f"{self._base_url}/healthz")
        raise_for_status(resp)
        body = resp.json()
        return HealthInfo(
            status=body["status"],
            version=body["version"],
            active_sessions=body.get("sessions", {}).get("active", 0),
            tracked_keys=body.get("meter", {}).get("tracked_keys", 0),
            timestamp=body["timestamp"],
        )

    # ------------------------------------------------------------------
    # 同步 Session 管理
    # ------------------------------------------------------------------

    def create_session(
        self,
        model: str | None = None,
        *,
        options: SessionOptions | None = None,
        **kwargs: Any,
    ) -> Session:
        """创建新 Session（同步）。

        可以传入 :class:`~cmdc.types.SessionOptions` 实例，
        也可以直接传入 keyword 参数。

        Args:
            model: LLM 模型标识，如 ``"deepseek:deepseek-chat"``。
            options: SessionOptions 实例（与 model/kwargs 二选一）。
            **kwargs: SessionOptions 的其他字段，如 ``system_prompt``、``tools`` 等。

        Returns:
            :class:`~cmdc.session.Session` 实例。

        用法::

            # 方式一：keyword 参数
            session = client.create_session(
                model="deepseek:deepseek-chat",
                system_prompt="你是助手",
                tools=["CMDC.Tool.Shell"],
            )

            # 方式二：SessionOptions
            opts = SessionOptions(model="deepseek:deepseek-chat")
            session = client.create_session(options=opts)
        """
        opts = self._resolve_options(model, options, kwargs)
        resp = self._sync_client.post(
            f"{self._base_url}/v1/sessions",
            headers=self._headers,
            json=opts.to_dict(),
        )
        raise_for_status(resp)
        body = resp.json()
        session_id = body["sessionId"]
        return Session(
            session_id=session_id,
            base_url=self._base_url,
            http_client=self._sync_client,
            headers=self._headers,
        )

    @contextmanager
    def session(
        self,
        model: str | None = None,
        *,
        options: SessionOptions | None = None,
        **kwargs: Any,
    ) -> Generator[Session, None, None]:
        """Context Manager：创建 Session，退出时自动 stop。

        用法::

            with client.session(model="deepseek:deepseek-chat") as session:
                for event in session.prompt("你好"):
                    print(event.delta or "")
        """
        sess = self.create_session(model, options=options, **kwargs)
        try:
            yield sess
        finally:
            try:
                sess.stop()
            except Exception:
                pass

    # ------------------------------------------------------------------
    # 异步 Session 管理
    # ------------------------------------------------------------------

    async def create_session_async(
        self,
        model: str | None = None,
        *,
        options: SessionOptions | None = None,
        **kwargs: Any,
    ) -> AsyncSession:
        """创建新 Session（异步）。"""
        opts = self._resolve_options(model, options, kwargs)
        resp = await self._async_client.post(
            f"{self._base_url}/v1/sessions",
            headers=self._headers,
            json=opts.to_dict(),
        )
        raise_for_status(resp)
        body = resp.json()
        session_id = body["sessionId"]
        return AsyncSession(
            session_id=session_id,
            base_url=self._base_url,
            http_client=self._async_client,
            headers=self._headers,
        )

    @asynccontextmanager
    async def asession(
        self,
        model: str | None = None,
        *,
        options: SessionOptions | None = None,
        **kwargs: Any,
    ) -> AsyncGenerator[AsyncSession, None]:
        """异步 Context Manager：创建 Session，退出时自动 stop。

        用法::

            async with client.asession(model="deepseek:deepseek-chat") as session:
                async for event in await session.aprompt("你好"):
                    print(event.delta or "")
        """
        sess = await self.create_session_async(model, options=options, **kwargs)
        try:
            yield sess
        finally:
            try:
                await sess.stop()
            except Exception:
                pass

    # ------------------------------------------------------------------
    # 客户端 Context Manager（管理 HTTP 连接池生命周期）
    # ------------------------------------------------------------------

    def __enter__(self) -> "CMDC":
        return self

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

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

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

    def close(self) -> None:
        """关闭同步 HTTP 连接池。"""
        self._sync_client.close()

    async def aclose(self) -> None:
        """关闭异步 HTTP 连接池。"""
        await self._async_client.aclose()

    # ------------------------------------------------------------------
    # 内部工具
    # ------------------------------------------------------------------

    def _resolve_options(
        self,
        model: str | None,
        options: SessionOptions | None,
        kwargs: dict[str, Any],
    ) -> SessionOptions:
        """将多种传参方式统一为 SessionOptions。"""
        if options is not None:
            return options
        if model is None:
            raise ValueError("必须提供 model 或 options 参数")

        from cmdc.types import SessionOptions as SO  # 避免循环导入

        return SO(
            model=model,
            session_id=kwargs.get("session_id"),
            system_prompt=kwargs.get("system_prompt"),
            working_dir=kwargs.get("working_dir"),
            tools=kwargs.get("tools", []),
            plugins=kwargs.get("plugins", []),
            blueprint=kwargs.get("blueprint"),
            max_turns=kwargs.get("max_turns"),
            max_tokens=kwargs.get("max_tokens"),
            skills_dirs=kwargs.get("skills_dirs", []),
            provider_opts=kwargs.get("provider_opts", {}),
        )
