# Copyright 2026 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# pyformat: disable

"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""

from . import errors, types, utils
from ._hooks import (
    AfterErrorContext,
    AfterSuccessContext,
    BeforeRequestContext,
    HookContext,
)
from .sdkconfiguration import SDKConfiguration
from .utils import RetryConfig, SerializedRequestBody, get_body_content
import httpx
from typing import Any, Callable, List, Mapping, Optional, Tuple
from urllib.parse import parse_qs, urlparse


class BaseSDK:
    sdk_configuration: SDKConfiguration
    parent_ref: Optional[object] = None
    """
    Reference to the root SDK instance, if any. This will prevent it from
    being garbage collected while there are active streams.
    """

    def __init__(
        self,
        sdk_config: SDKConfiguration,
        parent_ref: Optional[object] = None,
    ) -> None:
        self.sdk_configuration = sdk_config
        self.parent_ref = parent_ref

    def _get_url(self, base_url, url_variables):
        sdk_url, sdk_variables = self.sdk_configuration.get_server_details()

        if base_url is None:
            base_url = sdk_url

        if url_variables is None:
            url_variables = sdk_variables

        return utils.template_url(base_url, url_variables)

    def _coerce_timeout_ms(self, timeout: Optional[Any]) -> Optional[int]:
        if timeout is None:
            return None
        if isinstance(timeout, (int, float)):
            return int(timeout * 1000)
        if isinstance(timeout, httpx.Timeout):
            values = [timeout.connect, timeout.read, timeout.write, timeout.pool]
            finite_values = [value for value in values if value is not None]
            if not finite_values:
                return None
            return int(max(finite_values) * 1000)
        raise TypeError("timeout must be a float, int, httpx.Timeout, or None")

    def _build_request(
        self,
        method,
        path,
        base_url,
        url_variables,
        request,
        request_body_required,
        request_has_path_params,
        request_has_query_params,
        user_agent_header,
        accept_header_value,
        _globals=None,
        security=None,
        timeout_ms: Optional[int] = None,
        get_serialized_body: Optional[
            Callable[[], Optional[SerializedRequestBody]]
        ] = None,
        url_override: Optional[str] = None,
        http_headers: Optional[Mapping[str, str]] = None,
        extra_query_params: Optional[Mapping[str, Any]] = None,
        allow_empty_value: Optional[List[str]] = None,
        allowed_fields: Optional[List[str]] = None,
    ) -> httpx.Request:
        client = self.sdk_configuration.client
        return self._build_request_with_client(
            client,
            method,
            path,
            base_url,
            url_variables,
            request,
            request_body_required,
            request_has_path_params,
            request_has_query_params,
            user_agent_header,
            accept_header_value,
            _globals,
            security,
            timeout_ms,
            get_serialized_body,
            url_override,
            http_headers,
            extra_query_params,
            allow_empty_value,
            allowed_fields,
        )

    def _build_request_with_client(
        self,
        client,
        method,
        path,
        base_url,
        url_variables,
        request,
        request_body_required,
        request_has_path_params,
        request_has_query_params,
        user_agent_header,
        accept_header_value,
        _globals=None,
        security=None,
        timeout_ms: Optional[int] = None,
        get_serialized_body: Optional[
            Callable[[], Optional[SerializedRequestBody]]
        ] = None,
        url_override: Optional[str] = None,
        http_headers: Optional[Mapping[str, str]] = None,
        extra_query_params: Optional[Mapping[str, Any]] = None,
        allow_empty_value: Optional[List[str]] = None,
        allowed_fields: Optional[List[str]] = None,
    ) -> httpx.Request:
        query_params = {}

        url = url_override
        if url is None:
            url = utils.generate_url(
                self._get_url(base_url, url_variables),
                path,
                request if request_has_path_params else None,
                _globals if request_has_path_params else None,
            )

            query_params = utils.get_query_params(
                request if request_has_query_params else None,
                _globals if request_has_query_params else None,
                allow_empty_value,
            )
        else:
            # Pick up the query parameter from the override so they can be
            # preserved when building the request later on (necessary as of
            # httpx 0.28).
            parsed_override = urlparse(str(url_override))
            query_params = parse_qs(parsed_override.query, keep_blank_values=True)

        headers = utils.get_headers(request, _globals)
        headers["Accept"] = accept_header_value
        headers[user_agent_header] = self.sdk_configuration.user_agent

        if security is not None:
            if callable(security):
                security = security()
        security = utils.get_security_from_env(security, types.Security)
        if security is not None:
            security_headers, security_query_params = utils.get_security(
                security, allowed_fields
            )
            headers = {**headers, **security_headers}
            query_params = {**query_params, **security_query_params}
        if extra_query_params is not None:
            for name, value in extra_query_params.items():
                if value is None:
                    continue
                if isinstance(value, (list, tuple)):
                    query_params[name] = [str(v) for v in value]
                else:
                    query_params[name] = [str(value)]

        serialized_request_body = SerializedRequestBody()
        if get_serialized_body is not None:
            rb = get_serialized_body()
            if request_body_required and rb is None:
                raise ValueError("request body is required")

            if rb is not None:
                serialized_request_body = rb

        if (
            serialized_request_body.media_type is not None
            and serialized_request_body.media_type
            not in (
                "multipart/form-data",
                "multipart/mixed",
            )
        ):
            headers["content-type"] = serialized_request_body.media_type

        if http_headers is not None:
            for header, value in http_headers.items():
                headers[header] = value

        timeout = timeout_ms / 1000 if timeout_ms is not None else None

        return client.build_request(
            method,
            url,
            params=query_params,
            content=serialized_request_body.content,
            data=serialized_request_body.data,
            files=serialized_request_body.files,
            headers=headers,
            timeout=timeout if timeout is not None else httpx.USE_CLIENT_DEFAULT,
        )

    def do_request(
        self,
        hook_ctx: HookContext,
        request: httpx.Request,
        is_error_status_code: Callable[[int], bool],
        stream: bool = False,
        retry_config: Optional[Tuple[RetryConfig, List[str]]] = None,
    ) -> httpx.Response:
        client = self.sdk_configuration.client
        logger = self.sdk_configuration.debug_logger

        hooks = self.sdk_configuration.__dict__["_hooks"]

        def do(_attempt: int = 0):
            http_res = None
            try:
                req = hooks.before_request(BeforeRequestContext(hook_ctx), request)
                if "timeout" in request.extensions and "timeout" not in req.extensions:
                    req.extensions["timeout"] = request.extensions["timeout"]
                logger.debug(
                    "Request:\nMethod: %s\nURL: %s\nHeaders: %s\nBody: %s",
                    req.method,
                    req.url,
                    req.headers,
                    get_body_content(req),
                )

                if client is None:
                    raise ValueError("client is required")

                http_res = client.send(req, stream=stream)
            except Exception as e:
                _, e = hooks.after_error(AfterErrorContext(hook_ctx), None, e)
                if e is not None:
                    logger.debug("Request Exception", exc_info=True)
                    raise e

            if http_res is None:
                logger.debug("Raising no response SDK error")
                raise errors.NoResponseError("No response received")

            logger.debug(
                "Response:\nStatus Code: %s\nURL: %s\nHeaders: %s\nBody: %s",
                http_res.status_code,
                http_res.url,
                http_res.headers,
                "<streaming response>" if stream else http_res.text,
            )

            return http_res

        if retry_config is not None:
            http_res = utils.retry(do, utils.Retries(retry_config[0], retry_config[1]))
        else:
            http_res = do()

        if is_error_status_code(http_res.status_code):
            result, err = hooks.after_error(AfterErrorContext(hook_ctx), http_res, None)
            if err is not None:
                logger.debug("Request Exception", exc_info=True)
                raise err
            if result is not None:
                http_res = result
            else:
                logger.debug("Raising unexpected SDK error")
                raise errors.GenAiDefaultError("Unexpected error occurred", http_res)
        else:
            http_res = hooks.after_success(AfterSuccessContext(hook_ctx), http_res)

        return http_res


