#  Copyright 2016-2023. Couchbase, Inc.
#  All Rights Reserved.
#
#  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.

from __future__ import annotations

from typing import (TYPE_CHECKING,
                    Optional,
                    Tuple)

from couchbase.logic.observability import ObservableRequestHandler
from couchbase.management.logic.eventing_function_mgmt_types import (EVENTING_FUNCTION_MGMT_ERROR_MAP,
                                                                     DeployFunctionRequest,
                                                                     DropFunctionRequest,
                                                                     GetAllFunctionsRequest,
                                                                     GetFunctionRequest,
                                                                     GetFunctionsStatusRequest,
                                                                     PauseFunctionRequest,
                                                                     ResumeFunctionRequest,
                                                                     UndeployFunctionRequest,
                                                                     UpsertFunctionRequest)
from couchbase.options import forward_args

if TYPE_CHECKING:
    from couchbase.management.logic.eventing_function_mgmt_types import EventingFunction


class EventingFunctionMgmtRequestBuilder:

    def __init__(self) -> None:
        self._error_map = EVENTING_FUNCTION_MGMT_ERROR_MAP

    def _get_scope_context(self,
                           scope_context: Optional[Tuple[str, str]] = None) -> Tuple[Optional[str], Optional[str]]:
        if scope_context is not None:
            return scope_context[0], scope_context[1]

        return None, None

    def build_deploy_function_request(self,
                                      name: str,
                                      scope_context: Optional[Tuple[str, str]] = None,
                                      obs_handler: ObservableRequestHandler = None,
                                      *options: object,
                                      **kwargs: object) -> DeployFunctionRequest:
        final_args = forward_args(kwargs, *options)
        bucket_name, scope_name = self._get_scope_context(scope_context)
        parent_span = ObservableRequestHandler.maybe_get_parent_span(parent_span=final_args.pop('parent_span', None))
        obs_handler.create_http_span(parent_span=parent_span, bucket_name=bucket_name, scope_name=scope_name)
        timeout = final_args.pop('timeout', None)
        req = DeployFunctionRequest(self._error_map,
                                    name=name,
                                    bucket_name=bucket_name,
                                    scope_name=scope_name,
                                    **final_args)
        if timeout is not None:
            req.timeout = timeout

        return req

    def build_drop_function_request(self,
                                    name: str,
                                    scope_context: Optional[Tuple[str, str]] = None,
                                    obs_handler: ObservableRequestHandler = None,
                                    *options: object,
                                    **kwargs: object) -> DropFunctionRequest:
        final_args = forward_args(kwargs, *options)
        bucket_name, scope_name = self._get_scope_context(scope_context)
        parent_span = ObservableRequestHandler.maybe_get_parent_span(parent_span=final_args.pop('parent_span', None))
        obs_handler.create_http_span(parent_span=parent_span, bucket_name=bucket_name, scope_name=scope_name)
        timeout = final_args.pop('timeout', None)
        req = DropFunctionRequest(self._error_map,
                                  name=name,
                                  bucket_name=bucket_name,
                                  scope_name=scope_name,
                                  **final_args)
        if timeout is not None:
            req.timeout = timeout

        return req

    def build_get_all_functions_request(self,
                                        scope_context: Optional[Tuple[str, str]] = None,
                                        obs_handler: ObservableRequestHandler = None,
                                        *options: object,
                                        **kwargs: object) -> GetAllFunctionsRequest:
        final_args = forward_args(kwargs, *options)
        bucket_name, scope_name = self._get_scope_context(scope_context)
        parent_span = ObservableRequestHandler.maybe_get_parent_span(parent_span=final_args.pop('parent_span', None))
        obs_handler.create_http_span(parent_span=parent_span, bucket_name=bucket_name, scope_name=scope_name)
        timeout = final_args.pop('timeout', None)
        req = GetAllFunctionsRequest(self._error_map,
                                     bucket_name=bucket_name,
                                     scope_name=scope_name,
                                     **final_args)
        if timeout is not None:
            req.timeout = timeout

        return req

    def build_get_function_request(self,
                                   name: str,
                                   scope_context: Optional[Tuple[str, str]] = None,
                                   obs_handler: ObservableRequestHandler = None,
                                   *options: object,
                                   **kwargs: object) -> GetFunctionRequest:
        final_args = forward_args(kwargs, *options)
        parent_span = ObservableRequestHandler.maybe_get_parent_span(parent_span=final_args.pop('parent_span', None))
        obs_handler.create_http_span(parent_span=parent_span)
        timeout = final_args.pop('timeout', None)
        bucket_name, scope_name = self._get_scope_context(scope_context)
        req = GetFunctionRequest(self._error_map,
                                 name=name,
                                 bucket_name=bucket_name,
                                 scope_name=scope_name,
                                 **final_args)
        if timeout is not None:
            req.timeout = timeout

        return req

    def build_get_functions_status_request(self,
                                           scope_context: Optional[Tuple[str, str]] = None,
                                           obs_handler: ObservableRequestHandler = None,
                                           *options: object,
                                           **kwargs: object) -> GetFunctionsStatusRequest:
        final_args = forward_args(kwargs, *options)
        bucket_name, scope_name = self._get_scope_context(scope_context)
        parent_span = ObservableRequestHandler.maybe_get_parent_span(parent_span=final_args.pop('parent_span', None))
        obs_handler.create_http_span(parent_span=parent_span, bucket_name=bucket_name, scope_name=scope_name)
        timeout = final_args.pop('timeout', None)
        req = GetFunctionsStatusRequest(self._error_map,
                                        bucket_name=bucket_name,
                                        scope_name=scope_name,
                                        **final_args)
        if timeout is not None:
            req.timeout = timeout

        return req

    def build_pause_function_request(self,
                                     name: str,
                                     scope_context: Optional[Tuple[str, str]] = None,
                                     obs_handler: ObservableRequestHandler = None,
                                     *options: object,
                                     **kwargs: object) -> PauseFunctionRequest:
        final_args = forward_args(kwargs, *options)
        bucket_name, scope_name = self._get_scope_context(scope_context)
        parent_span = ObservableRequestHandler.maybe_get_parent_span(parent_span=final_args.pop('parent_span', None))
        obs_handler.create_http_span(parent_span=parent_span, bucket_name=bucket_name, scope_name=scope_name)
        timeout = final_args.pop('timeout', None)
        req = PauseFunctionRequest(self._error_map,
                                   name=name,
                                   bucket_name=bucket_name,
                                   scope_name=scope_name,
                                   **final_args)
        if timeout is not None:
            req.timeout = timeout

        return req

    def build_resume_function_request(self,
                                      name: str,
                                      scope_context: Optional[Tuple[str, str]] = None,
                                      obs_handler: ObservableRequestHandler = None,
                                      *options: object,
                                      **kwargs: object) -> ResumeFunctionRequest:
        final_args = forward_args(kwargs, *options)
        bucket_name, scope_name = self._get_scope_context(scope_context)
        parent_span = ObservableRequestHandler.maybe_get_parent_span(parent_span=final_args.pop('parent_span', None))
        obs_handler.create_http_span(parent_span=parent_span, bucket_name=bucket_name, scope_name=scope_name)
        timeout = final_args.pop('timeout', None)
        req = ResumeFunctionRequest(self._error_map,
                                    name=name,
                                    bucket_name=bucket_name,
                                    scope_name=scope_name,
                                    **final_args)
        if timeout is not None:
            req.timeout = timeout

        return req

    def build_undeploy_function_request(self,
                                        name: str,
                                        scope_context: Optional[Tuple[str, str]] = None,
                                        obs_handler: ObservableRequestHandler = None,
                                        *options: object,
                                        **kwargs: object) -> UndeployFunctionRequest:
        final_args = forward_args(kwargs, *options)
        bucket_name, scope_name = self._get_scope_context(scope_context)
        parent_span = ObservableRequestHandler.maybe_get_parent_span(parent_span=final_args.pop('parent_span', None))
        obs_handler.create_http_span(parent_span=parent_span, bucket_name=bucket_name, scope_name=scope_name)
        timeout = final_args.pop('timeout', None)
        req = UndeployFunctionRequest(self._error_map,
                                      name=name,
                                      bucket_name=bucket_name,
                                      scope_name=scope_name,
                                      **final_args)
        if timeout is not None:
            req.timeout = timeout

        return req

    def build_upsert_function_request(self,
                                      function: EventingFunction,
                                      scope_context: Optional[Tuple[str, str]] = None,
                                      obs_handler: ObservableRequestHandler = None,
                                      *options: object,
                                      **kwargs: object) -> UpsertFunctionRequest:
        final_args = forward_args(kwargs, *options)
        bucket_name, scope_name = self._get_scope_context(scope_context)
        parent_span = ObservableRequestHandler.maybe_get_parent_span(parent_span=final_args.pop('parent_span', None))
        obs_handler.create_http_span(parent_span=parent_span, bucket_name=bucket_name, scope_name=scope_name)
        timeout = final_args.pop('timeout', None)
        req = UpsertFunctionRequest(self._error_map,
                                    function=function.as_dict(),
                                    bucket_name=bucket_name,
                                    scope_name=scope_name,
                                    **final_args)
        if timeout is not None:
            req.timeout = timeout

        return req
