from parallels.core import messages
from parallels.core.actions.base.common_action import CommonAction
from parallels.core.actions.utils.logging_properties import LoggingProperties
from parallels.core.logging import get_logger
from parallels.core.registry import Registry
from parallels.core.task_manager.manager import TaskManager, TaskOperations
from parallels.core.task_manager.task import Task
from parallels.core.utils.stop_mark import StopMark

logger = get_logger(__name__)


class CancelTaskAction(CommonAction):
    """Stop and remove specified task from the queue"""

    def get_description(self):
        """Get short description of action as string

        :rtype: str
        """
        return messages.ACTION_CANCEL_TASK_DESCRIPTION

    def get_failure_message(self, global_context):
        """Get message for situation when action failed

        :type global_context: parallels.core.global_context.GlobalMigrationContext
        """
        return messages.ACTION_CANCEL_TASK_FAILURE

    def get_logging_properties(self):
        """Get how action should be logged to migration tools end-user

        :rtype: parallels.core.actions.utils.logging_properties.LoggingProperties
        """
        return LoggingProperties(info_log=False)

    def run(self, global_context):
        """Run action

        :type global_context: parallels.core.global_context.GlobalMigrationContext
        """
        task_manager = TaskManager(global_context.session_files.get_file_path('tasks'))
        task_id = global_context.options.task_id

        with task_manager.task_operations() as task_operations:
            assert isinstance(task_operations, TaskOperations)
            for task in task_operations.get_all_tasks():
                if task.task_id != task_id:
                    continue

                if task_operations.get_task_status(task) == Task.STATUS_RUNNING:
                    # The task is running right now - stop it.
                    logger.finfo(messages.STOP_RUNNING_TASK, task_id=task.task_id)
                    # 1. Create a stop file.
                    # Task process (e.g. transfer-accounts, check, test-all, etc) checks
                    # periodically if stop file exists, and if so, shuts down as soon as possible.
                    StopMark.set()
                    migration_lock = Registry.get_instance().get_migration_lock()
                    # 2. Wait once the task process finishes.
                    # Task process holds the migration lock when it is running. Once it exists - the migration lock
                    # is released, and we can acquire the lock. Here we block till we acquire the lock.
                    migration_lock.acquire_block()
                    # 3. Remove the stop file, so the next tasks in the queue are not stopped by the file.
                    try:
                        StopMark.remove()
                    finally:
                        migration_lock.release()
                    logger.finfo(messages.TASK_STOPPED, task_id=task.task_id)
                else:
                    task_operations.set_task_status(task, Task.STATUS_CANCELLED)
                    logger.finfo(messages.TASK_CANCELLED, task_id=task.task_id)
