from parallels.common import messages
import shutil
import logging
import string

from parallels.common.actions.base.common_action import CommonAction
from parallels.common.actions.utils.logging_properties import LoggingProperties
from parallels.common.plesk_backup import plesk_backup_xml
from parallels.common.registry import Registry
from parallels.common.target_panels import TargetPanels

logger = logging.getLogger(__name__)


class CreateConverted(CommonAction):
	_prefix_id = 0

	def __init__(self, subscription_backup):
		"""Class constructor

		Arguments:
		- subscription_backup - object that can retrieve subscription backup
		object for each subscription, list of servers with backups
		"""
		self._subscription_backup = subscription_backup

	def get_description(self):
		return "Create converted backups"

	def get_failure_message(self, global_context):
		return messages.FAILED_CREATE_CONVERTED_BACKUPS

	def get_logging_properties(self):
		return LoggingProperties(info_log=False)

	def run(self, global_context):
		"""
		:type global_context: parallels.common.global_context.GlobalMigrationContext
		"""
		backup_ids = self._subscription_backup.get_backup_ids(global_context)
		for backup_id in backup_ids:
			src_filename = self._make_cut_backup(global_context, backup_id)
			converted_filename = global_context.session_files.get_path_to_converted_plesk_backup(backup_id)
			# clone source backup file to converted backup file
			shutil.copy2(src_filename, converted_filename)

	def _make_cut_backup(self, global_context, backup_id):
		"""Make the cut backup, according to the current migration list.
		If there is no migration list - return the path of uncut backup.
		Otherwise, make a copy of uncut backup, cut that copy and return the path to the cut copy.

		:type global_context: parallels.common.global_context.GlobalMigrationContext
		"""
		raw_filename = self._get_raw_backup_filename(backup_id)
		if global_context.migration_list_data is None:
			return raw_filename
		else:
			# 1. make a copy of the uncut backup
			cut_filename = global_context.session_files.get_path_to_cut_plesk_backup(backup_id)
			shutil.copy2(raw_filename, cut_filename)

			tmp_filename = "%s.tmp" % cut_filename

			# 2. extract the relevant logins and subscriptions from migration list
			relevant_logins = (
				list(global_context.migration_list_data.resellers) +
				list(global_context.migration_list_data.customers_mapping.keys())
			)
			relevant_subscriptions = global_context.migration_list_data.subscriptions_mapping.keys()

			def load_unfiltered_backup(backup_filename):
				return plesk_backup_xml.load_backup(
					backup_filename,
					None,
					# XXX expand-specific properties in common action
					is_expand_mode=global_context.migrator.is_expand_mode(),
					discard_mailsystem=global_context.migrator._is_mail_centralized(backup_id)
				)

			backup = load_unfiltered_backup(cut_filename)

			# certain subscription listed in migration list may belong to a client and/or reseller
			# not listed in that migration list,
			# and due to backup hierarchy, to reach this subscription we must leave this client and reseller in backup
			# 3. extract the relevant logins from backup, based on what other relevant
			# logins and subscriptions are under them.
			local_relevant_logins = []
			for sub, client, reseller in backup.iter_all_subscriptions_with_owner_and_reseller():
				if sub.name in relevant_subscriptions:
					if client is not None:
						local_relevant_logins.append(client.login)
					if reseller is not None:
						local_relevant_logins.append(reseller.login)

			for client, reseller in backup.iter_all_clients_with_owner():
				if client.login in relevant_logins:
					if reseller is not None:
						local_relevant_logins.append(reseller.login)

			# 4. cut from backup copy the subscriptions, clients and resellers not relevant to migration list
			backup.cut_irrelevant_subcriptions(relevant_subscriptions)
			backup.cut_irrelevant_clients(relevant_logins + local_relevant_logins)
			backup.cut_irrelevant_resellers(relevant_logins + local_relevant_logins)

			# 5. save modified backup copy
			with open(tmp_filename, 'wb') as backup_file:
				backup.save(
					backup_file,
					'zip' if (
						global_context.conn.target.is_windows
						and
						global_context.target_panel == TargetPanels.PPA_WEB_SN
					) else 'tar',

					# set unique prefix for each migrated backup to avoid issues when
					# working with backups created at the same time when restoring them
					# with PMM
					prefix='migr%s_info' % (string.ascii_lowercase[CreateConverted._prefix_id])
				)
				CreateConverted._prefix_id += 1
			shutil.copy2(tmp_filename, cut_filename)

			return cut_filename

	@staticmethod
	def _get_raw_backup_filename(backup_id):
		return Registry.get_instance().get_context().session_files.get_path_to_raw_plesk_backup(backup_id)