class AsyncBaseSDK:
    sdk_configuration: SDKConfiguration
    parent_ref: Optional[object] = None
    """
    Reference to the root SDK instance, if any. This will prevent it from
    being garbage collected while there are active streams.
    """

    def __init__(
        self,
        sdk_config: SDKConfiguration,
        parent_ref: Optional[object] = None,
    ) -> None:
        self.sdk_configuration = sdk_config
        self.parent_ref = parent_ref

    def _get_url(self, base_url, url_variables):
        sdk_url, sdk_variables = self.sdk_configuration.get_server_details()

        if base_url is None:
            base_url = sdk_url

        if url_variables is None:
            url_variables = sdk_variables

        return utils.template_url(base_url, url_variables)

    def _coerce_timeout_ms(self, timeout: Optional[Any]) -> Optional[int]:
        if timeout is None:
            return None
        if isinstance(timeout, (int, float)):
            return int(timeout * 1000)
        if isinstance(timeout, httpx.Timeout):
            values = [timeout.connect, timeout.read, timeout.write, timeout.pool]
            finite_values = [value for value in values if value is not None]
            if not finite_values:
                return None
            return int(max(finite_values) * 1000)
        raise TypeError("timeout must be a float, int, httpx.Timeout, or None")

    def _build_request_async(
        self,
        method,
        path,
        base_url,
        url_variables,
        request,
        request_body_required,
        request_has_path_params,
        request_has_query_params,
        user_agent_header,
        accept_header_value,
        _globals=None,
        security=None,
        timeout_ms: Optional[int] = None,
        get_serialized_body: Optional[
            Callable[[], Optional[SerializedRequestBody]]
        ] = None,
        url_override: Optional[str] = None,
        http_headers: Optional[Mapping[str, str]] = None,
        extra_query_params: Optional[Mapping[str, Any]] = None,
        allow_empty_value: Optional[List[str]] = None,
        allowed_fields: Optional[List[str]] = None,
    ) -> httpx.Request:
        client = self.sdk_configuration.async_client
        return self._build_request_with_client(
            client,
            method,
            path,
            base_url,
            url_variables,
            request,
            request_body_required,
            request_has_path_params,
            request_has_query_params,
            user_agent_header,
            accept_header_value,
            _globals,
            security,
            timeout_ms,
            get_serialized_body,
            url_override,
            http_headers,
            extra_query_params,
            allow_empty_value,
            allowed_fields,
        )

    def _build_request_with_client(
        self,
        client,
        method,
        path,
        base_url,
        url_variables,
        request,
        request_body_required,
        request_has_path_params,
        request_has_query_params,
        user_agent_header,
        accept_header_value,
        _globals=None,
        security=None,
        timeout_ms: Optional[int] = None,
        get_serialized_body: Optional[
            Callable[[], Optional[SerializedRequestBody]]
        ] = None,
        url_override: Optional[str] = None,
        http_headers: Optional[Mapping[str, str]] = None,
        extra_query_params: Optional[Mapping[str, Any]] = None,
        allow_empty_value: Optional[List[str]] = None,
        allowed_fields: Optional[List[str]] = None,
    ) -> httpx.Request:
        query_params = {}

        url = url_override
        if url is None:
            url = utils.generate_url(
                self._get_url(base_url, url_variables),
                path,
                request if request_has_path_params else None,
                _globals if request_has_path_params else None,
            )

            query_params = utils.get_query_params(
                request if request_has_query_params else None,
                _globals if request_has_query_params else None,
                allow_empty_value,
            )
        else:
            # Pick up the query parameter from the override so they can be
            # preserved when building the request later on (necessary as of
            # httpx 0.28).
            parsed_override = urlparse(str(url_override))
            query_params = parse_qs(parsed_override.query, keep_blank_values=True)

        headers = utils.get_headers(request, _globals)
        headers["Accept"] = accept_header_value
        headers[user_agent_header] = self.sdk_configuration.user_agent

        if security is not None:
            if callable(security):
                security = security()
        security = utils.get_security_from_env(security, types.Security)
        if security is not None:
            security_headers, security_query_params = utils.get_security(
                security, allowed_fields
            )
            headers = {**headers, **security_headers}
            query_params = {**query_params, **security_query_params}
        if extra_query_params is not None:
            for name, value in extra_query_params.items():
                if value is None:
                    continue
                if isinstance(value, (list, tuple)):
                    query_params[name] = [str(v) for v in value]
                else:
                    query_params[name] = [str(value)]

        serialized_request_body = SerializedRequestBody()
        if get_serialized_body is not None:
            rb = get_serialized_body()
            if request_body_required and rb is None:
                raise ValueError("request body is required")

            if rb is not None:
                serialized_request_body = rb

        if (
            serialized_request_body.media_type is not None
            and serialized_request_body.media_type
            not in (
                "multipart/form-data",
                "multipart/mixed",
            )
        ):
            headers["content-type"] = serialized_request_body.media_type

        if http_headers is not None:
            for header, value in http_headers.items():
                headers[header] = value

        timeout = timeout_ms / 1000 if timeout_ms is not None else None

        return client.build_request(
            method,
            url,
            params=query_params,
            content=serialized_request_body.content,
            data=serialized_request_body.data,
            files=serialized_request_body.files,
            headers=headers,
            timeout=timeout if timeout is not None else httpx.USE_CLIENT_DEFAULT,
        )

    async def do_request_async(
        self,
        hook_ctx: HookContext,
        request: httpx.Request,
        is_error_status_code: Callable[[int], bool],
        stream: bool = False,
        retry_config: Optional[Tuple[RetryConfig, List[str]]] = None,
    ) -> httpx.Response:
        client = self.sdk_configuration.async_client
        logger = self.sdk_configuration.debug_logger

        async_hooks = self.sdk_configuration.__dict__["_async_hooks"]

        async def do(_attempt: int = 0):
            http_res = None
            try:
                req = await async_hooks.before_request(
                    BeforeRequestContext(hook_ctx), request
                )

                if "timeout" in request.extensions and "timeout" not in req.extensions:
                    req.extensions["timeout"] = request.extensions["timeout"]
                logger.debug(
                    "Request:\nMethod: %s\nURL: %s\nHeaders: %s\nBody: %s",
                    req.method,
                    req.url,
                    req.headers,
                    get_body_content(req),
                )

                if client is None:
                    raise ValueError("client is required")

                http_res = await client.send(req, stream=stream)
            except Exception as e:
                _, e = await async_hooks.after_error(
                    AfterErrorContext(hook_ctx), None, e
                )

                if e is not None:
                    logger.debug("Request Exception", exc_info=True)
                    raise e

            if http_res is None:
                logger.debug("Raising no response SDK error")
                raise errors.NoResponseError("No response received")

            logger.debug(
                "Response:\nStatus Code: %s\nURL: %s\nHeaders: %s\nBody: %s",
                http_res.status_code,
                http_res.url,
                http_res.headers,
                "<streaming response>" if stream else http_res.text,
            )

            return http_res

        if retry_config is not None:
            http_res = await utils.retry_async(
                do, utils.Retries(retry_config[0], retry_config[1])
            )
        else:
            http_res = await do()

        if is_error_status_code(http_res.status_code):
            result, err = await async_hooks.after_error(
                AfterErrorContext(hook_ctx), http_res, None
            )

            if err is not None:
                logger.debug("Request Exception", exc_info=True)
                raise err
            if result is not None:
                http_res = result
            else:
                logger.debug("Raising unexpected SDK error")
                raise errors.GenAiDefaultError("Unexpected error occurred", http_res)
        else:
            http_res = await async_hooks.after_success(
                AfterSuccessContext(hook_ctx), http_res
            )

        return http_res
