"""
Wrapped httpx module with proxy and CA certificate support.

This module provides pre-configured httpx Client/AsyncClient that automatically
uses the configured proxy and CA certificate settings.

Usage:
    from coze_workload_identity import httpx

    # Use module-level functions (uses default configured client)
    response = httpx.get("https://api.example.com/data")

    # Use Client class for more control
    with httpx.Client() as client:
        response = client.get("https://api.example.com/data")

    # Use AsyncClient for async operations
    async with httpx.AsyncClient() as client:
        response = await client.get("https://api.example.com/data")
"""

import logging
import threading
import os
from typing import Optional, Any

import httpx

from .proxy import HttpsProxy, CaBundlePath

logger = logging.getLogger("AuthProxyHttpx")


class Client(httpx.Client):
    """An httpx Client subclass with automatic proxy and CA configuration."""

    def __init__(self, **kwargs: Any):
        self._configure_kwargs(kwargs)
        super().__init__(**kwargs)

    def _configure_kwargs(self, kwargs: dict) -> None:
        """Configure kwargs with proxy and CA settings.

        Raises:
            ValueError: If HttpsProxy or CA bundle configuration fails
        """
        # Force configure trust_env to False
        kwargs["trust_env"] = False

        # Force configure proxy
        proxy_url = HttpsProxy()
        if not proxy_url:
            raise ValueError(
                "COZE_OUTBOUND_AUTH_PROXY environment variable is not configured. "
                "Please set this variable to configure the HTTPS proxy."
            )
        kwargs["proxy"] = proxy_url
        logger.info(f"Use HTTPS proxy: {proxy_url}")

        # Force configure verify
        ca_bundle_path = CaBundlePath()
        if not ca_bundle_path:
            raise ValueError(
                "COZE_OUTBOUND_AUTH_PROXY_CA_PATH environment variable is not configured. "
                "Please set this variable to configure the CA bundle file path."
            )
        kwargs["verify"] = ca_bundle_path
        logger.info(f"Use CA bundle: {ca_bundle_path}")

        # Configure PPE env header if needed
        coze_env_lane = os.environ.get('COZE_ENV_LANE')
        if coze_env_lane and coze_env_lane.startswith('ppe_'):
            headers = kwargs.get("headers", {})
            # Handle headers if it's not a dict (e.g. httpx.Headers)
            if not isinstance(headers, dict):
                headers = dict(headers)
            
            headers['x-tt-env'] = coze_env_lane
            kwargs["headers"] = headers
            logger.info(f"Set x-tt-env header: {coze_env_lane}")


class AsyncClient(httpx.AsyncClient):
    """An httpx AsyncClient subclass with automatic proxy and CA configuration."""

    def __init__(self, **kwargs: Any):
        self._configure_kwargs(kwargs)
        super().__init__(**kwargs)

    def _configure_kwargs(self, kwargs: dict) -> None:
        """Configure kwargs with proxy and CA settings.

        Raises:
            ValueError: If HttpsProxy or CA bundle configuration fails
        """
        # Force configure trust_env to False
        kwargs["trust_env"] = False

        # Force configure proxy
        proxy_url = HttpsProxy()
        if not proxy_url:
            raise ValueError(
                "COZE_OUTBOUND_AUTH_PROXY environment variable is not configured. "
                "Please set this variable to configure the HTTPS proxy."
            )
        kwargs["proxy"] = proxy_url
        logger.info(f"Use HTTPS proxy: {proxy_url}")

        # Force configure verify
        ca_bundle_path = CaBundlePath()
        if not ca_bundle_path:
            raise ValueError(
                "COZE_OUTBOUND_AUTH_PROXY_CA_PATH environment variable is not configured. "
                "Please set this variable to configure the CA bundle file path."
            )
        kwargs["verify"] = ca_bundle_path
        logger.info(f"Use CA bundle: {ca_bundle_path}")

        # Configure PPE env header if needed
        coze_env_lane = os.environ.get('COZE_ENV_LANE')
        if coze_env_lane and coze_env_lane.startswith('ppe_'):
            headers = kwargs.get("headers", {})
            # Handle headers if it's not a dict (e.g. httpx.Headers)
            if not isinstance(headers, dict):
                headers = dict(headers)
            
            headers['x-tt-env'] = coze_env_lane
            kwargs["headers"] = headers
            logger.info(f"Set x-tt-env header: {coze_env_lane}")


