#
# Copyright (c) 2021 Nitric Technologies Pty Ltd.
#
# This file is part of Nitric Python 3 SDK.
# See https://github.com/nitrictech/python-sdk for further info.
#
# 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.
#

# Generated by the protocol buffer compiler.  DO NOT EDIT!
# sources: proto/deploy/v1/deploy.proto
# plugin: python-betterproto
import warnings
from dataclasses import dataclass
from typing import (
    TYPE_CHECKING,
    AsyncIterator,
    Dict,
    List,
    Optional,
)

import betterproto
import betterproto.lib.google.protobuf as betterproto_lib_google_protobuf
import grpclib
from betterproto.grpc.grpclib_server import ServiceBase

from ...resource import v1 as __resource_v1__


if TYPE_CHECKING:
    import grpclib.server
    from betterproto.grpc.grpclib_client import MetadataLike
    from grpclib.metadata import Deadline


@dataclass(eq=False, repr=False)
class DeployUpRequest(betterproto.Message):
    spec: "Spec" = betterproto.message_field(1)
    """The spec to deploy"""

    attributes: "betterproto_lib_google_protobuf.Struct" = betterproto.message_field(2)
    """
    A map of attributes related to the deploy request this allows for adding
    project identifiers etc.
    """


@dataclass(eq=False, repr=False)
class DeployUpEvent(betterproto.Message):
    message: "DeployEventMessage" = betterproto.message_field(1, group="content")
    result: "DeployUpEventResult" = betterproto.message_field(2, group="content")


@dataclass(eq=False, repr=False)
class DeployEventMessage(betterproto.Message):
    """Messages to provide status updates on the deployment"""

    message: str = betterproto.string_field(1)


@dataclass(eq=False, repr=False)
class UpResult(betterproto.Message):
    string_result: str = betterproto.string_field(1, group="content")
    """Simple formatted string output as result"""


@dataclass(eq=False, repr=False)
class DeployUpEventResult(betterproto.Message):
    """Terminal message indicating deployment success"""

    success: bool = betterproto.bool_field(1)
    """Indicate the success status"""

    result: "UpResult" = betterproto.message_field(2)
    """
    Output state as a struct, this can be provided as an output file or pretty
    printed for CLI output
    """


@dataclass(eq=False, repr=False)
class DeployDownRequest(betterproto.Message):
    attributes: "betterproto_lib_google_protobuf.Struct" = betterproto.message_field(1)
    """
    A map of attributes related to the deploy request this allows for adding
    project identifiers etc.
    """


@dataclass(eq=False, repr=False)
class DeployDownEvent(betterproto.Message):
    message: "DeployEventMessage" = betterproto.message_field(1, group="content")
    result: "DeployDownEventResult" = betterproto.message_field(2, group="content")


@dataclass(eq=False, repr=False)
class DeployDownEventResult(betterproto.Message):
    """Terminal message indicating deployment success"""

    pass


@dataclass(eq=False, repr=False)
class ImageSource(betterproto.Message):
    """An image source to be used for execution unit deployment"""

    uri: str = betterproto.string_field(1)
    """
    URI of the docker image TODO: May also need to provide auth information
    (although this should just be configured on the running client already)
    """


@dataclass(eq=False, repr=False)
class ExecutionUnit(betterproto.Message):
    """A unit of execution (i.e. function/container)"""

    image: "ImageSource" = betterproto.message_field(1, group="source")
    """Container image as a execution unit"""

    workers: int = betterproto.int32_field(10)
    """Expected worker count for this execution unit"""

    timeout: int = betterproto.int32_field(11)
    """Configurable timeout for request handling"""

    memory: int = betterproto.int32_field(12)
    """Configurable memory size for this instance"""

    type: str = betterproto.string_field(13)
    """
    A simple type property describes the requested type of execution unit that
    this should be for this project, a provider can implement how this request
    is satisfied in any way
    """

    env: Dict[str, str] = betterproto.map_field(
        14, betterproto.TYPE_STRING, betterproto.TYPE_STRING
    )
    """Environment variables for this execution unit"""

    def __post_init__(self) -> None:
        super().__post_init__()
        if self.is_set("timeout"):
            warnings.warn("ExecutionUnit.timeout is deprecated", DeprecationWarning)
        if self.is_set("memory"):
            warnings.warn("ExecutionUnit.memory is deprecated", DeprecationWarning)


@dataclass(eq=False, repr=False)
class Bucket(betterproto.Message):
    pass


@dataclass(eq=False, repr=False)
class Topic(betterproto.Message):
    subscriptions: List["SubscriptionTarget"] = betterproto.message_field(1)
    """TODO: Include topic specifications here"""


@dataclass(eq=False, repr=False)
class Queue(betterproto.Message):
    pass


@dataclass(eq=False, repr=False)
class Collection(betterproto.Message):
    pass


@dataclass(eq=False, repr=False)
class Secret(betterproto.Message):
    pass


@dataclass(eq=False, repr=False)
class SubscriptionTarget(betterproto.Message):
    execution_unit: str = betterproto.string_field(1, group="target")
    """The name of an execution unit to target"""


