from parallels.plesk.source.plesk import messages
import logging

from parallels.core.actions.base.common_action import CommonAction
from parallels.core.logging_context import log_context
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.pmm_agent import create_pmm_agent

logger = logging.getLogger(__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.info(messages.LOG_USING_CACHED_DUMP.format(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()