# Default clients will be created on first use
_default_client: Optional[Client] = None
_default_async_client: Optional[AsyncClient] = None
_client_lock = threading.Lock()


def _get_default_client() -> Client:
    """Get or create the default configured client."""
    global _default_client
    if _default_client is None:
        with _client_lock:
            if _default_client is None:
                _default_client = Client()
    return _default_client


def _get_default_async_client() -> AsyncClient:
    """Get or create the default configured async client."""
    global _default_async_client
    if _default_async_client is None:
        with _client_lock:
            if _default_async_client is None:
                _default_async_client = AsyncClient()
    return _default_async_client


# Module-level convenience functions that use the default configured client

def request(
    method: str,
    url: str,
    *,
    params: Optional[Any] = None,
    content: Optional[Any] = None,
    data: Optional[Any] = None,
    files: Optional[Any] = None,
    json: Optional[Any] = None,
    headers: Optional[Any] = None,
    cookies: Optional[Any] = None,
    auth: Optional[Any] = None,
    follow_redirects: bool = False,
    timeout: Any = httpx.USE_CLIENT_DEFAULT,
    extensions: Optional[Any] = None,
) -> httpx.Response:
    """Send an HTTP request with configured proxy and CA settings."""
    return _get_default_client().request(
        method=method,
        url=url,
        params=params,
        content=content,
        data=data,
        files=files,
        json=json,
        headers=headers,
        cookies=cookies,
        auth=auth,
        follow_redirects=follow_redirects,
        timeout=timeout,
        extensions=extensions,
    )


def get(
    url: str,
    *,
    params: Optional[Any] = None,
    headers: Optional[Any] = None,
    cookies: Optional[Any] = None,
    auth: Optional[Any] = None,
    follow_redirects: bool = False,
    timeout: Any = httpx.USE_CLIENT_DEFAULT,
    extensions: Optional[Any] = None,
) -> httpx.Response:
    """Send a GET request with configured proxy and CA settings."""
    return _get_default_client().get(
        url=url,
        params=params,
        headers=headers,
        cookies=cookies,
        auth=auth,
        follow_redirects=follow_redirects,
        timeout=timeout,
        extensions=extensions,
    )


def post(
    url: str,
    *,
    content: Optional[Any] = None,
    data: Optional[Any] = None,
    files: Optional[Any] = None,
    json: Optional[Any] = None,
    params: Optional[Any] = None,
    headers: Optional[Any] = None,
    cookies: Optional[Any] = None,
    auth: Optional[Any] = None,
    follow_redirects: bool = False,
    timeout: Any = httpx.USE_CLIENT_DEFAULT,
    extensions: Optional[Any] = None,
) -> httpx.Response:
    """Send a POST request with configured proxy and CA settings."""
    return _get_default_client().post(
        url=url,
        content=content,
        data=data,
        files=files,
        json=json,
        params=params,
        headers=headers,
        cookies=cookies,
        auth=auth,
        follow_redirects=follow_redirects,
        timeout=timeout,
        extensions=extensions,
    )


def put(
    url: str,
    *,
    content: Optional[Any] = None,
    data: Optional[Any] = None,
    files: Optional[Any] = None,
    json: Optional[Any] = None,
    params: Optional[Any] = None,
    headers: Optional[Any] = None,
    cookies: Optional[Any] = None,
    auth: Optional[Any] = None,
    follow_redirects: bool = False,
    timeout: Any = httpx.USE_CLIENT_DEFAULT,
    extensions: Optional[Any] = None,
) -> httpx.Response:
    """Send a PUT request with configured proxy and CA settings."""
    return _get_default_client().put(
        url=url,
        content=content,
        data=data,
        files=files,
        json=json,
        params=params,
        headers=headers,
        cookies=cookies,
        auth=auth,
        follow_redirects=follow_redirects,
        timeout=timeout,
        extensions=extensions,
    )


