"""Base class for GL Deep Research API modules.

This module provides the BaseAPI class with common functionality shared
across all API modules (Health, Tasks, Taskgroups).

Authors:
    Sahat Nicholas Simangunsong (sahat.n.simangunsong@gdplabs.id)

References:
    https://gdplabs.gitbook.io/gl-deepresearch/api-contract
"""

import json
import logging
from collections.abc import Generator
from typing import TYPE_CHECKING, Any

import httpx

from gl_odr.models import StreamEvent

if TYPE_CHECKING:
    from gl_odr.client import DeepResearchClient

logger = logging.getLogger(__name__)


class BaseAPI:
    """Base class for API modules with common functionality."""

    def __init__(self, client: "DeepResearchClient"):
        """Initialize API module.

        Args:
            client: DeepResearchClient instance
        """
        self._client = client

    def _prepare_headers(
        self, extra_headers: dict[str, str] | None = None
    ) -> dict[str, str]:
        """Prepare headers for the API request.

        Args:
            extra_headers: Additional headers to merge with default headers

        Returns:
            Dictionary containing the request headers
        """
        headers = self._client.default_headers.copy()

        if self._client.api_key:
            headers["X-API-Key"] = self._client.api_key

        if extra_headers:
            headers.update(extra_headers)

        return headers

    def _make_request(
        self,
        method: str,
        url: str,
        headers: dict[str, str],
        data: dict[str, Any] | None = None,
        params: dict[str, Any] | None = None,
        json: dict[str, Any] | None = None,
    ) -> dict[str, Any]:
        """Make an HTTP request to the API.

        Args:
            method: HTTP method (GET, POST, DELETE, PUT)
            url: Request URL
            headers: Request headers
            data: Form data
            params: Query parameters
            json: JSON body (for API/MCP config endpoints)

        Returns:
            Response JSON data

        Raises:
            httpx.HTTPStatusError: If the request fails
        """
        timeout = httpx.Timeout(self._client.timeout)

        logger.debug("Request: %s %s", method, url)
        logger.debug("Headers: %s", headers)
        if data:
            logger.debug("Data: %s", data)

        with httpx.Client(timeout=timeout) as client:
            response = client.request(
                method=method,
                url=url,
                headers=headers,
                data=data,
                params=params,
                json=json,
            )
            response.raise_for_status()

            if response.status_code == 204 or not response.content:
                return {}

            try:
                resp_data = response.json()
            except Exception:
                return {}

            if not isinstance(resp_data, dict):
                raise TypeError(f"Unexpected response type: {type(resp_data)}")
            return resp_data

    def _make_request_list(
        self,
        method: str,
        url: str,
        headers: dict[str, str],
        params: dict[str, Any] | None = None,
    ) -> list[dict[str, Any]]:
        """Make an HTTP request that returns a JSON array.

        Args:
            method: HTTP method
            url: Request URL
            headers: Request headers
            params: Query parameters

        Returns:
            List of response JSON objects

        Raises:
            httpx.HTTPStatusError: If the request fails
        """
        timeout = httpx.Timeout(self._client.timeout)

        logger.debug("Request: %s %s", method, url)

        with httpx.Client(timeout=timeout) as client:
            response = client.request(
                method=method,
                url=url,
                headers=headers,
                params=params,
            )
            response.raise_for_status()

            if response.status_code == 204 or not response.content:
                return []

            try:
                resp_data = response.json()
            except Exception:  # pragma: no cover
                return []

            if not isinstance(resp_data, list):
                raise TypeError(f"Unexpected response type: {type(resp_data)}")
            return resp_data

    def _stream_sse(
        self,
        method: str,
        url: str,
        headers: dict[str, str],
        data: dict[str, Any] | None = None,
    ) -> Generator[StreamEvent, None, None]:
        """Stream Server-Sent Events from the API.

        Args:
            method: HTTP method (GET or POST)
            url: Request URL
            headers: Request headers
            data: Form data (for POST requests)

        Yields:
            StreamEvent objects parsed from SSE data

        Raises:
            httpx.HTTPStatusError: If the request fails
        """
        timeout = httpx.Timeout(self._client.timeout)

        # Add SSE-specific headers
        sse_headers = headers.copy()
        sse_headers["Accept"] = "text/event-stream"
        sse_headers["Cache-Control"] = "no-cache"

        with httpx.Client(timeout=timeout) as client:
            with client.stream(
                method,
                url,
                headers=sse_headers,
                data=data,
            ) as response:
                response.raise_for_status()

                # Buffer for incomplete lines
                buffer = ""

                for chunk in response.iter_text():
                    buffer += chunk

                    # Process complete lines from buffer
                    while "\n" in buffer:
                        line, buffer = buffer.split("\n", 1)
                        line = line.strip()

                        if not line:
                            continue

                        if line.startswith("data:"):
                            # Handle both "data: " and "data:" formats
                            data_str = line[5:].strip()
                            if not data_str:
                                continue

                            try:
                                event_data = json.loads(data_str)
                                # Backend may send "event_type" or "type"
                                event_type = event_data.get("event_type") or event_data.get("type", "")
                                yield StreamEvent(
                                    event_type=event_type,
                                    value=event_data.get("value", ""),
                                )
                            except json.JSONDecodeError:
                                logger.warning("Failed to parse SSE data: %s", data_str)
                                continue

                # Process any remaining data in buffer
                if buffer.strip():
                    line = buffer.strip()
                    if line.startswith("data:"):
                        data_str = line[5:].strip()
                        if data_str:
                            try:
                                event_data = json.loads(data_str)
                                event_type = event_data.get("event_type") or event_data.get("type", "")
                                yield StreamEvent(
                                    event_type=event_type,
                                    value=event_data.get("value", ""),
                                )
                            except json.JSONDecodeError:
                                logger.warning("Failed to parse SSE data: %s", data_str)
