from parallels.ppa.source.pbas import messages
import logging
from xml.etree import ElementTree

from parallels.plesk.source.plesk.migrator import Migrator as PlesksMigrator

from parallels.core.utils.common import cached, obj, group_by_id

from .converter import Converter
from migration_list import MigrationList
from parallels.core.existing_objects_model import ExistingObjectsModel
from parallels.core.utils.yaml_utils import write_yaml
from parallels.ppa.source.pbas.client_subscription_converter import PBASToPPAClientSubscriptionConverterAdapter

logger = logging.getLogger(__name__)


class Migrator(PlesksMigrator):
	def _load_configuration(self):
		super(Migrator, self)._load_configuration()
		self.pbas_data_filename = self.config.get('GLOBAL', 'pbas-data-file')

	@cached
	def _get_pbas_data(self):
		"""Read PBAS data file
		Return list of Plesk accounts with information where to transfer their subscriptions.
		"""
		with open(self.pbas_data_filename) as source:
			root = ElementTree.ElementTree(file=source)

		accounts = {}
		# { plesk_login: ppa_login, ppa_st_id, ppa_group_name }
		for account_node in root.findall('Node/Vendor/Account'):
			ppa_login = account_node.attrib['PaLogin']
			for subscription_node in account_node.findall('Subscr'):
				plesk_login = subscription_node.attrib['PleskLogin']
				ppa_st_id = int(subscription_node.attrib['SrvTmplId'])
				ppa_group_name = subscription_node.attrib['PleskName']
				accounts[plesk_login] = obj(
					ppa_login=ppa_login, ppa_st_id=ppa_st_id, ppa_group_name=ppa_group_name, source=None
				)

		return accounts

	def _get_migration_list_source_data(self):
		"""Return source data for migration list creation/validation"""
		return obj(dump_iterator=lambda: self.iter_shallow_dumps(), accounts=self._get_pbas_data())

	@classmethod
	def _get_migration_list_class(cls):
		return MigrationList

	def _read_migration_list(self, options, migration_list_optional=False):
		mapping = super(Migrator, self)._read_migration_list(
			options, 
			# in PBAS migrator migration list is always optional, and in default scenario is not specified by customer
			migration_list_optional=True
		)
		if mapping is None:
			mapping, _ = self._get_migration_list_class().generate_migration_list(
				self._get_migration_list_source_data(),
				ppa_webspace_names=[], ppa_service_templates={},
			)
		return mapping

	def import_resellers(self):
		logger.warning(messages.PBAS_MIGRATOR_DOES_NOT_SUPPORT_IMPORT_RESELLERS)

	def _convert_resellers(self, existing_resellers, resellers_migration_list, report):
		return Converter.convert_resellers(
			self.global_context.get_source_servers_info(), existing_resellers,
			resellers_migration_list, report, self.global_context.password_holder
		)

	# we want _check() complain if plan or reseller does not exist in PPA
	# according to Plesk's migrator implementation, it will not complain, if finds plan or reseller on source Plesks
	# but since we do not convert source Plesks' plans and resellers, it will complain
	# so we don't have to redefine it here.

	def _convert_accounts(self, converted_resellers, report):
		converter_adapter = PBASToPPAClientSubscriptionConverterAdapter()
		return converter_adapter.convert(self.global_context, converted_resellers, report)

	@staticmethod
	def is_expand_mode():
		return True

	@staticmethod
	def _get_converter_class():
		return Converter

	def _fetch_target_existing_objects(self, reseller_logins, client_logins, plans, subscription_names, domain_names):
		"""In addition to fetching from PPA the objects according to migration list,
		fetch PBAS-specific data, such as service templates
		referred by identifier (which cannot be specified in migration list).
		"""
		target_existing_objects_plesk = super(Migrator, self)._fetch_target_existing_objects(
			reseller_logins,
			client_logins + [account_info.ppa_login for account_info in self._get_pbas_data().itervalues()],
			plans,
			subscription_names,
			domain_names
		)

		service_templates_plesk = group_by_id(target_existing_objects_plesk.service_templates, lambda st: st.st_id)

		service_templates_pbas = self._get_target_panel_api().get_service_template_info_list([
			account_info.ppa_st_id for account_info in self._get_pbas_data().itervalues()
			if account_info.ppa_st_id not in service_templates_plesk
		])

		target_existing_objects = ExistingObjectsModel(
			resellers=target_existing_objects_plesk.resellers,
			customers=target_existing_objects_plesk.customers,
			# the difference is below
			service_templates=target_existing_objects_plesk.service_templates + service_templates_pbas,
			addon_service_templates=target_existing_objects_plesk.addon_service_templates,
			raw_target_subscriptions=target_existing_objects_plesk.raw_ppa_subscriptions,
			webspaces=target_existing_objects_plesk.webspaces,
			auxiliary_user_roles=target_existing_objects_plesk.auxiliary_user_roles,
			auxiliary_users=target_existing_objects_plesk.auxiliary_users,
			plesk_subscriptions=target_existing_objects_plesk.plesk_subscriptions,
			plesk_sites=target_existing_objects_plesk.plesk_sites,
			plesk_site_aliases=target_existing_objects_plesk.plesk_site_aliases,
			plesk_subdomains=target_existing_objects_plesk.plesk_subdomains,
			ip_addresses=target_existing_objects_plesk.ip_addresses,
		)

		# XXX existing objects should not be stored into a file
		target_filename = self.session_files.get_path_to_existing_objects_model()
		write_yaml(target_filename, target_existing_objects)
		return target_existing_objects
