from parallels.plesk.source.plesk import messages
import logging

import parallels.plesk.config as plesk_config
from parallels.plesk.utils.xml_rpc.plesk import operator as plesk_ops
from parallels.core.utils.common import cached
from parallels.core.utils.common import merge_dicts
from parallels.core import MigrationError
from parallels.plesk.source.plesk.server import PleskSourceServer
from parallels.core.connections.checker import PleskConnectionCheckError
from parallels.core.connections.checker import ConnectionChecker
from parallels.core.utils.migrator_utils import version_to_tuple
from parallels.core.connections.connections import Connections

logger = logging.getLogger(__name__)


class PleskMigratorConnections(Connections):
	def __init__(self, global_context, target_panel, migrator_server):
		super(PleskMigratorConnections, self).__init__(global_context, target_panel)

		self._migrator_server = migrator_server

		self._source_plesks = self._read_plesk_settings_from_config(
			global_context.config, 'source-servers'
		)
		for settings in self.get_source_plesks().itervalues():
			logger.info(messages.LOG_SOURCE_PLESK_HOST, settings.id, settings.ip)

	@cached
	def get_source_node(self, node_id):
		all_servers = merge_dicts(
			self.get_source_plesks(), self.get_external_db_servers()
		)
		node_settings = all_servers[node_id]
		return PleskSourceServer(
			node_id, node_settings, self._migrator_server
		)

	def get_information_servers(self):
		return self._source_plesks

	def get_source_plesks(self):
		"""Get information about source Plesk servers
		
		Returns dictionary {server_id: server_settings} with connection
		information.
		"""
		return self._source_plesks

	def get_dns_servers(self):
		"""Get information about source DNS servers
		
		Return dictionary {server_id: server_settings} with connection
		information for each DNS server.
		"""
		# For Plesk, DNS servers works on the same server
		return self._source_plesks

	def check_source_servers(self):
		""" Verify remote shell, Plesk API connections to source servers."""
		super(PleskMigratorConnections, self).check_source_servers()
		self._check_connections_plesk_servers()
		self._check_source_version()

	def _check_connections_plesk_servers(self):
		"""Check connections (SSH/Samba/Plesk API) to source Plesk servers"""

		check_list = []
		for source_id in self.get_source_plesks():
			check_list.append((
				self.get_source_node(source_id),
				["ssh_auth", "windows_auth"]
			))
		self._check_connection_for_servers(check_list)
		for source_id in self.get_source_plesks():
			source_server = self.get_source_node(source_id)
			ConnectionChecker().check_plesk_api(source_server)

	@staticmethod
	def _check_connection_for_servers(check_list):
		try:
			ConnectionChecker().check(check_list) 
		except PleskConnectionCheckError:
			# just raise, do not any other text
			raise
		except Exception as e:
			logger.debug(messages.LOG_EXCEPTION, exc_info=e)
			raise MigrationError(messages.CHECKING_CONNECTIONS_FAILURE % e)

	def _check_source_version(self):
		"""Check version of Plesk on source Plesk servers"""
		try:
			for source_id in self.get_source_plesks():
				source_server = self.get_source_node(source_id)
				result = source_server.plesk_api().send(
					plesk_ops.server.ServerOperator.Get([plesk_ops.server.ServerOperator.Dataset.STAT])
				)
				plesk_version = result.data.statistics.versions.plesk_version

				if version_to_tuple(plesk_version) >= version_to_tuple('12.6'):
					raise MigrationError(messages.SOURCE_PLESK_VERSION_IS_NOT_SUPPORTED % plesk_version)
		except MigrationError:
			raise
		except Exception:
			logger.debug(messages.LOG_EXCEPTION, exc_info=True)
			logger.error(messages.INTERNAL_FAILURE_UNABLE_TO_DETECT_SOURCE_PANEL_VERSION)

	@staticmethod
	def _read_plesk_settings_from_config(config, list_name, optional=False):
		if not optional or config.has_option('GLOBAL', list_name):
			settings = {}
			list_str = config.get('GLOBAL', list_name)
			if list_str.strip() != '':
				source_sections = map(str.strip, list_str.split(','))
				for section_name in source_sections:
					settings[section_name] = plesk_config.read_source_plesk_settings(config, section_name)
				return settings
			else:
				return {}
		else:
			return {}
