# src/chatiss_open_sdk/core/base.py
from __future__ import annotations

import json
from typing import Any, Dict, Iterator, Optional

import httpx
from httpx_sse import connect_sse

from .common import build_default_headers, extract_request_id
from .token import TokenProvider
from ..config import SDKConfig
from ..exceptions import APIError, NetworkError


class BaseClient:
    def __init__(self, config: SDKConfig) -> None:
        config.validate()
        self._config = config

        self._http = httpx.Client(
            base_url=config.base_url.rstrip("/"),
            timeout=config.timeout,
        )
        self._token_provider = TokenProvider(config, self._http)

    def close(self) -> None:
        self._http.close()

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

    def __exit__(self, exc_type, exc, tb) -> None:
        self.close()

    @staticmethod
    def _extract_request_id(resp: httpx.Response) -> Optional[str]:
        return extract_request_id(resp)

    def _build_headers(
        self,
        *,
        headers: Optional[Dict[str, str]],
        auth: bool,
        accept: str,
        content_type: Optional[str] = None,
    ) -> Dict[str, str]:
        final_headers = build_default_headers(headers=headers, accept=accept, content_type=content_type)

        if auth:
            token = self._token_provider.get_token()
            final_headers["Authorization"] = f"Bearer {token}"

        return final_headers

    def _request(
        self,
        method: str,
        path: str,
        *,
        params: Optional[Dict[str, Any]] = None,
        json_body: Any = None,
        headers: Optional[Dict[str, str]] = None,
        auth: bool = True,
    ) -> Dict[str, Any]:
        final_headers = self._build_headers(
            headers=headers,
            auth=auth,
            accept="application/json",
            content_type="application/json",
        )

        try:
            resp = self._http.request(method, path, params=params, json=json_body, headers=final_headers)
        except httpx.RequestError as e:
            raise NetworkError(f"request failed: {e}") from e

        request_id = self._extract_request_id(resp)

        if resp.status_code >= 400:
            raise APIError(resp.status_code, resp.text, request_id=request_id, body=resp.text)

        return resp.json()

    def _stream_sse(
        self,
        method: str,
        path: str,
        *,
        params: Optional[Dict[str, Any]] = None,
        json_body: Any = None,
        headers: Optional[Dict[str, str]] = None,
        auth: bool = True,
    ) -> Iterator[Dict[str, Any]]:
        final_headers = self._build_headers(
            headers=headers,
            auth=auth,
            accept="text/event-stream",
            content_type="application/json",
        )

        try:
            with connect_sse(
                self._http,
                method,
                path,
                params=params,
                json=json_body,
                headers=final_headers,
            ) as event_source:
                for sse in event_source.iter_sse():
                    if sse.data == "[DONE]":
                        break

                    try:
                        payload = json.loads(sse.data)
                    except json.JSONDecodeError:
                        payload = sse.data

                    yield {"event": sse.event or "message", "data": payload}
        except httpx.RequestError as e:
            raise NetworkError(f"request failed: {e}") from e
