# coding: utf-8

"""
    Rapidata.Dataset

    No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)

    The version of the OpenAPI document: v1
    Generated by OpenAPI Generator (https://openapi-generator.tech)

    Do not edit the class manually.
"""  # noqa: E501

import io
import json
import re
from typing import Dict, Optional

import httpx
from authlib.integrations.httpx_client import OAuth2Client
from httpx import Timeout

from rapidata.api_client.exceptions import ApiException, ApiValueError


class RESTResponse(io.IOBase):

    def __init__(self, resp: httpx.Response) -> None:
        self.response = resp
        self.status = resp.status_code
        self.reason = resp.reason_phrase
        self.data = None

    def read(self):
        if self.data is None:
            self.data = self.response.content
        return self.data

    def getheaders(self) -> Dict[str, str]:
        """Returns a dictionary of the response headers."""
        return dict(self.response.headers)

    def getheader(self, name, default=None) -> Optional[str]:
        """Returns a given response header."""
        return self.response.headers.get(name, default)


RESTResponseType = RESTResponse


class RESTClientObject:

    def __init__(self, configuration) -> None:
        self.configuration = configuration

        self.session: Optional[OAuth2Client] = None

    def setup_oauth_client_credentials(
        self, client_id: str, client_secret: str, token_endpoint: str, scope: str
    ):
        client_args = self._get_session_defaults()
        self.session = OAuth2Client(
            client_id=client_id,
            client_secret=client_secret,
            token_endpoint=token_endpoint,
            scope=scope,
            **client_args,
        )

        self.session.fetch_token()

    def setup_oauth_with_token(
        self,
        client_id: str | None,
        client_secret: str | None,
        token: dict,
        token_endpoint: str,
        leeway: int = 60,
    ):
        client_args = self._get_session_defaults()
        self.session = OAuth2Client(
            token=token,
            token_endpoint=token_endpoint,
            client_id=client_id,
            client_secret=client_secret,
            leeway=leeway,
            **client_args,
        )

    def request(
        self,
        method,
        url,
        headers=None,
        body=None,
        post_params=None,
        _request_timeout=None,
    ):
        """Perform requests.

        :param method: http request method
        :param url: http request url
        :param headers: http request headers
        :param body: request json body, for `application/json`
        :param post_params: request post parameters,
                            `application/x-www-form-urlencoded`
                            and `multipart/form-data`
        :param _request_timeout: timeout setting for this request. If one
                                 number provided, it will be total request
                                 timeout. It can also be a pair (tuple) of
                                 (connection, read) timeouts.
        """
        method = method.upper()
        assert method in ["GET", "HEAD", "DELETE", "POST", "PUT", "PATCH", "OPTIONS"]

        if post_params and body:
            raise ApiValueError(
                "body parameter cannot be used with post_params parameter."
            )

        post_params = post_params or {}
        headers = headers or {}

        if not self.session:
            raise ApiValueError(
                "OAuth2 session is not initialized. Please initialize it before making requests."
            )

        session = self.session

        timeout = None
        if _request_timeout:
            if isinstance(_request_timeout, (int, float)):
                timeout = Timeout(timeout=_request_timeout)
            elif isinstance(_request_timeout, tuple) and len(_request_timeout) == 2:
                connect_timeout, read_timeout = _request_timeout
                timeout = Timeout(timeout=connect_timeout, read=read_timeout)

        try:
            if method in ["POST", "PUT", "PATCH", "OPTIONS", "DELETE"]:
                content_type = headers.get("Content-Type")

                if not content_type or re.search("json", content_type, re.IGNORECASE):
                    request_body = None
                    if body is not None:
                        request_body = json.dumps(body)
                    r = session.request(
                        method,
                        url,
                        content=request_body,
                        timeout=timeout,
                        headers=headers,
                    )

                elif content_type == "application/x-www-form-urlencoded":
                    r = session.request(
                        method, url, data=post_params, timeout=timeout, headers=headers
                    )

                elif content_type == "multipart/form-data":
                    del headers["Content-Type"]
                    files = []
                    data = {}

                    for key, value in post_params:
                        if isinstance(value, tuple) and len(value) >= 2:
                            # This is a file tuple (filename, file_data, [content_type])
                            filename, file_data = value[0], value[1]
                            content_type = value[2] if len(value) > 2 else None
                            files.append((key, (filename, file_data, content_type)))
                        elif isinstance(value, dict):
                            # JSON-serialize dictionary values
                            if key in data:
                                # If we already have this key, handle as needed
                                # (convert to list or append to existing list)
                                if not isinstance(data[key], list):
                                    data[key] = [data[key]]
                                data[key].append(json.dumps(value))
                            else:
                                data[key] = json.dumps(value)
                        else:
                            # Regular form data
                            if key in data:
                                if not isinstance(data[key], list):
                                    data[key] = [data[key]]
                                data[key].append(value)
                            else:
                                data[key] = value
                    r = session.request(
                        method,
                        url,
                        files=files,
                        data=data,
                        timeout=timeout,
                        headers=headers,
                    )

                elif isinstance(body, str) or isinstance(body, bytes):
                    r = session.request(
                        method, url, content=body, timeout=timeout, headers=headers
                    )

                elif headers["Content-Type"].startswith("text/") and isinstance(
                    body, bool
                ):
                    request_body = "true" if body else "false"
                    r = session.request(
                        method,
                        url,
                        content=request_body,
                        timeout=timeout,
                        headers=headers,
                    )

                else:
                    msg = """Cannot prepare a request message for provided arguments.
                             Please check that your arguments match declared content type."""
                    raise ApiException(status=0, reason=msg)

            else:
                r = session.request(
                    method, url, timeout=timeout, headers=headers
                )

        except httpx.HTTPError as e:
            msg = "\n".join([type(e).__name__, str(e)])
            raise ApiException(status=0, reason=msg)

        return RESTResponse(r)

    def _get_session_defaults(self):
        client_kwargs = {
            "verify": (
                self.configuration.ssl_ca_cert
                if self.configuration.ssl_ca_cert
                else self.configuration.verify_ssl
            )
        }

        if self.configuration.proxy:
            client_kwargs["proxy"] = self.configuration.proxy

            existing_headers = client_kwargs.pop("headers")
            if self.configuration.proxy_headers:
                for key, value in self.configuration.proxy_headers.items():
                    existing_headers[key] = value
            client_kwargs["headers"] = existing_headers

        if self.configuration.retries is not None:
            transport = httpx.HTTPTransport(retries=self.configuration.retries)
            client_kwargs["transport"] = transport

        return client_kwargs
