#######################################################################################
# Copyright ETSI Contributors and Others.
#
# 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 dataclasses import dataclass

from osm_common.dbbase import DbBase
from osm_common.temporal.activities.base import BaseActivity
from osm_common.temporal.dataclasses_common import CharmInfo, VduComputeConstraints


class TestVimConnectivity(BaseActivity):
    """Validates the credentials by attempting to connect to the given Juju Controller.

    Collaborators:
        Juju Controller:    Connect only

    Raises  (Retryable):
        ApplicationError    If any of password, cacert, cloud_credentials is invalid
                            or Juju controller is not reachable

    Activity Lifecycle:
        This activity should complete relatively quickly (in a few seconds).
        However, it would be reasonable to wait more than 72 seconds (network timeout)
        incase there are network issues.

        This activity will not report a heartbeat due to its
        short-running nature.

        It is recommended, although not necessary to implement a
        back-off strategy for this activity, as it will naturally block
        and wait on each connection attempt.
    """

    @dataclass
    class Input:
        """
        Input dataclass for the Test Vim Connectivity Ativity

        Attributes:
        -----------
        vim_uuid : str
            The UUID of the VIM account as stored in the OSM vim
            collection in Mongo
        """

        vim_uuid: str

    def __init__(self, juju_controller):
        super().__init__()
        self.juju_controller = juju_controller

    async def __call__(self, activity_input: Input) -> None:
        raise NotImplementedError()


class CreateModel(BaseActivity):
    """Connects to Juju Controller. Creates a new model.

    Collaborators:
        DB Read:            vim_accounts
        Juju Controller:    Connect and create model.

    Raises  (Retryable):
        ApplicationError    If Juju controller is not reachable.
                            If the model already exists.

    Activity Lifecycle:
        This activity should complete relatively quickly (in a few seconds).
        However, it would be reasonable to wait more than 72 seconds (network timeout)
        incase there are network issues.

        This activity will not report a heartbeat due to its
        short-running nature.

        It is recommended, although not necessary to implement a
        back-off strategy for this activity, as it will naturally block
        and wait on each connection attempt.
    """

    @dataclass
    class Input:
        """
        Contains the information related to a model.

        Attributes:
        -----------
        vim_uuid : str
            The UUID of the VIM as stored in the OSM vim_accounts
            collection in Mongo.

        model_name : str
            Name of the Juju model used to deploy charms.
        """

        vim_uuid: str
        model_name: str

    def __init__(self, db: DbBase, juju_controller):
        super().__init__()
        self.db: DbBase = db
        self.juju_controller = juju_controller

    async def __call__(self, activity_input: Input) -> None:
        raise NotImplementedError()


class DeployCharm(BaseActivity):
    """Deploys a charm.

    Collaborators:
        Juju Controller:    Connect and deploy charm

    Raises  (Retryable):
        ApplicationError    If Juju controller is not reachable
                            If application already exists

    Activity Lifecycle:
        This activity should complete relatively quickly (in a few seconds).
        However, it would be reasonable to wait more than 72 seconds (network timeout)
        incase there are network issues.

        This activity will not report a heartbeat due to its
        short-running nature.

        It is recommended, although not necessary to implement a
        back-off strategy for this activity, as it will naturally block
        and wait on each connection attempt.
    """

    @dataclass
    class Input:
        """
        Input dataclass for workflow that instantiates a VDU.

        vim_uuid: str

        model_name: str

        charm_info : CharmInfo

        constraints: VduComputeConstraints

        cloud: VIM cloud type

        config: Config details of application
        """

        vim_uuid: str
        model_name: str
        charm_info: CharmInfo
        constraints: VduComputeConstraints
        cloud: str
        config: dict

    def __init__(self, juju_controller):
        super().__init__()
        self.juju_controller = juju_controller

    async def __call__(self, activity_input: Input) -> None:
        raise NotImplementedError()


