# coding: utf-8
"""
    Snowflake Services API

    The Snowflake Services API is a REST API that you can use to access, update, and perform certain actions on Services resource in a Snowflake database.  # noqa: E501

    The version of the OpenAPI document: 0.0.1
    Contact: support@snowflake.com
    Generated by: https://openapi-generator.tech

    Do not edit this file manually.
"""

from __future__ import absolute_import

import json
import logging
import re
import typing
import urllib3

from snowflake.core._http_requests import create_connection_pool
from snowflake.core.exceptions import (APIError, UnauthorizedError,
                                       ForbiddenError, NotFoundError,
                                       ConflictError, ServerError,
                                       _APIValueError)
from snowflake.connector import SnowflakeConnection
from snowflake.core.rest import RESTResponse

if typing.TYPE_CHECKING:
    from snowflake.core._root import Root

# Until we work figure out how to have the generated code use APIError
#  instead of ApiException we alias it.
ApiException = APIError

logger = logging.getLogger(__name__)

RETRY_STATUS_CODES: typing.Set[int] = {202, 429, 503, 504}


def evaluate_status_codes(root, r):
    retry_request = (root.parameters.should_retry_request
                     and r.status in RETRY_STATUS_CODES)
    if not 200 <= r.status <= 299 and not retry_request:
        if r.status == 401:
            raise UnauthorizedError(http_resp=r)

        if r.status == 403:
            raise ForbiddenError(http_resp=r)

        if r.status == 404:
            raise NotFoundError(http_resp=r)

        if r.status == 409:
            raise ConflictError(http_resp=r)

        if 500 <= r.status <= 599:
            raise ServerError(http_resp=r)

        raise APIError(http_resp=r)

    return r


