import logging
from collections import defaultdict
from contextlib import closing
from itertools import chain

from parallels.common import MigrationError
from parallels.common import target_data_model as ppa
from parallels.target_panel_plesk.import_api.import_api_base import PleskPlanSettings
from parallels.common.utils.plesk_limits_permissions import PLESK_LIMIT_NAMES, PLESK_PERMISSION_NAMES

class PlansConverter(object):
	"""Generate list of plans ready to import to target panel.
	
	Plans converter takes the following information:
	- Plesk backup or other source of information 
	about resellers and plans on source servers
	- Import API which provides information about 
	resellers and plans that already exist on target panel
	- Migration list, which contains list of plans that 
	will be used during migration

	Plans converter:
	- Converts each plan we need to migrate to 
	format ready to import to target panel.
	- Performs conflict resolution, if plan exists on multiple
	source panel, or already exists on target panel.

	Result is a list of plans ready to import to target panel.
	"""

	logger = logging.getLogger(u'%s.Converter' % __name__)

	def convert_plans(self, source_panels, import_api, plan_migration_list, convert_settings_function):
		"""Arguments:
		source_panels - list of source panels, each item is an object with:
			- attribute 'id' 
			- method load_raw_backup() to read Plesk backup of the panel
		import_api - instance of parallels.common.import_api.import_api.ImportAPI
		plan_migration_list - dictionary with key - reseller login, value - list of service templates to create
			for example: {None: ['Plan 1', 'Plan 2'], 'res1': ['Plan 3']} means that we need to have
			'Plan 1' and 'Plan 2' plans for administrator and 'Plan 3' plan for reseller 'res1'
		convert_settings_function - function capable to convert plan settings from Plesk backup format to
			import api client format
		"""
		self.logger.info(u"Convert service plans")

		all_plans = defaultdict(lambda: defaultdict(list))

		target_all_admin_plans = chain(
			import_api.get_service_template_list(None), 
			import_api.get_addon_service_template_list(None)
		)
		for plan in target_all_admin_plans:
			all_plans[None][plan.name].append(
				ppa.Plan(name=plan.name, source='target', is_addon=None, settings=None)
			)
		target_resellers = import_api.list_resellers([
			reseller 
			for reseller in plan_migration_list.iterkeys() 
			if reseller is not None
		])
		for reseller in target_resellers:
			target_all_reseller_plans = chain(
				import_api.get_service_template_list(reseller.id), 
				import_api.get_addon_service_template_list(reseller.id)
			)
			for plan in target_all_reseller_plans:
				all_plans[reseller.contact.username][plan.name].append(
					ppa.Plan(name=plan.name, source='target', is_addon=None, settings=None)
				)

		for source_panel in source_panels:
			with closing(source_panel.load_raw_backup()) as backup:
				backup_all_admin_plans = chain(
					backup.get_plans(), 
					backup.get_addon_plans()
				)
				for plan in backup_all_admin_plans:
					all_plans[None][plan.name].append(
						ppa.Plan(
							name=plan.name, 
							source=source_panel.id, 
							is_addon=plan.is_addon, 
							settings=convert_settings_function(plan)
						)
					)
				for reseller in backup.iter_resellers():
					backup_all_reseller_plans = chain(
						backup.iter_reseller_plans(reseller.login), 
						backup.iter_reseller_addon_plans(reseller.login)
					)
					for plan in backup_all_reseller_plans:
						all_plans[reseller.login][plan.name].append(
							ppa.Plan(
								name=plan.name, 
								source=source_panel.id, 
								is_addon=plan.is_addon, 
								settings=convert_settings_function(plan)
							)
						)

		merged_plans = defaultdict(dict)
		for reseller, plans in plan_migration_list.iteritems():
			for plan in plans:
				converted_plans = all_plans.get(reseller, {}).get(plan, [])
				if len(converted_plans) == 0:
					raise MigrationError("Plan '%s' specified in migration list file does not exist" % (plan,)) 
				else:
					first_converted_plan = converted_plans[0]
					if len(converted_plans) > 1:
						self.logger.debug("Plan '%s' exists on multiple panels. Plan from '%s' will be used", plan, first_converted_plan.source)
					merged_plans[reseller][plan] = first_converted_plan

		return merged_plans

	@staticmethod
	def convert_plan_settings_from_plesk_backup_to_plesk_import_api(backup_plan):
		return PleskPlanSettings(
			limits={
				limit_name: backup_plan.properties[limit_name]
				for limit_name in PLESK_LIMIT_NAMES 
				if limit_name in backup_plan.properties
			},
			permissions={
				permission_name: backup_plan.properties[permission_name]
				for permission_name in PLESK_PERMISSION_NAMES 
				if permission_name in backup_plan.properties
			},
		)