class CheckCharmStatus(BaseActivity):
    """Checks the ready status of the charm.  This activity will block until the status of
    the application is either "active" or "blocked".  Additionally, it also blocks until
    the workload status of each of its units is also either "active" or "blocked".

    Collaborators:
        Juju Controller:    Connect to controller and check charm status.

    Raises  (Retryable):
        ApplicationError    If any of password, cacert, cloud_credentials is invalid
                            or Juju controller is not reachable

    Activity Lifecycle:
        This activity will continue indefinitely until the specified charm deployment
        has reached a ready state.  Heartbeats are performed to ensure this activity
        does not time out.

        A start-to-close of something reasonable (such as 5 minutes) should be implemented
        at the workflow level and such a timeout shall trigger workflow failure logic.
    """

    @dataclass
    class Input:
        """
        Input dataclass for checking on a specific charm's deployment
        status

        Attributes:
        -----------
        vim_uuid : str
            The UUID of the VIM as stored in the OSM vim_accounts
            collection in Mongo.

        model_name : str
            Name of the model to create in Juju.

        application_name : str
            Name of the application that the state is going to be
            awaited.

        poll_interval : int (optional)
            Time, in seconds, to wait between status checks.
        """

        vim_uuid: str
        model_name: str
        application_name: str
        poll_interval: int = 1

    def __init__(self, juju_controller):
        super().__init__()
        self.juju_controller = juju_controller

    async def __call__(self, activity_input: Input) -> None:
        raise NotImplementedError()


class RemoveCharm(BaseActivity):
    """Removes the given charm from the given model.

    Collaborators:
        Juju Controller:    Connect to controller and remove charm.

    Raises  (Retryable):
        ApplicationError    If any of password, cacert, cloud_credentials is invalid
                            or Juju controller is not reachable

    Activity Lifecycle:
        This activity should complete relatively quickly (in a few seconds).
        However, it would be reasonable to wait more than 72 seconds (network timeout)
        incase there are network issues.

        This activity will not report a heartbeat due to its
        short-running nature.

        It is recommended, although not necessary to implement a
        back-off strategy for this activity, as it will naturally block
        and wait on each connection attempt.
    """

    @dataclass
    class Input:
        """
        Input dataclass for removing a specific charm

        Attributes:
        -----------
        vim_uuid : str
            The UUID of the VIM as stored in the OSM vim_accounts
            collection in Mongo.

        model_name : str
            Name of the model in Juju where the charm is deployed.

        application_name : str
            Name of the application to be removed.

        force_remove : bool
            If application has to be forcefully removed.

        """

        vim_uuid: str
        model_name: str
        application_name: str
        force_remove: bool

    def __init__(self, juju_controller):
        super().__init__()
        self.juju_controller = juju_controller

    async def __call__(self, activity_input: Input) -> None:
        raise NotImplementedError()


class CheckCharmIsRemoved(BaseActivity):
    """Checks the removal of the charm. This activity will block until the application
    is not present in the specified model.

    Collaborators:
        Juju Controller:    Connect to controller and check charm status.

    Raises  (Retryable):
        ApplicationError    If any of password, cacert, cloud_credentials is invalid
                            or Juju controller is not reachable

    Activity Lifecycle:
        This activity will continue indefinitely until the specified charm is removed.
        Heartbeats are performed to ensure this activity does not time out.

        A start-to-close of something reasonable (such as 5 minutes) should be implemented
        at the workflow level and such a timeout shall trigger workflow failure logic.
    """

    @dataclass
    class Input:
        """
        Input dataclass for checking on a specific charm's removal
        status

        Attributes:
        -----------
        vim_uuid : str
            The UUID of the VIM as stored in the OSM vim_accounts
            collection in Mongo.

        model_name : str
            Name of the model in Juju where the charm is deployed.

        application_name : str
            Name of the application for which the removal is going
            to be awaited.

        poll_interval : int (optional)
            Time, in seconds, to wait between status checks.
        """

        vim_uuid: str
        model_name: str
        application_name: str
        poll_interval: int = 1

    def __init__(self, juju_controller):
        super().__init__()
        self.juju_controller = juju_controller

    async def __call__(self, activity_input: Input) -> None:
        raise NotImplementedError()


