import logging
from collections import defaultdict
import os

from parallels.common.actions.base.common_action import CommonAction
from parallels.common.utils import plesk_utils
from parallels.common.context import log_context
from parallels.common.utils.windows_utils import cmd_command
from parallels.common.utils.windows_utils import path_join as windows_path_join

logger = logging.getLogger(__name__)

class Transfer(CommonAction):
	def run(self, global_context):
		safe = global_context.safe

		logger.info(u"Collecting information about Web Presense Builder sites")
		wpb_sites = self._list_wpb_sites(global_context)

		domain_num = 0
		total_domain_num = len([
			site 
			for plesk_sites in wpb_sites.itervalues()
			for subscription_sites in plesk_sites.itervalues()
			for site in subscription_sites
		])
		
		with global_context.conn.target.main_node_runner() as runner_ppa:
			for plesk_id, plesk_wpb_sites in wpb_sites.iteritems():
				source_server = global_context.conn.get_source_node(plesk_id)
				is_windows = source_server.is_windows()
				with source_server.runner() as runner_source:
					for subscription_name, subscription_wpb_sites in plesk_wpb_sites.iteritems():
						for domain_name, sitebuilder_site_id in subscription_wpb_sites:
							domain_num += 1
							with safe.try_subscription(subscription_name, u"Failed to transfer Web Presense Builder site for domain '%s'." % (domain_name,), is_critical=False):
								with log_context(domain_name):

									logger.info(u"Transfer Web Presense Builder site for domain '%s' (#%d out of #%d)", domain_name, domain_num, total_domain_num)

									base_filename = u'wpb_%s_%s.zip' % (plesk_id, domain_name)
									source_filename = source_server.get_session_file_path(base_filename)
									local_filename = global_context.migrator_server.get_session_file_path(base_filename)
									ppa_filename = global_context.conn.target.main_node_session_file_path(base_filename)

									logger.debug(u"Backup Web Presense Builder site on the source node")
									if is_windows:
										source_plesk_dir = plesk_utils.get_windows_plesk_dir(runner_source)
										runner_source.sh(
											ur'cmd.exe /c "{php} -dauto_prepend_file="" {bru} backup --target=site --uuid={site_id} --file={filename}"',
											dict(
												php=ur"%s\admin\bin\php" % (source_plesk_dir,),
												bru=u"%s\\sb\\utils\\bru.php" % (source_plesk_dir,),
												source_plesk_dir=source_plesk_dir, 
												site_id=sitebuilder_site_id, filename=source_filename
											)
										)
									else:
										source_plesk_dir = plesk_utils.get_unix_product_root_dir(runner_source)
										runner_source.sh(
											u'{plesk_dir}/bin/sw-engine-pleskrun /usr/local/sb/utils/bru.php backup --target=site --uuid={site_id} --file={filename}',
											dict(
												plesk_dir=source_plesk_dir, 
												site_id=sitebuilder_site_id, 
												filename=source_filename
											)
										)

									logger.debug(u"Download backup file to local server")
									runner_source.get_file(source_filename, local_filename)
									runner_source.remove_file(source_filename) # remove immediately as all WPB sites could take a lot of disk space
									logger.debug(u"Upload backup file to target panel management node")
									runner_ppa.upload_file(local_filename, ppa_filename)
									os.remove(local_filename)

									logger.debug(u"Restore Web Presense Builder site on target panel management node")
									if global_context.conn.target.is_windows:
										target_plesk_dir = plesk_utils.get_windows_plesk_dir(runner_ppa)
										runner_ppa.sh(
											cmd_command('{php} -dauto_prepend_file="" {bru} restore --target=site --uuid={site_id} --file={filename} --use_defaults'),
											dict(
												php=ur"%s\admin\bin\php" % (target_plesk_dir,),
												bru=u"%s\\sb\\utils\\bru.php" % (target_plesk_dir,),
												source_plesk_dir=target_plesk_dir, 
												site_id=sitebuilder_site_id, filename=ppa_filename
											)
										)
										runner_ppa.remove_file(ppa_filename)
										plesk_bin_domain=windows_path_join(target_plesk_dir, "bin/domain")
									else:
										target_plesk_dir = plesk_utils.get_unix_product_root_dir(runner_ppa)
										runner_ppa.sh(
											u'{plesk_dir}/bin/sw-engine-pleskrun /usr/local/sb/utils/bru.php restore --target=site --uuid={site_id} --file={filename} --use_defaults',
											dict(plesk_dir=target_plesk_dir, site_id=sitebuilder_site_id, filename=ppa_filename)
										)
										runner_ppa.remove_file(ppa_filename)
										plesk_bin_domain="%s/bin/domain" % target_plesk_dir
	
									logger.debug(u"Restore relation between Plesk domain and Web Presense Builder site")
									runner_ppa.sh(
										u'{plesk_bin_domain} --update {domain_name} -publish-sb-site true -sb-site-id {site_id}',
										dict(plesk_bin_domain=plesk_bin_domain, domain_name=domain_name, site_id=sitebuilder_site_id)
									)
	def _list_wpb_sites(self, global_context):
		""" Returns the following structure:
		  wpb_sites[source_plesk_id][subscription_name] = [
			  (subscription_name, site_id), 
			  (subdomain1_name, site_id), (subdomain2_name, site_id), ..., 
			  (addondomain1_name, site_id), (addondomain2_name, site_id), ...
		  ]
		  where site_id is an id of WPB site for subscription/addon domain/subdomain
		  the function guarantees that site_id is not None
		"""

		safe = global_context.safe
		wpb_sites = defaultdict(lambda: defaultdict(list))

		for subscription in global_context.iter_all_subscriptions():
			with safe.try_subscription(subscription.name, u"Failed to read information about Web Presense Builder sites.", is_critical=False):
				for site in subscription.converted_backup.iter_domains():
					if site.sitebuilder_site_id is not None:
						wpb_sites[subscription.model.source][subscription.name].append((site.name, site.sitebuilder_site_id))

		return wpb_sites
