from parallels.core.logging import get_logger
from parallels.core.logging import log_context
from parallels.core.actions.base.common_action import CommonAction
from parallels.core.utils.pmm.agent import DumpSelected, DumpAll
from parallels.core.utils.yaml_utils import read_yaml, write_yaml
from parallels.plesk.source.plesk import messages
from parallels.plesk.source.plesk.pmm_agent.utils import create_pmm_agent

logger = get_logger(__name__)


class FetchBackupActionBase(CommonAction):
    def run(self, global_context):
        """
        :type global_context: parallels.core.global_context.GlobalMigrationContext
        """
        for source_id in self._get_source_servers(global_context):
            with log_context(source_id), global_context.migrator_server.runner() as local_runner:
                self._fetch_dump(global_context, local_runner, source_id)

    @classmethod
    def _fetch_dump(cls, global_context, local_runner, source_id):
        if global_context.migration_list_data is None:
            selection = DumpAll()
        else:
            selection = DumpSelected(
                resellers=set(global_context.migration_list_data.resellers),
                clients=set(global_context.migration_list_data.customers_mapping.keys()),
                domains=set(global_context.migration_list_data.subscriptions_mapping.keys())
            )
        selection_filename = cls._get_selection_filename(global_context, source_id)
        if local_runner.file_exists(selection_filename):
            previous_selection = read_yaml(selection_filename)
        else:
            previous_selection = None

        dump_filename = cls._get_dump_filename(global_context, source_id)
        if (
            previous_selection is not None and
            previous_selection.covers(selection) and
            local_runner.file_exists(dump_filename) and
            not global_context.options.reload_source_data
        ):
            logger.finfo(messages.LOG_USING_CACHED_DUMP, dump_filename=dump_filename, source_id=source_id)
        else:
            if local_runner.file_exists(selection_filename):
                local_runner.remove_file(selection_filename)
            global_context.dump_agent = create_pmm_agent(global_context, global_context.conn.get_source_node(source_id))
            cls._create_dump(global_context.dump_agent, dump_filename, selection)
            write_yaml(selection_filename, selection)

    @staticmethod
    def _create_dump(agent, dump_filename, selection):
        """
        Create dump of specified objects via agent
        :type agent: parallels.core.utils.pmm.agent.PmmMigrationAgentBase
        :type dump_filename: str
        :type selection: parallels.core.utils.pmm.agent.DumpAll | parallels.core.utils.pmm.agent.DumpSelected
        """
        agent.create_dump(dump_filename, selection=selection)

    @staticmethod
    def _get_dump_filename(global_context, source_id):
        """
        Return filename there fetched dump will be stored
        :type global_context: parallels.core.global_context.GlobalMigrationContext
        :type source_id: str
        :rtype: str
        """
        return global_context.session_files.get_raw_dump_filename(source_id)

    @staticmethod
    def _get_selection_filename(global_context, source_id):
        """
        Return filename there previously fetched dump selection was cached
        :type global_context: parallels.core.global_context.GlobalMigrationContext
        :type source_id: str
        :rtype: str
        """
        return global_context.session_files.get_path_to_raw_plesk_backup_selection(source_id)

    @staticmethod
    def _get_source_servers(global_context):
        """
        :type global_context: parallels.core.global_context.GlobalMigrationContext
        """
        raise NotImplementedError()


class FetchBackupAction(FetchBackupActionBase):
    def get_description(self):
        return messages.FETCH_CONFIGURATION_DATA_FROM_PLESK_SERVERS

    def get_failure_message(self, global_context):
        """
        :type global_context: parallels.core.global_context.GlobalMigrationContext
        """
        return messages.FAILED_FETCH_CONFIGURATION_DATA_FROM_PLESK

    @staticmethod
    def _get_source_servers(global_context):
        """
        :type global_context: parallels.core.global_context.GlobalMigrationContext
        """
        return global_context.conn.get_source_plesks()