class RemoveModel(BaseActivity):
    """Removes the given model from the given controller.

    Collaborators:
        Juju Controller:    Connect to controller and remove model.

    Raises  (Retryable):
        ApplicationError    If any of password, cacert, cloud_credentials is invalid
                            or Juju controller is not reachable

    Activity Lifecycle:
        This activity should complete relatively quickly (in a few seconds).
        However, it would be reasonable to wait more than 72 seconds (network timeout)
        incase there are network issues.

        This activity will not report a heartbeat due to its
        short-running nature.

        It is recommended, although not necessary to implement a
        back-off strategy for this activity, as it will naturally block
        and wait on each connection attempt.
    """

    @dataclass
    class Input:
        """
        Input dataclass for removing a specific model

        Attributes:
        -----------
        vim_uuid : str
            The UUID of the VIM as stored in the OSM vim_accounts
            collection in Mongo.

        model_name : str
            Name of the model in Juju to be removed.

        force_remove : bool
            If model has to be forcefully removed.

        """

        vim_uuid: str
        model_name: str
        force_remove: bool

    def __init__(self, juju_controller):
        super().__init__()
        self.juju_controller = juju_controller

    async def __call__(self, activity_input: Input) -> None:
        raise NotImplementedError()


class CheckModelIsRemoved(BaseActivity):
    """Checks the removal of the model. This activity will block until the model
    is not present in the specified controller.

    Collaborators:
        Juju Controller:    Connect to controller and check model removal status.

    Raises  (Retryable):
        ApplicationError    If any of password, cacert, cloud_credentials is invalid
                            or Juju controller is not reachable

    Activity Lifecycle:
        This activity will continue indefinitely until the specified model is removed.
        Heartbeats are performed to ensure this activity does not time out.

        A start-to-close of something reasonable (such as 5 minutes) should be implemented
        at the workflow level and such a timeout shall trigger workflow failure logic.
    """

    @dataclass
    class Input:
        """
        Input dataclass for checking on a specific model's removal
        status

        Attributes:
        -----------
        vim_uuid : str
            The UUID of the VIM as stored in the OSM vim_accounts
            collection in Mongo.

        model_name : str
            Name of the model in Juju for which the removal is going
            to be awaited.

        poll_interval : int (optional)
            Time, in seconds, to wait between status checks.
        """

        vim_uuid: str
        model_name: str
        poll_interval: int = 1

    def __init__(self, juju_controller):
        super().__init__()
        self.juju_controller = juju_controller

    async def __call__(self, activity_input: Input) -> None:
        raise NotImplementedError()


class ResolveCharmErrors(BaseActivity):
    """Mark the errors as resolved in all units for the given charm in the given model.

    Collaborators:
        Juju Controller:    Connect to controller and mark charm errors as resolved.

    Raises  (Retryable):
        ApplicationError    If any of password, cacert, cloud_credentials is invalid
                            or Juju controller is not reachable

    Activity Lifecycle:
        This activity will continue indefinitely until the error of specific application
        is resolved. Heartbeats are performed to ensure this activity does not time out.

        A start-to-close of something reasonable (such as 2 minutes) should be implemented
        at the workflow level and such a timeout shall trigger workflow failure logic.

    """

    @dataclass
    class Input:
        """
        Input dataclass for marking errors as resolved on all units of a specific charm

        Attributes:
        -----------
        vim_uuid : str
            The UUID of the VIM as stored in the OSM vim_accounts
            collection in Mongo.

        model_name : str
            Name of the model in Juju where the charm is deployed.

        application_name : str
            Name of the application for which unit errors have to be resolved.

        poll_interval : int (optional)
            Time, in seconds, to wait between application status checks to see if error resolved or not.

        """

        vim_uuid: str
        model_name: str
        application_name: str
        poll_interval: int = 1

    def __init__(self, juju_controller):
        super().__init__()
        self.juju_controller = juju_controller

    async def __call__(self, activity_input: Input) -> None:
        raise NotImplementedError()