class RESTClientObject(object):

    def __init__(self, root: "Root", *args, **kwargs):
        self.root = root
        self.pool_manager = create_connection_pool(
            *args,
            **kwargs,
        )

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

        :param method: http request method
        :param url: http request url
        :param query_params: query parameters in the 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 _preload_content: if False, the urllib3.HTTPResponse object will
                                 be returned without reading/decoding response
                                 data. Default is True.
        :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 {}
        # url already contains the URL query string
        # so reset query_params to empty dict
        query_params = {}

        timeout = None
        if _request_timeout:
            if isinstance(_request_timeout, (int, float)):  # noqa: E501,F821
                timeout = urllib3.Timeout(total=_request_timeout)
            elif (isinstance(_request_timeout, tuple)
                  and len(_request_timeout) == 2):
                timeout = urllib3.Timeout(connect=_request_timeout[0],
                                          read=_request_timeout[1])

        try:
            # For `POST`, `PUT`, `PATCH`, `OPTIONS`, `DELETE`
            if method in ['POST', 'PUT', 'PATCH', 'OPTIONS', 'DELETE']:

                # no content type provided or payload is json
                if not headers.get('Content-Type') or re.search(
                        'json', headers['Content-Type'], re.IGNORECASE):
                    request_body = None
                    if body is not None:
                        request_body = json.dumps(body)
                    r = self.pool_manager.request(
                        root,
                        method,
                        url,
                        body=request_body,
                        preload_content=_preload_content,
                        timeout=timeout,
                        headers=headers)
                elif headers[
                        'Content-Type'] == 'application/x-www-form-urlencoded':  # noqa: E501
                    r = self.pool_manager.request(
                        root,
                        method,
                        url,
                        fields=post_params,
                        encode_multipart=False,
                        preload_content=_preload_content,
                        timeout=timeout,
                        headers=headers)
                elif headers['Content-Type'] == 'multipart/form-data':
                    # must del headers['Content-Type'], or the correct
                    # Content-Type which generated by urllib3 will be
                    # overwritten.
                    del headers['Content-Type']
                    r = self.pool_manager.request(
                        root,
                        method,
                        url,
                        fields=post_params,
                        encode_multipart=True,
                        preload_content=_preload_content,
                        timeout=timeout,
                        headers=headers)
                # Pass a `string` parameter directly in the body to support
                # other content types than Json when `body` argument is
                # provided in serialized form
                elif isinstance(body, str) or isinstance(body, bytes):
                    request_body = body
                    r = self.pool_manager.request(
                        root,
                        method,
                        url,
                        body=request_body,
                        preload_content=_preload_content,
                        timeout=timeout,
                        headers=headers)
                else:
                    # Cannot generate the request from given parameters
                    msg = """Cannot prepare a request message for provided
                             arguments. Please check that your arguments match
                             declared content type."""
                    raise APIError(status=0, reason=msg)
            # For `GET`, `HEAD`
            else:
                r = self.pool_manager.request(
                    root,
                    method,
                    url,
                    fields={},
                    preload_content=_preload_content,
                    timeout=timeout,
                    headers=headers,
                )
        except urllib3.exceptions.SSLError as e:
            msg = "{0}\n{1}".format(type(e).__name__, str(e))
            raise APIError(status=0, reason=msg)

        if _preload_content:
            r = RESTResponse(r)

            # log response body
            logger.debug("response body: %s", r.data)

        return evaluate_status_codes(root, r)

    def get_request(
        self,
        root,
        url,
        headers=None,
        query_params=None,
        _preload_content=True,
        _request_timeout=None,
    ):
        return self.request(
            root,
            "GET",
            url,
            headers=headers,
            _preload_content=_preload_content,
            _request_timeout=_request_timeout,
            query_params=query_params,
        )

    def head_request(
        self,
        root,
        url,
        headers=None,
        query_params=None,
        _preload_content=True,
        _request_timeout=None,
    ):
        return self.request(
            root,
            "HEAD",
            url,
            headers=headers,
            _preload_content=_preload_content,
            _request_timeout=_request_timeout,
            query_params=query_params,
        )

    def options_request(self,
                        root,
                        url,
                        headers=None,
                        query_params=None,
                        post_params=None,
                        body=None,
                        _preload_content=True,
                        _request_timeout=None):
        return self.request(
            root,
            "OPTIONS",
            url,
            headers=headers,
            query_params=query_params,
            post_params=post_params,
            _preload_content=_preload_content,
            _request_timeout=_request_timeout,
            body=body,
        )

    def delete_request(self,
                       root,
                       url,
                       headers=None,
                       query_params=None,
                       body=None,
                       _preload_content=True,
                       _request_timeout=None):
        return self.request(
            root,
            "DELETE",
            url,
            headers=headers,
            query_params=query_params,
            _preload_content=_preload_content,
            _request_timeout=_request_timeout,
            body=body,
        )

    def post_request(self,
                     root,
                     url,
                     headers=None,
                     query_params=None,
                     post_params=None,
                     body=None,
                     _preload_content=True,
                     _request_timeout=None):
        return self.request(
            root,
            "POST",
            url,
            headers=headers,
            query_params=query_params,
            post_params=post_params,
            _preload_content=_preload_content,
            _request_timeout=_request_timeout,
            body=body,
        )

    def put_request(self,
                    root,
                    url,
                    headers=None,
                    query_params=None,
                    post_params=None,
                    body=None,
                    _preload_content=True,
                    _request_timeout=None):
        return self.request(
            root,
            "PUT",
            url,
            headers=headers,
            query_params=query_params,
            post_params=post_params,
            _preload_content=_preload_content,
            _request_timeout=_request_timeout,
            body=body,
        )

    def patch_request(self,
                      root,
                      url,
                      headers=None,
                      query_params=None,
                      post_params=None,
                      body=None,
                      _preload_content=True,
                      _request_timeout=None):
        return self.request(
            root,
            "PATCH",
            url,
            headers=headers,
            query_params=query_params,
            post_params=post_params,
            _preload_content=_preload_content,
            _request_timeout=_request_timeout,
            body=body,
        )


