#######################################################################################
# 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.

import asynctest
from datetime import timedelta

from osm_common.temporal_task_queues.task_queues_mappings import LCM_TASK_QUEUE
from osm_common.temporal.activities.paas import (
    DeployCharm,
    CheckCharmStatus,
)
from osm_common.temporal.dataclasses_common import CharmInfo, VduComputeConstraints
from osm_lcm.temporal.vdu_workflows import VduInstantiateWorkflowImpl
from temporalio import activity
from temporalio.client import WorkflowFailureError
from temporalio.exceptions import (
    ActivityError,
)
from temporalio.testing import WorkflowEnvironment
from temporalio.worker import Worker

TASK_TIMEOUT = timedelta(seconds=0.5)
EXECUTION_TIMEOUT = timedelta(seconds=1)


class TestException(Exception):
    pass


@activity.defn(name=DeployCharm.__name__)
async def deploy_charm_mocked(deploy_charm_input: DeployCharm.Input) -> None:
    pass


@activity.defn(name=DeployCharm.__name__)
async def deploy_charm_mocked_raises(deploy_charm_input: DeployCharm.Input) -> None:
    raise TestException(f"{DeployCharm.__name__} failed.")


@activity.defn(name=CheckCharmStatus.__name__)
async def check_charm_status_mocked(check_charm_status: CheckCharmStatus.Input) -> None:
    pass


@activity.defn(name=CheckCharmStatus.__name__)
async def check_charm_status_mocked_raises(
    check_charm_status: CheckCharmStatus.Input,
) -> None:
    raise TestException(f"{CheckCharmStatus.__name__} failed.")


class TestVduWorkflows(asynctest.TestCase):
    task_queue_name = LCM_TASK_QUEUE
    vim_id = "some-vim-uuid"
    namespace = "some-namespace"
    app_name = "my_app_name"
    channel = "latest"
    entity_url = "ch:my-charm"
    cloud = "microk8s"
    config = {"domain_name1": "osm.org", "domain_name2": "osm.com"}
    constraints = VduComputeConstraints(mem=1, cores=1)
    charm_info = CharmInfo(app_name, channel, entity_url)
    vdu_instantiate_input_without_config = VduInstantiateWorkflowImpl.Input(
        vim_id, namespace, charm_info, constraints, cloud, {}
    )
    vdu_instantiate_input_with_config = VduInstantiateWorkflowImpl.Input(
        vim_id, namespace, charm_info, constraints, cloud, config
    )
    workflow_id = namespace + "-" + app_name

    async def setUp(self):
        self.env = await WorkflowEnvironment.start_time_skipping()
        self.client = self.env.client

    def get_worker(self, activities: list) -> Worker:
        return Worker(
            self.client,
            task_queue=self.task_queue_name,
            workflows=[VduInstantiateWorkflowImpl],
            activities=activities,
            debug_mode=True,
        )

    async def test_vdu_instantiate__without_config__successful(self):
        activities = [deploy_charm_mocked, check_charm_status_mocked]
        async with self.env, self.get_worker(activities):
            await self.client.execute_workflow(
                VduInstantiateWorkflowImpl.run,
                arg=self.vdu_instantiate_input_without_config,
                id=self.workflow_id,
                task_queue=self.task_queue_name,
                task_timeout=TASK_TIMEOUT,
                execution_timeout=EXECUTION_TIMEOUT,
            )

    async def test_vdu_instantiate__with_config__successful(self):
        activities = [deploy_charm_mocked, check_charm_status_mocked]
        async with self.env, self.get_worker(activities):
            await self.client.execute_workflow(
                VduInstantiateWorkflowImpl.run,
                arg=self.vdu_instantiate_input_with_config,
                id=self.workflow_id,
                task_queue=self.task_queue_name,
                task_timeout=TASK_TIMEOUT,
                execution_timeout=EXECUTION_TIMEOUT,
            )

    async def test_vdu_instantiate__deploy_charm_failed__raise_activity_error(self):
        activities = [deploy_charm_mocked_raises, check_charm_status_mocked]
        async with self.env, self.get_worker(activities):
            with self.assertRaises(WorkflowFailureError) as err:
                await self.client.execute_workflow(
                    VduInstantiateWorkflowImpl.run,
                    arg=self.vdu_instantiate_input_without_config,
                    id=self.workflow_id,
                    task_queue=self.task_queue_name,
                    task_timeout=TASK_TIMEOUT,
                    execution_timeout=EXECUTION_TIMEOUT,
                )
            self.assertTrue(err.exception.cause, ActivityError)

    async def test_vdu_instantiate__deploy_charm_failed__get_error_details(self):
        activities = [deploy_charm_mocked_raises, check_charm_status_mocked]
        async with self.env, self.get_worker(activities):
            with self.assertRaises(WorkflowFailureError) as err:
                await self.client.execute_workflow(
                    VduInstantiateWorkflowImpl.run,
                    arg=self.vdu_instantiate_input_without_config,
                    id=self.workflow_id,
                    task_queue=self.task_queue_name,
                    task_timeout=TASK_TIMEOUT,
                    execution_timeout=EXECUTION_TIMEOUT,
                )
            self.assertEqual(
                str(err.exception.cause.cause),
                f"TestException: {DeployCharm.__name__} failed.",
            )

    async def test_vdu_instantiate__check_status__raise_activity_error(self):
        activities = [deploy_charm_mocked, check_charm_status_mocked_raises]
        async with self.env, self.get_worker(activities):
            with self.assertRaises(WorkflowFailureError) as err:
                await self.client.execute_workflow(
                    VduInstantiateWorkflowImpl.run,
                    arg=self.vdu_instantiate_input_without_config,
                    id=self.workflow_id,
                    task_queue=self.task_queue_name,
                    task_timeout=TASK_TIMEOUT,
                    execution_timeout=EXECUTION_TIMEOUT,
                )
            self.assertTrue(err.exception.cause, ActivityError)

    async def test_vdu_instantiate__check_status__get_error_details(self):
        activities = [deploy_charm_mocked, check_charm_status_mocked_raises]
        async with self.env, self.get_worker(activities):
            with self.assertRaises(WorkflowFailureError) as err:
                await self.client.execute_workflow(
                    VduInstantiateWorkflowImpl.run,
                    arg=self.vdu_instantiate_input_without_config,
                    id=self.workflow_id,
                    task_queue=self.task_queue_name,
                    task_timeout=TASK_TIMEOUT,
                    execution_timeout=EXECUTION_TIMEOUT,
                )
            self.assertEqual(
                str(err.exception.cause.cause),
                f"TestException: {CheckCharmStatus.__name__} failed.",
            )
