"""Shared helpers for sync/async SDKRouter clients."""

from __future__ import annotations

import logging
from typing import TYPE_CHECKING, Any, Generic, TypeVar, overload

from openai.types.chat import ChatCompletion

from .._types.parsed import ParsedChatCompletion, ParsedChoice

if TYPE_CHECKING:
    from .._config import SDKConfig

logger = logging.getLogger(__name__)

T = TypeVar("T")


class LazyTool(Generic[T]):
    """Descriptor for lazy-loaded tool resources.

    Eliminates boilerplate: instead of 17 identical @property blocks,
    each tool is declared once as a class variable.

    Usage:
        class SDKRouter:
            vision = LazyTool("_vision", VisionResource, "Vision analysis and OCR")
            cdn = LazyTool("_cdn", CDNResource, "CDN file storage")
    """

    def __init__(self, attr: str, factory: type[T], doc: str = "") -> None:
        self._attr = attr
        self._factory = factory
        self.__doc__ = doc

    @overload
    def __get__(self, obj: None, objtype: type) -> LazyTool[T]: ...
    @overload
    def __get__(self, obj: Any, objtype: type) -> T: ...

    def __get__(self, obj: Any, objtype: type = None) -> Any:
        if obj is None:
            return self
        instance = getattr(obj, self._attr, None)
        if instance is None:
            instance = self._factory(obj._sdk_config)
            setattr(obj, self._attr, instance)
        return instance


def parse_completion(
    response: ChatCompletion,
    response_format: type,
) -> ParsedChatCompletion:
    """Parse a ChatCompletion into a structured ParsedChatCompletion."""
    from ..utils.json_extractor import JSONExtractor

    parsed_choices = []
    for choice in response.choices:
        content = choice.message.content or ""
        parsed_obj = None
        if content:
            _, parsed_obj, _ = JSONExtractor.extract(content, response_format)
        parsed_choices.append(
            ParsedChoice.from_choice(choice, parsed_content=parsed_obj)
        )

    return ParsedChatCompletion.from_completion(response, parsed_choices)


def merge_provider_extra_body(
    provider: str | None,
    kwargs: dict,
) -> None:
    """Inject provider into kwargs['extra_body'] (mutates in-place).

    Per-request provider in extra_body takes precedence over default.
    """
    if not provider:
        return
    extra_body = kwargs.get("extra_body", {}) or {}
    if "provider" not in extra_body:
        extra_body["provider"] = provider
        kwargs["extra_body"] = extra_body
