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


class CreateResellerPlans(CommonAction):
	def get_description(self):
		"""
		:rtype: basestring
		"""
		return "Create admin's reseller plans"

	def get_failure_message(self, global_context):
		"""
		:type global_context: parallels.common.global_context.GlobalMigrationContext
		:rtype: basestring
		"""
		return "Failed to create admin's reseller plans"

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

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

	def run(self, global_context):
		"""
		:type global_context: parallels.common.global_context.GlobalMigrationContext
		:rtype: None
		"""
		report = Report("Admin's reseller plans migration status", 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.source_server_infos, 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,
							"Failed to create reseller plan '%s': %s" % (converted_plan.name, e)
						),
						"Check the log above and debug.log for more details.\n"
						"To continue migration, create the plan manually on target panel\n"
						"or assign resellers to another plan with the help of migration list."
					)

		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.common.global_context.GlobalMigrationContext
		:type plan_names: list[basestring]
		:rtype: list[parallels.plesk_api.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.common.global_context.GlobalMigrationContext
		:type converted_plan: parallels.target_panel_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.common.global_context.GlobalMigrationContext
		:type converted_plan: parallels.target_panel_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.common.global_context.GlobalMigrationContext
		:type report parallels.common.checking.Report
		"""
		if report.has_errors():
			global_context.migrator.print_report(
				report, "convert_report_tree", show_no_issue_branches=False
			)
			raise MigrationError(
				u"Unable to continue migration until there are no issues "
				u"at 'error' level when creating admin's reseller plans. "
				u"Please review the report tree above and fix the errors. "
			)
		elif report.has_errors_or_warnings():
			global_context.migrator.print_report(
				report, "convert_report_tree", show_no_issue_branches=False
			)