class StoredProcClientObject(object):

    def request(
        self,
        root,
        method,
        url,
        query_params={},
        headers={},
        body={},
        post_params={},
        _preload_content=True,
        _request_timeout=None,
    ):
        """Perform requests.
        :param method: http request method
        :param url: http request url
        :param query_params: query parameters in the 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 _preload_content: if False, the urllib3.HTTPResponse object will
                                 be returned without reading/decoding response
                                 data. Default is True.
        :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'
        ]
        import _snowflake
        parsed_url = urllib3.util.parse_url(url)
        query_params = query_params or {}
        response_dict = _snowflake.send_snow_api_request(
            method, parsed_url.path, dict(query_params), headers, body,
            post_params, _request_timeout)
        json_content = json.loads(response_dict["content"])
        if "data" in json_content:
            r = urllib3.HTTPResponse(body=json.dumps(json_content["data"]))
        else:
            r = urllib3.HTTPResponse(
                body=json.dumps(json_content).encode("utf-8"))
        r.status = response_dict["status"]
        formatted_headers = {
            key.strip(): value
            for key, value in response_dict["headers"].items()
        }
        r.headers = formatted_headers
        logger.info(
            "Received an HTTP response of status %d",
            r.status,
        )
        if _preload_content:
            r = RESTResponse(r)
            # log response body
            logger.debug("response body: %s", r.data)

        return evaluate_status_codes(root, r)

    def get_request(
        self,
        root,
        url,
        headers=None,
        query_params=None,
        _preload_content=True,
        _request_timeout=None,
    ):
        return self.request(
            root,
            "GET",
            url,
            headers=headers,
            _preload_content=_preload_content,
            _request_timeout=_request_timeout,
            query_params=query_params,
        )

    def head_request(
        self,
        root,
        url,
        headers=None,
        query_params=None,
        _preload_content=True,
        _request_timeout=None,
    ):
        return self.request(
            root,
            "HEAD",
            url,
            headers=headers,
            _preload_content=_preload_content,
            _request_timeout=_request_timeout,
            query_params=query_params,
        )

    def options_request(
        self,
        root,
        url,
        headers=None,
        query_params=None,
        post_params=None,
        body=None,
        _preload_content=True,
        _request_timeout=None,
    ):
        return self.request(
            root,
            "OPTIONS",
            url,
            headers=headers,
            query_params=query_params,
            post_params=post_params,
            _preload_content=_preload_content,
            _request_timeout=_request_timeout,
            body=body,
        )

    def delete_request(
        self,
        root,
        url,
        headers=None,
        query_params=None,
        body=None,
        _preload_content=True,
        _request_timeout=None,
    ):
        return self.request(
            root,
            "DELETE",
            url,
            headers=headers,
            query_params=query_params,
            _preload_content=_preload_content,
            _request_timeout=_request_timeout,
            body=body,
        )

    def post_request(
        self,
        root,
        url,
        headers=None,
        query_params=None,
        post_params=None,
        body=None,
        _preload_content=True,
        _request_timeout=None,
    ):
        return self.request(
            root,
            "POST",
            url,
            headers=headers,
            query_params=query_params,
            post_params=post_params,
            _preload_content=_preload_content,
            _request_timeout=_request_timeout,
            body=body,
        )

    def put_request(
        self,
        root,
        url,
        headers=None,
        query_params=None,
        post_params=None,
        body=None,
        _preload_content=True,
        _request_timeout=None,
    ):
        return self.request(
            root,
            "PUT",
            url,
            headers=headers,
            query_params=query_params,
            post_params=post_params,
            _preload_content=_preload_content,
            _request_timeout=_request_timeout,
            body=body,
        )

    def patch_request(
        self,
        root,
        url,
        headers=None,
        query_params=None,
        post_params=None,
        body=None,
        _preload_content=True,
        _request_timeout=None,
    ):
        return self.request(
            root,
            "PATCH",
            url,
            headers=headers,
            query_params=query_params,
            post_params=post_params,
            _preload_content=_preload_content,
            _request_timeout=_request_timeout,
            body=body,
        )
