from parallels.core import migrator_config
from parallels.core.connections.checker import ConnectionChecker
from parallels.core.connections.physical_server import create_physical_server
from parallels.core.utils import config_utils
from parallels.core.utils.common import cached, group_value_by_id
from parallels.core.connections.source_server import SourceServer


class Connections(object):
	def __init__(self, global_context, target_panel):
		self._config = global_context.config
		self.target = target_panel.get_connections(global_context)
		self._external_db_servers = self._read_external_db_settings_from_config(
			global_context.config, 'external-db-servers'
		)

	def check_source_servers(self):
		"""Check source servers: connections, versions, licenses, etc

		By default, check external database servers only
		Override in child classes and add more checks
		"""
		for external_db_server_id in self._external_db_servers.iterkeys():
			physical_server = self.get_external_db_physical_server(external_db_server_id)
			if physical_server.is_windows():
				with physical_server.runner() as runner:
					runner.check(physical_server.description())
			else:
				ConnectionChecker().check_ssh(physical_server.description(), physical_server.settings())

	@cached
	def has_source_node(self, node_id):
		return node_id in self.get_information_servers()

	@cached
	def get_source_node(self, node_id):
		return SourceServer(
			node_id,
			self.get_information_servers()[node_id]
		)

	def get_information_servers(self):
		"""Get servers which could provide info about subscriptions and customers""" 
		raise NotImplementedError()

	def get_external_db_servers(self):
		"""Get information about source external database servers

		Returns dictionary {server_id: server_settings} with connection
		information.

		:rtype: dict[str, parallels.core.migrator_config.ExternalDBConfigBase]
		"""
		return self._external_db_servers

	def get_external_db_server_id(self, db_type, host):
		"""Get external database server ID by database type and host

		Return external database server ID - name of a section in configuration file which describes this
		external database server. If no server with such database type and host was found - return None.

		:param str db_type: type of database server, 'mysql' or 'mssql' or 'postgresql'
		:param: str host: hostname of database server, for example '192.168.1.15', or '10.52.1.55\MSSQLSERVER2008'
		:rtype: str | None
		"""
		external_db_servers_by_host = group_value_by_id(
			self._external_db_servers.items(),
			key_func=lambda s: (s[1].db_type, s[1].host),
			value_func=lambda s: s[0]
		)
		return external_db_servers_by_host.get((db_type, host))

	@cached
	def get_external_db_physical_server(self, server_id):
		"""Get physical server object that corresponds to external database server with specified ID

		:param str server_id: external database server ID name of a section in configuration file
		:rtype: parallels.core.connections.physical_server.PhysicalServer
		"""
		return create_physical_server(self._external_db_servers[server_id])

	@staticmethod
	def _read_external_db_settings_from_config(config, list_name):
		"""
		:rtype: dict[str, parallels.core.migrator_config.ExternalDBConfigBase]
		"""
		sections = config_utils.get_sections_list(config, 'GLOBAL', list_name)
		return {
			section_name: migrator_config.read_external_db_settings(config, section_name)
			for section_name in sections
		}