import os

from parallels.core import messages
from parallels.core.actions.base.common_action import CommonAction
from parallels.core.logging import get_logger
from parallels.core.migration_list.reader.json_reader import MigrationListReaderJSON
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.common import open_no_inherit
from parallels.core.utils.json_utils import read_json
from parallels.core.utils.migration_progress import SubscriptionMigrationStatus
from parallels.core.utils.stop_mark import StopMark
from parallels.core.utils.subscription_operations import command_name_to_operation

logger = get_logger(__name__)


class StopMigrationAction(CommonAction):
    """Stop all migrations running within current session

    This action:
    1) Stops currently running migration (commands like "transfer-accounts", "copy-content", etc).
    2) Stops queue runner ("run-tasks" command).
    3) Removes all queued subscriptions from the queue.

    This action works with the help of stop file and locks.
    By stop file we notify commands that they should terminate.
    By acquiring locks we ensure that commands were terminated.
    """
    def get_description(self):
        """Get short description of action as string

        :rtype: str
        """
        return messages.ACTION_STOP_MIGRATION_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_STOP_MIGRATION_FAILURE

    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'))

        # cancel all not running tasks, stop running task and wait till it finishes
        with task_manager.task_operations() as task_operations:
            assert isinstance(task_operations, TaskOperations)
            for task in task_operations.get_all_tasks():
                task_status = task_operations.get_task_status(task)
                if task_status == Task.STATUS_RUNNING:
                    task_operations.set_task_status(task, Task.STATUS_CANCELLED)
                    StopMark.set()
                    migration_lock = Registry.get_instance().get_migration_lock()
                    migration_lock.acquire_block()
                    try:
                        StopMark.remove()
                    finally:
                        migration_lock.release()
                elif task_status == Task.STATUS_READY or task == Task.STATUS_PREPARE:
                    task_operations.set_task_status(task, Task.STATUS_CANCELLED)
                    self._set_subscriptions_cancelled(global_context, task)

        # wait till task manager finishes
        task_manager.wait_for_shutdown()

    @staticmethod
    def _set_subscriptions_cancelled(global_context, task):
        """
        :type global_context: parallels.core.global_context.GlobalMigrationContext
        :type task: parallels.core.task_manager.task.Task
        :rtype: None 
        """
        migration_list_file = os.path.join(task.task_dir, 'migration-list.json')
        command_file = os.path.join(task.task_dir, 'command.json')

        if not os.path.isfile(command_file) or not os.path.exists(migration_list_file):
            return

        command_info = read_json(command_file)
        migration_list_reader = MigrationListReaderJSON(
            global_context.migrator.get_migration_list_source_data()
        )

        operation = command_name_to_operation(command_info['command'])

        if operation is None:
            return

        with open_no_inherit(migration_list_file, 'r') as fp:
            migration_list_data, _ = migration_list_reader.read(fp, subscriptions_only=True)

        for subscription_name in migration_list_data.subscriptions_mapping.keys():
            global_context.subscriptions_status.set_operation_status(
                subscription_name, operation, SubscriptionMigrationStatus.CANCELLED
            )
