import logging

import parallels.common.migrator_config as connections_config
from parallels import plesk_api
from parallels.plesk_api import operator as plesk_ops
from parallels.utils import cached
from parallels.utils import merge_dicts
from parallels.common import MigrationError
from parallels.plesks_migrator.server import PleskSourceServer
from parallels.common.connections.checker import PleskConnectionCheckError
from parallels.common.connections.checker import ConnectionChecker
from parallels.common.utils.migrator_utils import version_to_tuple
from parallels.common.connections.connections import Connections

logger = logging.getLogger(__name__)

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

		self._migrator_server = migrator_server

		self._source_plesks = self._read_plesk_settings_from_config(
			config, 'source-plesks'
		)
		self._external_db_servers = self._read_external_db_settings_from_config(
			config, 'external-postgresql-servers'
		)

		for settings in self.get_source_plesks().itervalues():
			logger.info(
				u"Source Plesk '%s' host: %s", 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_external_db_servers(self):
		"""Get information about source external database servers

		Returns dictionary {server_id: server_settings} with connection
		information.
		We have support for only PostgreSQL external database servers now.
		"""
		return self._external_db_servers

	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."""
		self._check_connections_plesk_servers()
		self._check_connections_external_db_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", "plesk_api", "windows_auth"]
			))
		self._check_connection_for_servers(check_list)

	def _check_connections_external_db_servers(self):
		"""Check connections (SSH) to external DB servers"""

		check_list = []
		for source_id in self.get_external_db_servers():
			check_list.append((
				self.get_source_db_node(source_id),
				["ssh_auth"]
			))

		self._check_connection_for_servers(check_list)

	def _check_connection_for_servers(self, check_list):
		try:
			ConnectionChecker().check(check_list) 
		except PleskConnectionCheckError:
			raise # just raise, do not any other text
		except Exception as e:
			logger.debug(u'Exception:', exc_info=e)
			raise MigrationError(
				u"Error while checking connection settings: '%s'" % e
			)

	def _check_source_version(self):
		"""Check version of Plesk on source Plesk servers"""

		try:
			servers = self.get_source_plesks().iteritems()
			for _, server_options in servers:
				result = plesk_api.Client(  # TODO
					server_options.plesk_api.url,
					server_options.plesk_api.auth.username,
					server_options.plesk_api.auth.password,
					'1.5.2.1',
					pretty_print=True
				).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.1.0'):  # TODO
				raise MigrationError(
					u"You are trying to migrate from Plesk version '%s'. "
					u"Migration from this version is not supported yet."
					% plesk_version
				)
		except MigrationError:
			raise
		except Exception as e:
			logger.debug(u'Exception:', exc_info=e)
			logger.error(
				u'Unable to detect source panel version. Skip checking.'
			)

	@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] = connections_config.read_source_plesk_settings(config, section_name)
				return settings
			else:
				return {}
		else:
			return {}

	@staticmethod
	def _read_external_db_settings_from_config(config, list_name):
		settings = {}
		if config.has_option('GLOBAL', list_name):
			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] = connections_config.read_external_db_settings(config, section_name)
				return settings
			else:
				return {}
		else:
			return {}