def patch(
    url: str,
    *,
    content: Optional[Any] = None,
    data: Optional[Any] = None,
    files: Optional[Any] = None,
    json: Optional[Any] = None,
    params: Optional[Any] = None,
    headers: Optional[Any] = None,
    cookies: Optional[Any] = None,
    auth: Optional[Any] = None,
    follow_redirects: bool = False,
    timeout: Any = httpx.USE_CLIENT_DEFAULT,
    extensions: Optional[Any] = None,
) -> httpx.Response:
    """Send a PATCH request with configured proxy and CA settings."""
    return _get_default_client().patch(
        url=url,
        content=content,
        data=data,
        files=files,
        json=json,
        params=params,
        headers=headers,
        cookies=cookies,
        auth=auth,
        follow_redirects=follow_redirects,
        timeout=timeout,
        extensions=extensions,
    )


def delete(
    url: str,
    *,
    params: Optional[Any] = None,
    headers: Optional[Any] = None,
    cookies: Optional[Any] = None,
    auth: Optional[Any] = None,
    follow_redirects: bool = False,
    timeout: Any = httpx.USE_CLIENT_DEFAULT,
    extensions: Optional[Any] = None,
) -> httpx.Response:
    """Send a DELETE request with configured proxy and CA settings."""
    return _get_default_client().delete(
        url=url,
        params=params,
        headers=headers,
        cookies=cookies,
        auth=auth,
        follow_redirects=follow_redirects,
        timeout=timeout,
        extensions=extensions,
    )


def head(
    url: str,
    *,
    params: Optional[Any] = None,
    headers: Optional[Any] = None,
    cookies: Optional[Any] = None,
    auth: Optional[Any] = None,
    follow_redirects: bool = False,
    timeout: Any = httpx.USE_CLIENT_DEFAULT,
    extensions: Optional[Any] = None,
) -> httpx.Response:
    """Send a HEAD request with configured proxy and CA settings."""
    return _get_default_client().head(
        url=url,
        params=params,
        headers=headers,
        cookies=cookies,
        auth=auth,
        follow_redirects=follow_redirects,
        timeout=timeout,
        extensions=extensions,
    )


def options(
    url: str,
    *,
    params: Optional[Any] = None,
    headers: Optional[Any] = None,
    cookies: Optional[Any] = None,
    auth: Optional[Any] = None,
    follow_redirects: bool = False,
    timeout: Any = httpx.USE_CLIENT_DEFAULT,
    extensions: Optional[Any] = None,
) -> httpx.Response:
    """Send an OPTIONS request with configured proxy and CA settings."""
    return _get_default_client().options(
        url=url,
        params=params,
        headers=headers,
        cookies=cookies,
        auth=auth,
        follow_redirects=follow_redirects,
        timeout=timeout,
        extensions=extensions,
    )


def stream(
    method: str,
    url: str,
    *,
    params: Optional[Any] = None,
    content: Optional[Any] = None,
    data: Optional[Any] = None,
    files: Optional[Any] = None,
    json: Optional[Any] = None,
    headers: Optional[Any] = None,
    cookies: Optional[Any] = None,
    auth: Optional[Any] = None,
    follow_redirects: bool = False,
    timeout: Any = httpx.USE_CLIENT_DEFAULT,
    extensions: Optional[Any] = None,
) -> httpx.Response:
    """Stream an HTTP request with configured proxy and CA settings."""
    return _get_default_client().stream(
        method=method,
        url=url,
        params=params,
        content=content,
        data=data,
        files=files,
        json=json,
        headers=headers,
        cookies=cookies,
        auth=auth,
        follow_redirects=follow_redirects,
        timeout=timeout,
        extensions=extensions,
    )


# Async versions of the module-level functions

