#######################################################################################
# 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 datetime import timedelta
import traceback

from osm_common.temporal_task_queues.task_queues_mappings import LCM_TASK_QUEUE
from osm_common.temporal.states import VimState, VimOperationState
from osm_common.temporal.workflows.vim import (
    VimCreateWorkflow,
    VimUpdateWorkflow,
    VimDeleteWorkflow,
)
from osm_common.temporal.activities.vim import (
    DeleteVimRecord,
    UpdateVimState,
    UpdateVimOperationState,
)
from osm_common.temporal.activities.paas import TestVimConnectivity
from temporalio import workflow
from temporalio.common import RetryPolicy
from temporalio.exceptions import ActivityError

_SANDBOXED = False
retry_policy = RetryPolicy(maximum_attempts=3)
default_schedule_to_close_timeout = timedelta(minutes=10)


@workflow.defn(name=VimCreateWorkflow.__name__, sandboxed=False)
class VimCreateWorkflowImpl(VimCreateWorkflow):
    @workflow.run
    async def run(self, workflow_input: VimCreateWorkflow.Input) -> None:
        vim_state = UpdateVimState.Input(
            workflow_input.vim_uuid, VimState.ENABLED, "Done"
        )
        op_state = UpdateVimOperationState.Input(
            workflow_input.vim_uuid,
            workflow_input.op_id,
            VimOperationState.COMPLETED,
            "Done",
        )
        try:
            await workflow.execute_activity(
                activity=TestVimConnectivity.__name__,
                arg=TestVimConnectivity.Input(workflow_input.vim_uuid),
                activity_id=f"{TestVimConnectivity.__name__}-{workflow_input.vim_uuid}",
                task_queue=LCM_TASK_QUEUE,
                schedule_to_close_timeout=default_schedule_to_close_timeout,
                retry_policy=retry_policy,
            )

        except ActivityError as e:
            error_details = str(e.cause.with_traceback(e.__traceback__))
            vim_state = UpdateVimState.Input(
                workflow_input.vim_uuid, VimState.ERROR, str(e.cause.message)
            )
            op_state = UpdateVimOperationState.Input(
                workflow_input.vim_uuid,
                workflow_input.op_id,
                VimOperationState.FAILED,
                error_details,
            )
            await self.update_states(op_state, vim_state)
            self.logger.error(
                f"{VimCreateWorkflow.__name__} failed with {error_details}"
            )
            raise e

        except Exception as e:
            error_details = str(traceback.format_exc())
            vim_state = UpdateVimState.Input(
                workflow_input.vim_uuid, VimState.ERROR, str(e)
            )
            op_state = UpdateVimOperationState.Input(
                workflow_input.vim_uuid,
                workflow_input.op_id,
                VimOperationState.FAILED,
                error_details,
            )
            await self.update_states(op_state, vim_state)
            self.logger.error(
                f"{VimCreateWorkflow.__name__} failed with {error_details}"
            )
            raise e

        await self.update_states(op_state, vim_state)

    async def update_states(
        self,
        op_state: UpdateVimOperationState.Input,
        vim_state: UpdateVimState.Input,
    ):
        raised_exceptions = []
        try:
            await workflow.execute_activity(
                activity=UpdateVimState.__name__,
                arg=vim_state,
                activity_id=f"{UpdateVimState.__name__}-{vim_state.vim_uuid}",
                task_queue=LCM_TASK_QUEUE,
                schedule_to_close_timeout=default_schedule_to_close_timeout,
                retry_policy=retry_policy,
            )
        except ActivityError as e:
            raised_exceptions.append(e)
            self.logger.error(
                f"{VimCreateWorkflow.__name__} failed to update VIM state."
            )
        try:
            await workflow.execute_activity(
                activity=UpdateVimOperationState.__name__,
                arg=op_state,
                activity_id=f"{UpdateVimOperationState.__name__}-{op_state.vim_uuid}",
                task_queue=LCM_TASK_QUEUE,
                schedule_to_close_timeout=default_schedule_to_close_timeout,
                retry_policy=retry_policy,
            )
        except ActivityError as e:
            self.logger.error(
                f"{VimCreateWorkflow.__name__} failed to update VIM operation state."
            )
            raised_exceptions.append(e)

        # Only return de first exception. There is not much value in complicating the logic
        # here to return all the exceptions if more than one is raised.
        if raised_exceptions:
            raise raised_exceptions[0]


@workflow.defn(name=VimUpdateWorkflow.__name__, sandboxed=False)
class VimUpdateWorkflowImpl(VimCreateWorkflowImpl):
    @workflow.run
    async def run(self, workflow_input: VimUpdateWorkflow.Input) -> None:
        await super().run(workflow_input)


@workflow.defn(name=VimDeleteWorkflow.__name__, sandboxed=False)
class VimDeleteWorkflowImpl(VimDeleteWorkflow):
    @workflow.run
    async def run(self, workflow_input: VimDeleteWorkflow.Input) -> None:
        await workflow.execute_activity(
            activity=DeleteVimRecord.__name__,
            arg=DeleteVimRecord.Input(workflow_input.vim_uuid),
            activity_id=f"{DeleteVimRecord.__name__}-{workflow_input.vim_uuid}",
            task_queue=LCM_TASK_QUEUE,
            schedule_to_close_timeout=default_schedule_to_close_timeout,
            retry_policy=retry_policy,
        )
