import os
import sys

from parallels.core import is_run_on_windows, get_logger, local_command_exec, MigratorInterruptException
from parallels.core.actions.base.common_action import CommonAction
from parallels.core.actions.utils.logging_properties import LoggingProperties
from parallels.core.registry import Registry
from parallels.core.task_manager.manager import TaskManager
from parallels.core.task_manager.stop_mark import DelayedStopMark
from parallels.core.task_manager.task_runner import TaskRunner
from parallels.core.utils import windows_utils, unix_utils
from parallels.core.utils.locks.file_lock import FileLock
from parallels.plesk.source.mail import messages


logger = get_logger(__name__)


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

        :rtype: str
        """
        return messages.ACTION_RUN_TASKS_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_RUN_TASKS_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.plesk.source.mail.global_context.MailGlobalMigrationContext
        """
        task_manager = TaskManager(global_context.session_files.get_file_path('tasks'))
        task_manager.run_tasks_parallel(MailMigrationTaskRunner(global_context))


class MailMigrationTaskRunner(TaskRunner):
    HARD_STOP_INTERVAL = 60  # 60 ticks, which is almost the same as 60 seconds

    def __init__(self, global_context):
        self._global_context = global_context

    def run(self, task):
        """
        :type task: parallels.core.task_manager.task.Task
        :rtype: None
        """
        lock = FileLock(task.task_dir, 'migration.lock')
        lock.acquire_block()
        try:
            stop_mark_file_path = os.path.join(task.task_dir, 'stop')
            migration_list_file_path = os.path.join(task.task_dir, 'migration-list.json')
            async_status_report_path = os.path.join(task.task_dir, 'async_status_report')
            task_status_path = os.path.join(task.task_dir, 'task-status.json')

            if os.path.exists(stop_mark_file_path):
                return

            args = [
                'transfer-mail',
                self._global_context.session_files.get_path_to_config_file(),
                '--mail-migration-list=%s' % migration_list_file_path,
                '--stop-mark-file=%s' % stop_mark_file_path,
                '--task-id=%s' % task.task_id
            ]
            # When async reporting is used, all migration status is written immediately to global
            # status file, not to per-task status file.
            # Otherwise, write task status to per-task status file till actual
            # transfer of messages begins.
            if not os.path.exists(async_status_report_path):
                args.append('--task-status=%s' % task_status_path)
            else:
                args.append('--async-status-report')

            if is_run_on_windows():
                command_str = command = windows_utils.format_command_list(
                    sys.executable,
                    [
                        sys.argv[0],
                        Registry.get_instance().get_var_dir()
                    ] + args
                )
            else:
                command_str = unix_utils.format_command_list(
                    sys.argv[0], args
                )
                command = ['/bin/sh', '-c', command_str]

            logger.finfo(messages.RUN_COMMAND, command=command_str)
            stop_mark = DelayedStopMark(stop_mark_file_path, self.HARD_STOP_INTERVAL)
            try:
                local_command_exec(command, stop_mark=stop_mark, stop_poll_interval=1)
            except MigratorInterruptException:
                logger.finfo(messages.COMMAND_TERMINATED)
            else:
                logger.finfo(messages.COMMAND_FINISHED)
        finally:
            lock.release()