async def arequest(
    method: str,
    url: str,
    *,
    params: Optional[Any] = None,
    content: Optional[Any] = None,
    data: Optional[Any] = None,
    files: Optional[Any] = None,
    json: Optional[Any] = None,
    headers: Optional[Any] = None,
    cookies: Optional[Any] = None,
    auth: Optional[Any] = None,
    follow_redirects: bool = False,
    timeout: Any = httpx.USE_CLIENT_DEFAULT,
    extensions: Optional[Any] = None,
) -> httpx.Response:
    """Send an async HTTP request with configured proxy and CA settings."""
    client = _get_default_async_client()
    return await client.request(
        method=method,
        url=url,
        params=params,
        content=content,
        data=data,
        files=files,
        json=json,
        headers=headers,
        cookies=cookies,
        auth=auth,
        follow_redirects=follow_redirects,
        timeout=timeout,
        extensions=extensions,
    )


async def aget(
    url: str,
    *,
    params: Optional[Any] = None,
    headers: Optional[Any] = None,
    cookies: Optional[Any] = None,
    auth: Optional[Any] = None,
    follow_redirects: bool = False,
    timeout: Any = httpx.USE_CLIENT_DEFAULT,
    extensions: Optional[Any] = None,
) -> httpx.Response:
    """Send an async GET request with configured proxy and CA settings."""
    client = _get_default_async_client()
    return await client.get(
        url=url,
        params=params,
        headers=headers,
        cookies=cookies,
        auth=auth,
        follow_redirects=follow_redirects,
        timeout=timeout,
        extensions=extensions,
    )


async def apost(
    url: str,
    *,
    content: Optional[Any] = None,
    data: Optional[Any] = None,
    files: Optional[Any] = None,
    json: Optional[Any] = None,
    params: Optional[Any] = None,
    headers: Optional[Any] = None,
    cookies: Optional[Any] = None,
    auth: Optional[Any] = None,
    follow_redirects: bool = False,
    timeout: Any = httpx.USE_CLIENT_DEFAULT,
    extensions: Optional[Any] = None,
) -> httpx.Response:
    """Send an async POST request with configured proxy and CA settings."""
    client = _get_default_async_client()
    return await client.post(
        url=url,
        content=content,
        data=data,
        files=files,
        json=json,
        params=params,
        headers=headers,
        cookies=cookies,
        auth=auth,
        follow_redirects=follow_redirects,
        timeout=timeout,
        extensions=extensions,
    )


async def aput(
    url: str,
    *,
    content: Optional[Any] = None,
    data: Optional[Any] = None,
    files: Optional[Any] = None,
    json: Optional[Any] = None,
    params: Optional[Any] = None,
    headers: Optional[Any] = None,
    cookies: Optional[Any] = None,
    auth: Optional[Any] = None,
    follow_redirects: bool = False,
    timeout: Any = httpx.USE_CLIENT_DEFAULT,
    extensions: Optional[Any] = None,
) -> httpx.Response:
    """Send an async PUT request with configured proxy and CA settings."""
    client = _get_default_async_client()
    return await client.put(
        url=url,
        content=content,
        data=data,
        files=files,
        json=json,
        params=params,
        headers=headers,
        cookies=cookies,
        auth=auth,
        follow_redirects=follow_redirects,
        timeout=timeout,
        extensions=extensions,
    )


async def apatch(
    url: str,
    *,
    content: Optional[Any] = None,
    data: Optional[Any] = None,
    files: Optional[Any] = None,
    json: Optional[Any] = None,
    params: Optional[Any] = None,
    headers: Optional[Any] = None,
    cookies: Optional[Any] = None,
    auth: Optional[Any] = None,
    follow_redirects: bool = False,
    timeout: Any = httpx.USE_CLIENT_DEFAULT,
    extensions: Optional[Any] = None,
) -> httpx.Response:
    """Send an async PATCH request with configured proxy and CA settings."""
    client = _get_default_async_client()
    return await client.patch(
        url=url,
        content=content,
        data=data,
        files=files,
        json=json,
        params=params,
        headers=headers,
        cookies=cookies,
        auth=auth,
        follow_redirects=follow_redirects,
        timeout=timeout,
        extensions=extensions,
    )