@dataclass(eq=False, repr=False)
class TopicSubscription(betterproto.Message):
    target: "SubscriptionTarget" = betterproto.message_field(1)


@dataclass(eq=False, repr=False)
class Api(betterproto.Message):
    openapi: str = betterproto.string_field(1, group="document")
    """
    An OpenAPI document for deployment This document will contain extensions
    that hint of execution units that should be targeted as part of the
    deployment
    """


@dataclass(eq=False, repr=False)
class ScheduleTarget(betterproto.Message):
    execution_unit: str = betterproto.string_field(1, group="target")
    """The name of an execution unit to target"""


@dataclass(eq=False, repr=False)
class Schedule(betterproto.Message):
    cron: str = betterproto.string_field(1)
    target: "ScheduleTarget" = betterproto.message_field(2)


@dataclass(eq=False, repr=False)
class Resource(betterproto.Message):
    name: str = betterproto.string_field(1)
    type: "__resource_v1__.ResourceType" = betterproto.enum_field(2)
    execution_unit: "ExecutionUnit" = betterproto.message_field(10, group="config")
    bucket: "Bucket" = betterproto.message_field(11, group="config")
    topic: "Topic" = betterproto.message_field(12, group="config")
    queue: "Queue" = betterproto.message_field(13, group="config")
    api: "Api" = betterproto.message_field(14, group="config")
    policy: "Policy" = betterproto.message_field(15, group="config")
    schedule: "Schedule" = betterproto.message_field(16, group="config")
    collection: "Collection" = betterproto.message_field(17, group="config")
    secret: "Secret" = betterproto.message_field(18, group="config")


@dataclass(eq=False, repr=False)
class Policy(betterproto.Message):
    """
    TODO: This is already defined in our resource contracts... Need to
    determine if it's worth re-using unfortunately there are parts we don't
    want to duplicate, such as API config
    """

    principals: List["Resource"] = betterproto.message_field(1)
    actions: List["__resource_v1__.Action"] = betterproto.enum_field(2)
    """
    TODO: Split out discrete action definitions from resources Also need to
    allow custom action types as well Should incorporate action re-use here...
    """

    resources: List["Resource"] = betterproto.message_field(3)


@dataclass(eq=False, repr=False)
class Spec(betterproto.Message):
    resources: List["Resource"] = betterproto.message_field(1)
    """list of resources to deploy"""


class DeployServiceStub(betterproto.ServiceStub):
    async def up(
        self,
        deploy_up_request: "DeployUpRequest",
        *,
        timeout: Optional[float] = None,
        deadline: Optional["Deadline"] = None,
        metadata: Optional["MetadataLike"] = None
    ) -> AsyncIterator["DeployUpEvent"]:
        async for response in self._unary_stream(
            "/nitric.deploy.v1.DeployService/Up",
            deploy_up_request,
            DeployUpEvent,
            timeout=timeout,
            deadline=deadline,
            metadata=metadata,
        ):
            yield response

    async def down(
        self,
        deploy_down_request: "DeployDownRequest",
        *,
        timeout: Optional[float] = None,
        deadline: Optional["Deadline"] = None,
        metadata: Optional["MetadataLike"] = None
    ) -> AsyncIterator["DeployDownEvent"]:
        async for response in self._unary_stream(
            "/nitric.deploy.v1.DeployService/Down",
            deploy_down_request,
            DeployDownEvent,
            timeout=timeout,
            deadline=deadline,
            metadata=metadata,
        ):
            yield response


class DeployServiceBase(ServiceBase):
    async def up(
        self, deploy_up_request: "DeployUpRequest"
    ) -> AsyncIterator["DeployUpEvent"]:
        raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)

    async def down(
        self, deploy_down_request: "DeployDownRequest"
    ) -> AsyncIterator["DeployDownEvent"]:
        raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)

    async def __rpc_up(
        self, stream: "grpclib.server.Stream[DeployUpRequest, DeployUpEvent]"
    ) -> None:
        request = await stream.recv_message()
        await self._call_rpc_handler_server_stream(
            self.up,
            stream,
            request,
        )

    async def __rpc_down(
        self, stream: "grpclib.server.Stream[DeployDownRequest, DeployDownEvent]"
    ) -> None:
        request = await stream.recv_message()
        await self._call_rpc_handler_server_stream(
            self.down,
            stream,
            request,
        )

    def __mapping__(self) -> Dict[str, grpclib.const.Handler]:
        return {
            "/nitric.deploy.v1.DeployService/Up": grpclib.const.Handler(
                self.__rpc_up,
                grpclib.const.Cardinality.UNARY_STREAM,
                DeployUpRequest,
                DeployUpEvent,
            ),
            "/nitric.deploy.v1.DeployService/Down": grpclib.const.Handler(
                self.__rpc_down,
                grpclib.const.Cardinality.UNARY_STREAM,
                DeployDownRequest,
                DeployDownEvent,
            ),
        }
