from parallels.plesk.source.expand import messages
from collections import defaultdict
from parallels.core.checking import Problem
from parallels.core.converter.business_objects.common import EntityConverter
from parallels.core.logging_context import log_context
from parallels.core.utils.common import group_by_id, format_list


class ExpandToPleskSubscriptionConverter(object):
	def __init__(self, ip_mapper):
		self._ip_mapper = ip_mapper

	def convert(self, sources_info, existing_objects, subscription_mapping, model, plain_report):
		"""
		:type existing_objects: parallels.core.existing_objects_model.ExistingObjectsModel
		:type subscription_mapping: dict[basestring, parallels.core.migration_list.SubscriptionMappingInfo]
		:type model: parallels.core.target_data_model.Model
		:type plain_report: parallels.core.checking.SubscriptionReportProvider
		"""
		converted_subscriptions, failed_subscriptions = self._convert_subscriptions_plain(sources_info, plain_report)

		clients_by_login = {}

		for client in model.clients.itervalues():
			clients_by_login[client.login] = client
		for reseller in model.resellers.itervalues():
			for client in reseller.clients:
				clients_by_login[client.login] = client

		for subscription_name, mapping in subscription_mapping.iteritems():
			if subscription_name in failed_subscriptions:
				continue

			converted_subscription = converted_subscriptions.get(subscription_name)

			if converted_subscription is None:
				plain_report.get_root_report().add_issue(
					Problem(
						"subscription_does_not_exist", Problem.ERROR,
						messages.SUBSCRIPTION_DOES_NOT_EXIST_ON_SOURCE % subscription_name,
					),
					messages.SOLUTION_CORRECT_SUBSCRIPTION_NAME)
				continue

			subscription_report = plain_report.get_subscription_report(
				converted_subscription.source, subscription_name
			)

			if mapping.owner not in clients_by_login:
				subscription_report.add_issue(
					Problem(
						"owner_does_not_exist", Problem.ERROR,
						messages.CUSTOMER_OR_RESELLER_DOES_NOT_EXIST % mapping.owner,
					),
					messages.CREATE_CUSTOMER_OR_RESELLER_MANUALLY)
				continue

			client = clients_by_login[mapping.owner]
			client.subscriptions.append(converted_subscription)

		webspaces_by_name = group_by_id(existing_objects.webspaces, lambda ws: ws.name)

		def does_not_have_hosting_on_target(subscr):
			return (
				subscr.name not in webspaces_by_name or
				webspaces_by_name[subscr.name].htype == 'none'
			)

		new_subscriptions = list()
		for client in model.clients.itervalues():
			for subscription in client.subscriptions:
				if does_not_have_hosting_on_target(subscription):
					new_subscriptions.append((None, client.login, subscription))
		for reseller in model.resellers.itervalues():
			for client in reseller.clients:
				for subscription in client.subscriptions:
					if does_not_have_hosting_on_target(subscription):
						new_subscriptions.append((reseller.login, client.login, subscription))

		sources_by_id = group_by_id(sources_info, lambda s: s.id)
		backups = {
			subscription.name: sources_by_id[subscription.source].load_raw_dump()
			for subscription in converted_subscriptions.itervalues()
		}
		backup_subscriptions = {
			name: backup.get_subscription(name) for name, backup in backups.iteritems()
		}
		self._ip_mapper.map_ip_addresses(
			backups, new_subscriptions, backup_subscriptions, subscription_mapping, plain_report
		)

	@staticmethod
	def _convert_subscriptions_plain(sources_info, plain_report):
		"""Perform direct conversion: backup subscription to target model subscription
		:type sources_info: list[parallels.core.global_context.SourceInfo]
		:type plain_report: parallels.core.checking.SubscriptionReportProvider
		"""
		converted_subscriptions = defaultdict(list)
		failed_subscriptions = set()

		for source_info in sources_info:
			with log_context(source_info.id):
				backup = source_info.load_raw_dump()
				for subscription in backup.iter_all_subscriptions():
					model_subscription = EntityConverter(None).create_subscription_from_plesk_backup_subscription(
						subscription, source_info.id, source_info.is_windows
					)
					model_subscription.group_name = model_subscription.name

					converted_subscriptions[subscription.name].append(model_subscription)

		merged_subscriptions = {}

		for name, subscriptions in converted_subscriptions.iteritems():
			if len(subscriptions) == 1:
				subscription = subscriptions[0]
				merged_subscriptions[subscription.name] = subscription
			else:
				plain_report.get_root_report().add_issue(
					Problem(
						"subscription_on_multiple_sources", Problem.ERROR,
						messages.SUBSCRIPTION_EXISTS_ON_MULTIPLE_SOURCE_PANELS % (
							name,
							format_list([s.source for s in subscriptions]),
						)
					),
					messages.SOLUTION_SPECIFY_THE_ONLY_SERVER_TO_MIGRATE_SUBSCRIPTION)
				failed_subscriptions.add(name)

		return merged_subscriptions, failed_subscriptions
