import os

from parallels.core import get_logger, MigrationError
from parallels.core.runners.base import BaseRunner
from parallels.core.runners.entities import ExecutionOptions
from parallels.core.utils.common import is_empty_list
from parallels.core.utils.common.threading_utils import execute_once_synchronized_by_args
from parallels.core.utils.pmm.agent import DumpAll, DumpSelected
from parallels.plesk.source.cpanel import messages
from parallels.plesk.source.legacy.pmm_agent import UnixPmmMigrationAgent

logger = get_logger(__name__)


class CpanelPmmMigrationAgent(UnixPmmMigrationAgent):
    def __init__(self, global_context, server, migrator_pmm_dir, settings):
        super(CpanelPmmMigrationAgent, self).__init__(global_context, server, migrator_pmm_dir, settings)

    @execute_once_synchronized_by_args
    def get_mysql_credentials(self):
        with self._source_server.runner() as runner:
            assert isinstance(runner, BaseRunner)
            return runner.execute_command(
                u'cd {path}; {perl} getMysqlCredentials.pl', dict(
                    perl=self._perl_bin,
                    path=self.agent_dir
                ),
                # do not log password due to security consideration
                ExecutionOptions(
                    log_output=False
                )
            ).stdout.strip().split("\n")

    def get_vhosts_dir(self, sysuser):
        with self._source_server.runner() as runner:
            assert isinstance(runner, BaseRunner)
            return runner.execute_command(u'cd {path}; {perl} getVhostsDir.pl --sysuser {sysuser}', dict(
                perl=self._perl_bin,
                path=self.agent_dir,
                sysuser=sysuser
            )).stdout.strip()

    @property
    def shallow_dump_log(self):
        return os.path.join(self._get_pmm_session_dir(), 'shallow-dump.log')

    def _run_shallow(self, dump_xml_filename):
        """Run migration agent on a source panel and download the results.

        Execute the agent which puts results into XML dump file.
        Download dump XML file and log file to specified locations:
        - dump_xml_filename - local filename to put dump XML to
        - dump_log_filename - local filename to put dump log to

        Raise MigrationError in case of any failure.
        """
        self._run_script(
            script_filename=self.settings.agent_script_name,
            local_data_filename=dump_xml_filename, remote_data_filename='shallow_dump.xml',
            local_log_filename=self.shallow_dump_log, remote_log_filename='shallow_dump.log',
            log_step_info_msg=messages.LOG_STEP_CREATE_SOURCE_PANEL_SHALLOW_DUMP_XML,
            log_execute_msg=messages.EXECUTE_CREATE_SHALLOW_DUMP_XML,
            log_download_data_file_msg=messages.LOG_DOWNLOAD_SHALLOW_DUMP_LOG,
            log_download_log_file_msg=messages.LOG_SHALLOW_DUMP_XML_TO,
            error_run_script_msg=messages.ERROR_CREATING_SHALLOW_DUMP,
            error_download_log_msg=messages.ERROR_DOWNLOADING_SHALLOW_DUMP_LOG,
            additional_args=['--shallow-dump']
        )

    def _run(self, dump_xml_filename, dump_log_filename, selection=DumpAll()):
        """Run migration agent on a source panel and download the results.

        Execute the agent which puts results into XML dump file.
        Download dump XML file and log file to specified locations:
        - dump_xml_filename - local filename to put dump XML to
        - dump_log_filename - local filename to put dump log to

        Raise MigrationError in case of any failure.
        """
        self._run_script(
            script_filename=self.settings.agent_script_name,
            local_data_filename=dump_xml_filename, remote_data_filename='dump.xml',
            local_log_filename=dump_log_filename, remote_log_filename='dump.log',
            log_step_info_msg=messages.LOG_STEP_CREATE_SOURCE_PANEL_DUMP_XML,
            log_execute_msg=messages.EXECUTE_CREATE_DUMP_XML,
            log_download_data_file_msg=messages.LOG_DOWNLOAD_DUMP_LOG,
            log_download_log_file_msg=messages.LOG_DUMP_XML_TO,
            error_run_script_msg=messages.ERROR_CREATING_DUMP,
            error_download_log_msg=messages.ERROR_DOWNLOADING_DUMP_LOG,
            additional_args=self._get_selection_cli_additional_args(selection)
        )

    def _run_script(
        self,
        script_filename,
        local_data_filename, remote_data_filename,
        local_log_filename, remote_log_filename,
        log_step_info_msg, log_execute_msg,
        log_download_data_file_msg,
        log_download_log_file_msg,
        error_run_script_msg,
        error_download_log_msg,
        additional_args=None
    ):
        logger.info(log_step_info_msg)

        try:
            with self._source_server.runner() as runner:
                assert isinstance(runner, BaseRunner)
                logger.debug(log_execute_msg)
                try:
                    format_string_elements = []
                    args_dict = dict(
                        perl=self._perl_bin,
                        path=self.agent_dir,
                        script=script_filename,
                        log=remote_log_filename,
                    )
                    if additional_args:
                        for i, arg in enumerate(additional_args):
                            element_name = 'arg_%i' % i
                            format_string_elements.append('{%s}' % element_name)
                            args_dict[element_name] = arg
                    runner.execute_command(
                        u'cd {path}; {perl} {script} ' +
                        u' '.join(format_string_elements) +
                        u' --configuration-only --server -v 5 > {log} 2>&1',
                        args_dict
                    )
                    logger.debug(
                        log_download_data_file_msg, local_data_filename
                    )
                    runner.get_file(
                        os.path.join(
                            self.agent_dir,
                            remote_data_filename
                        ),
                        local_data_filename
                    )
                finally:  # always try to download log, even if script failed
                    try:
                        logger.debug(
                            log_download_log_file_msg, local_log_filename
                        )
                        runner.get_file(
                            os.path.join(
                                self.agent_dir,
                                remote_log_filename
                            ),
                            local_log_filename
                        )
                    except Exception as e:
                        logger.exception()
                        logger.error(error_download_log_msg % e)

        except Exception as e:
            logger.exception()
            raise MigrationError(error_run_script_msg % e)

    @staticmethod
    def _get_selection_cli_additional_args(selection):
        args = []
        if isinstance(selection, DumpAll):
            args.append('--dump-all')
        elif isinstance(selection, DumpSelected):
            if not is_empty_list(selection.domains):
                args.append('--dump-domains='+",".join(selection.domains))
            if not is_empty_list(selection.clients):
                args.append('--dump-clients='+",".join(selection.clients))
            if not is_empty_list(selection.resellers):
                args.append('--dump-resellers='+",".join(selection.resellers))
        return args

    def _is_dump_agent_creates_archive(self):
        """Whether dump agent creates ready to use dump archive (ZIP or TAR). If False then agent creates plain XML.

        :rtype: bool
        """
        return False

    def _is_dump_version_upgrade_required(self):
        """Whether dump agent creates dump in old format (< 17.0), and upgrade is required to use with target Plesk 18

        :rtype: bool
        """
        return True