async def adelete(
    url: str,
    *,
    params: Optional[Any] = None,
    headers: Optional[Any] = None,
    cookies: Optional[Any] = None,
    auth: Optional[Any] = None,
    follow_redirects: bool = False,
    timeout: Any = httpx.USE_CLIENT_DEFAULT,
    extensions: Optional[Any] = None,
) -> httpx.Response:
    """Send an async DELETE request with configured proxy and CA settings."""
    client = _get_default_async_client()
    return await client.delete(
        url=url,
        params=params,
        headers=headers,
        cookies=cookies,
        auth=auth,
        follow_redirects=follow_redirects,
        timeout=timeout,
        extensions=extensions,
    )


async def ahead(
    url: str,
    *,
    params: Optional[Any] = None,
    headers: Optional[Any] = None,
    cookies: Optional[Any] = None,
    auth: Optional[Any] = None,
    follow_redirects: bool = False,
    timeout: Any = httpx.USE_CLIENT_DEFAULT,
    extensions: Optional[Any] = None,
) -> httpx.Response:
    """Send an async HEAD request with configured proxy and CA settings."""
    client = _get_default_async_client()
    return await client.head(
        url=url,
        params=params,
        headers=headers,
        cookies=cookies,
        auth=auth,
        follow_redirects=follow_redirects,
        timeout=timeout,
        extensions=extensions,
    )


async def aoptions(
    url: str,
    *,
    params: Optional[Any] = None,
    headers: Optional[Any] = None,
    cookies: Optional[Any] = None,
    auth: Optional[Any] = None,
    follow_redirects: bool = False,
    timeout: Any = httpx.USE_CLIENT_DEFAULT,
    extensions: Optional[Any] = None,
) -> httpx.Response:
    """Send an async OPTIONS request with configured proxy and CA settings."""
    client = _get_default_async_client()
    return await client.options(
        url=url,
        params=params,
        headers=headers,
        cookies=cookies,
        auth=auth,
        follow_redirects=follow_redirects,
        timeout=timeout,
        extensions=extensions,
    )


# Expose common httpx types and exceptions
from httpx import (
    # Types
    Response,
    Request,
    URL,
    Headers,
    Cookies,
    QueryParams,
    Timeout,
    Limits,
    Proxy,
    HTTPTransport,
    AsyncHTTPTransport,
    # Exceptions
    HTTPError,
    HTTPStatusError,
    InvalidURL,
    CookieConflict,
    StreamError,
    StreamConsumed,
    StreamClosed,
    ReadError,
    WriteError,
    ConnectError,
    CloseError,
    NetworkError,
    ProtocolError,
    ProxyError,
    UnsupportedProtocol,
    DecodingError,
    TooManyRedirects,
    # Status codes
    codes,
    # Constants
    USE_CLIENT_DEFAULT,
)

__all__ = [
    # Client classes
    "Client",
    "AsyncClient",
    # Sync functions
    "request",
    "get",
    "post",
    "put",
    "patch",
    "delete",
    "head",
    "options",
    "stream",
    # Async functions
    "arequest",
    "aget",
    "apost",
    "aput",
    "apatch",
    "adelete",
    "ahead",
    "aoptions",
    # Types
    "Response",
    "Request",
    "URL",
    "Headers",
    "Cookies",
    "QueryParams",
    "Timeout",
    "Limits",
    "Proxy",
    "HTTPTransport",
    "AsyncHTTPTransport",
    # Exceptions
    "HTTPError",
    "HTTPStatusError",
    "InvalidURL",
    "CookieConflict",
    "StreamError",
    "StreamConsumed",
    "StreamClosed",
    "ReadError",
    "WriteError",
    "ConnectError",
    "CloseError",
    "NetworkError",
    "ProtocolError",
    "ProxyError",
    "UnsupportedProtocol",
    "DecodingError",
    "TooManyRedirects",
    # Status codes
    "codes",
    # Constants
    "USE_CLIENT_DEFAULT",
]
