import json
from typing import Any, Optional

import httpx

from .exceptions import (
    AnyFastError,
    AuthenticationError,
    BadRequestError,
    InternalServerError,
    APIError,
)


class BaseClient:
    """Low-level HTTP client for AnyFast API."""

    def __init__(
        self,
        api_key: str,
        base_url: str = "https://www.anyfast.ai",
        gateway_url: str = "https://www.anyfast.ai",
        timeout: float = 30.0,
    ):
        self.api_key = api_key
        self.base_url = base_url.rstrip("/")
        self.gateway_url = gateway_url.rstrip("/")
        self.timeout = timeout

    def _handle_error(self, status_code: int, data: dict):
        message = data.get("message") or data.get("error", {}).get("message") or f"Request failed with status {status_code}"
        code = data.get("code") or data.get("error", {}).get("code")

        if status_code == 401:
            raise AuthenticationError(message, status=status_code, code=code, data=data)
        elif status_code == 400:
            raise BadRequestError(message, status=status_code, code=code, data=data)
        elif status_code >= 500:
            raise InternalServerError(message, status=status_code, code=code, data=data)
        else:
            raise APIError(message, status=status_code, code=code, data=data)

    def request(self, method: str, endpoint: str, body: dict = None, **kwargs) -> dict:
        url = f"{self.base_url}{endpoint}"
        headers = {
            "Content-Type": "application/json",
            "Authorization": f"Bearer {self.api_key}",
        }

        with httpx.Client(timeout=self.timeout) as client:
            response = client.request(
                method=method,
                url=url,
                headers=headers,
                content=json.dumps(body) if body else None,
                **kwargs,
            )

        data = response.json()
        if response.status_code >= 400:
            self._handle_error(response.status_code, data)
        return data

    def streaming_request(self, method: str, endpoint: str, body: dict = None):
        """Make a streaming request, returns a context manager that yields the response.

        Usage:
            with client.streaming_request("POST", "/endpoint", body) as response:
                for chunk in response.iter_text():
                    ...
        """
        url = f"{self.base_url}{endpoint}"
        headers = {
            "Content-Type": "application/json",
            "Authorization": f"Bearer {self.api_key}",
            "Accept": "text/event-stream",
        }

        return _StreamingContext(
            method=method,
            url=url,
            headers=headers,
            content=json.dumps(body) if body else None,
            timeout=self.timeout,
        )

    def gateway_request(self, method: str, endpoint: str, body: dict = None) -> dict:
        url = f"{self.gateway_url}{endpoint}"
        headers = {
            "Content-Type": "application/json",
            "API-KEY": self.api_key,
        }

        with httpx.Client(timeout=self.timeout) as client:
            response = client.request(
                method=method,
                url=url,
                headers=headers,
                content=json.dumps(body) if body else None,
            )

        data = response.json()
        if response.status_code >= 400:
            self._handle_error(response.status_code, data)
        return data

    def post(self, endpoint: str, body: dict) -> dict:
        return self.request("POST", endpoint, body)

    def get(self, endpoint: str) -> dict:
        return self.request("GET", endpoint)


class _StreamingContext:
    """Context manager that properly cleans up both httpx.Client and streaming response."""

    def __init__(self, method: str, url: str, headers: dict, content: str = None, timeout: float = 30.0):
        self._method = method
        self._url = url
        self._headers = headers
        self._content = content
        self._timeout = timeout
        self._client = None
        self._response_cm = None
        self._response = None

    def __enter__(self):
        self._client = httpx.Client(timeout=self._timeout)
        self._response_cm = self._client.stream(
            method=self._method,
            url=self._url,
            headers=self._headers,
            content=self._content,
        )
        self._response = self._response_cm.__enter__()
        return self._response

    def __exit__(self, exc_type, exc_val, exc_tb):
        try:
            if self._response_cm:
                self._response_cm.__exit__(exc_type, exc_val, exc_tb)
        finally:
            if self._client:
                self._client.close()
