from parallels.plesk import messages
from parallels.core import MigrationError
from parallels.core.actions.base.common_action import CommonAction
from parallels.core.actions.utils.logging_properties import LoggingProperties
from parallels.core.checking import Report, Problem
from parallels.core.converter.business_objects.common import SOURCE_TARGET
from parallels.plesk.utils.xml_rpc.plesk.core import filter_object_not_found_error, get_result_set_data
from parallels.plesk.utils.xml_rpc.plesk.operator.reseller_plan import \
	ResellerPlanOperator, ResellerPlanLimit, ResellerPlanPermission, ResellerPlanLimits, ResellerPlanApsPackage
from parallels.plesk.converter.reseller_plan import ResellerPlanConverter


class CreateResellerPlans(CommonAction):
	def get_description(self):
		"""
		:rtype: basestring
		"""
		return messages.ACTION_CREATE_RESELLER_PLANS_DESCRIPTION

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

	def get_logging_properties(self):
		"""Get how action should be logged to migration tools end-user

		:rtype parallels.core.actions.utils.logging_properties.LoggingProperties
		"""
		return LoggingProperties(info_log=True, compound=False)

	def run(self, global_context):
		"""
		:type global_context: parallels.core.global_context.GlobalMigrationContext
		:rtype: None
		"""
		report = Report(messages.RESELLER_PLANS_CONVERSION_REPORT_TITLE, None)
		migration_list_data = global_context.migrator._read_migration_list_resellers(global_context.options)
		plan_names = set(migration_list_data.values()) - {None}
		target_plans = self._fetch_target_plans(global_context, plan_names)
		converted_plans = ResellerPlanConverter().convert_plans(
			global_context.get_source_servers_info(), target_plans, plan_names, report
		)
		for converted_plan in converted_plans:
			if converted_plan.source != SOURCE_TARGET:
				try:
					self._create_plan(global_context, converted_plan)
				except Exception as e:
					report.add_issue(
						Problem(
							'failed_to_create_reseller_plan', Problem.ERROR,
							messages.FAILED_TO_CREATE_RESELLER_PLAN_ISSUE % (converted_plan.name, e)
						),
						messages.FAILED_TO_CREATE_RESELLER_PLAN_SOLUTION
					)

		self._check_report_errors(global_context, report)

	@staticmethod
	def _fetch_target_plans(global_context, plan_names):
		"""Fetch already existing admin's reseller plans from target panel

		:type global_context: parallels.core.global_context.GlobalMigrationContext
		:type plan_names: list[basestring]
		:rtype: list[parallels.plesk.utils.xml_rpc.plesk.operator.reseller_plan.ResellerPlanInfo]
		"""
		if len(plan_names) == 0:
			return []

		plesk_api = global_context.conn.target.plesk_api()
		request = ResellerPlanOperator.Get(
			filter=ResellerPlanOperator.FilterByName(plan_names)
		)
		response = plesk_api.send(request)
		target_plans = get_result_set_data(filter_object_not_found_error(response))
		return target_plans

	def _create_plan(self, global_context, converted_plan):
		"""Create admin's reseller plan on target server

		:type global_context: parallels.core.global_context.GlobalMigrationContext
		:type converted_plan: parallels.plesk.models.target_data_model.AdminResellerPlan
		:rtype: None
		"""
		plesk_api = global_context.conn.target.plesk_api()
		plesk_api.send(
			ResellerPlanOperator.Add(
				name=converted_plan.name,
				limits=ResellerPlanLimits(
					overuse=converted_plan.overuse,
					oversell=converted_plan.oversell,
					limits=[
						ResellerPlanLimit(name, value)
						for name, value in converted_plan.limits.iteritems()
					]
				),
				permissions=[
					ResellerPlanPermission(name, value)
					for name, value in converted_plan.permissions.iteritems()
				]
			)
		).check()
		self._create_aps_bundle_filter(global_context, converted_plan)

	@staticmethod
	def _create_aps_bundle_filter(global_context, converted_plan):
		"""
		:type global_context: parallels.core.global_context.GlobalMigrationContext
		:type converted_plan: parallels.plesk.models.target_data_model.AdminResellerPlan
		:rtype: None
		"""
		plesk_api = global_context.conn.target.plesk_api()

		if converted_plan.aps_bundle_filter is None:
			return

		if converted_plan.aps_bundle_filter.type != 'white':
			# Other types of APS filters are not supported
			return

		api_filter = ResellerPlanOperator.FilterByName([converted_plan.name])
		enable_aps_filter_request = ResellerPlanOperator.EnableApsFilter(
			filter=api_filter
		)
		plesk_api.send(enable_aps_filter_request).check()
		add_package_request = ResellerPlanOperator.AddPackage(
			filter=api_filter,
			packages=[
				ResellerPlanApsPackage(name=package.name, value=package.value)
				for package in converted_plan.aps_bundle_filter.items
			]
		)
		plesk_api.send(add_package_request).check()

	@staticmethod
	def _check_report_errors(global_context, report):
		"""Check report and exit in case of errors

		:type global_context: parallels.core.global_context.GlobalMigrationContext
		:type report parallels.core.checking.Report
		"""
		if report.has_errors():
			global_context.migrator.print_report(
				report, "convert_report_tree", show_no_issue_branches=False
			)
			raise MigrationError(
				messages.CREATE_RESELLER_PLAN_CONVERSION_ERRORS)
		elif report.has_errors_or_warnings():
			global_context.migrator.print_report(
				report, "convert_report_tree", show_no_issue_branches=False
			)
