"""SAGAX API client with HMAC-SHA256 signature authentication."""

import hashlib
import hmac as hmac_mod
import json
import time
import urllib.parse

import requests

from sagax.exceptions import APIError, AuthError, NotFoundError, RateLimitError, SagaxError


class SagaxClient:
    """Client for the SAGAX Quantitative Trading Platform API.

    Args:
        api_key: Your SAGAX API key (starts with ``sagax_ak_``).
        secret_key: Your SAGAX secret key (starts with ``sagax_sk_``).
        base_url: API base URL. Defaults to ``https://api.sagax.cn``.
        timeout: Request timeout in seconds. Defaults to 30.
        max_retries: Max retries on 429 rate-limit responses. Defaults to 3.

    Usage::

        from sagax import SagaxClient
        client = SagaxClient(api_key="...", secret_key="...")
        overview = client.market.overview()
    """

    # Default base URLs per market
    DEFAULT_URLS = {
        "astock": "https://api.sagax.cn",      # port 5000
        "futures": "https://api.sagax.cn:5001", # port 5001
    }

    def __init__(
        self,
        api_key: str,
        secret_key: str,
        base_url: str = "https://api.sagax.cn",
        futures_url: str = None,
        timeout: int = 30,
        max_retries: int = 3,
    ):
        self.api_key = api_key
        self.secret_key = secret_key
        self.base_url = base_url.rstrip("/")
        self.futures_url = (futures_url or "").rstrip("/") or None
        self.timeout = timeout
        self.max_retries = max_retries
        self._session = requests.Session()

        # A-stock API modules
        from sagax.agent import AgentAPI
        from sagax.ai import AIAPI
        from sagax.backtest import BacktestAPI
        from sagax.chat import ChatAPI
        from sagax.factorlab import FactorLabAPI
        from sagax.factors import FactorsAPI
        from sagax.market import MarketAPI
        from sagax.news import NewsAPI
        from sagax.stock import StockAPI

        self.market = MarketAPI(self)
        self.stock = StockAPI(self)
        self.factors = FactorsAPI(self)
        self.ai = AIAPI(self)
        self.backtest = BacktestAPI(self)
        self.agent = AgentAPI(self)
        self.factorlab = FactorLabAPI(self)
        self.chat = ChatAPI(self)
        self.news = NewsAPI(self)

        # Schema discovery
        from sagax.schema import SchemaAPI

        self.schema = SchemaAPI(self)

        # Futures API module
        from sagax.futures import FuturesAPI

        self.futures = FuturesAPI(self)

    # ------------------------------------------------------------------
    # HMAC-SHA256 signing
    # ------------------------------------------------------------------

    def _sign(self, params: dict = None, data: str = "") -> dict:
        """Build authentication headers with HMAC-SHA256 signature.

        Signature algorithm::

            payload  = timestamp_ms + query_string + body
            signature = HMAC-SHA256(payload, secret_key)

        Returns:
            dict of HTTP headers (X-API-KEY, X-TIMESTAMP, X-SIGNATURE).
        """
        timestamp_ms = str(int(time.time() * 1000))

        # Build query string in the same form the server reads it
        query_string = urllib.parse.urlencode(params, doseq=True) if params else ""
        body = data if data else ""

        payload = timestamp_ms + query_string + body
        signature = hmac_mod.new(
            self.secret_key.encode("utf-8"),
            payload.encode("utf-8"),
            hashlib.sha256,
        ).hexdigest()

        return {
            "X-API-KEY": self.api_key,
            "X-TIMESTAMP": timestamp_ms,
            "X-SIGNATURE": signature,
        }

    # ------------------------------------------------------------------
    # Core HTTP request
    # ------------------------------------------------------------------

    def _request(
        self,
        method: str,
        path: str,
        params: dict = None,
        data: dict = None,
        timeout: int = None,
        stream: bool = False,
        _base_url: str = None,
    ) -> dict:
        """Send a signed request to the SAGAX API.

        Args:
            method: HTTP method (GET, POST, PUT, DELETE).
            path: API path (e.g. ``/api/market/overview``).
            params: Query parameters dict.
            data: JSON body dict (for POST/PUT).
            timeout: Override default timeout for this request.
            stream: If True, return the raw ``requests.Response`` (for SSE).

        Returns:
            Parsed JSON response dict, or raw Response if *stream=True*.

        Raises:
            AuthError: 401/403 responses.
            RateLimitError: 429 responses (after exhausting retries).
            NotFoundError: 404 responses.
            APIError: Other non-2xx responses.
        """
        # Strip None values from params
        if params:
            params = {k: v for k, v in params.items() if v is not None}

        body_str = json.dumps(data, ensure_ascii=False) if data else ""
        url = (_base_url or self.base_url) + path
        effective_timeout = timeout or self.timeout

        for attempt in range(self.max_retries + 1):
            headers = self._sign(params=params, data=body_str)
            if data:
                headers["Content-Type"] = "application/json"

            resp = self._session.request(
                method=method,
                url=url,
                params=params,
                data=body_str.encode("utf-8") if body_str else None,
                headers=headers,
                timeout=effective_timeout,
                stream=stream,
            )

            if stream:
                if resp.status_code == 200:
                    return resp
                # For non-200 on stream, fall through to error handling

            if resp.status_code == 429:
                retry_after = int(resp.headers.get("Retry-After", "5"))
                if attempt < self.max_retries:
                    time.sleep(retry_after)
                    continue
                raise RateLimitError(
                    "Rate limit exceeded",
                    retry_after=retry_after,
                    code="RATE_LIMIT",
                    status_code=429,
                    response=resp,
                )

            break

        return self._handle_response(resp)

    # ------------------------------------------------------------------
    # Response handling
    # ------------------------------------------------------------------

    @staticmethod
    def _handle_response(resp: requests.Response) -> dict:
        """Parse response JSON and raise appropriate exceptions."""
        if resp.status_code in (401, 403):
            try:
                body = resp.json()
            except ValueError:
                body = {}
            raise AuthError(
                body.get("error", "Authentication failed"),
                code=body.get("code"),
                status_code=resp.status_code,
                response=resp,
            )

        if resp.status_code == 404:
            try:
                body = resp.json()
            except ValueError:
                body = {}
            raise NotFoundError(
                body.get("error", "Resource not found"),
                code=body.get("code"),
                status_code=404,
                response=resp,
            )

        if resp.status_code >= 400:
            try:
                body = resp.json()
            except ValueError:
                body = {}
            raise APIError(
                body.get("error", f"API error ({resp.status_code})"),
                code=body.get("code"),
                status_code=resp.status_code,
                response=resp,
            )

        try:
            return resp.json()
        except ValueError:
            raise SagaxError(
                "Invalid JSON response from server",
                status_code=resp.status_code,
                response=resp,
            )
