from parallels.target.ppa import messages
import logging
from contextlib import contextmanager
from collections import namedtuple

from parallels.core.connections.ssh.connection_pool import SSHConnectionPool
from parallels.core.utils.common import cached
from parallels.core import run_command
from parallels.api.poa import poa_api
from parallels.api import plesk as plesk_api
from parallels.api.ppab import import_api as billing_import_api
import parallels.core.migrator_config as connections_config
from parallels.core import MigrationError
from parallels.core.connections.checker import ConnectionChecker
from parallels.target.plesk.connections.target_server import PleskTargetServer
from parallels.core.connections.target_connections import TargetConnections
from parallels.core.utils.session_dir import UnixSessionDir


logger = logging.getLogger(__name__)

class PPATargetConnections(TargetConnections):
	def __init__(self, config):
		self._ppa_settings = connections_config.read_ppa_settings(config, 'ppa')
		if not self._ppa_settings.is_local:
			self._ssh = SSHConnectionPool.get_instance().get(
				self._ppa_settings, 'PPA management node'
			)
		self._session_dir = UnixSessionDir(
			self.main_node_runner, self._ppa_settings.unix_session_dir
		)

		logger.info(u"PPA management node: %s", self._ppa_settings.ip)
		logger.info(u"PPA Plesk URL: %s", self._ppa_settings.plesk_api.url)

	@property
	@cached
	def plesk_server(self):
		return PleskTargetServer(self)
		
	@property
	def main_node_ip(self):
		return self._ppa_settings.ip

	@property
	def panel_admin_password(self):
		return self._ppa_settings.plesk_api.auth.password

	@contextmanager
	def main_node_runner(self):
		if self._ppa_settings.is_local:
			yield run_command.LocalUnixRunner()
		else:
			with self._ssh.runner() as runner:
				yield runner

	def main_node_description(self):
		return 'PPA management node'

	def main_node_session_file_path(self, filename):
		return self._session_dir.get_file_path(filename)

	@property
	def session_dir_obj(self):
		return self._session_dir

	@contextmanager
	def ppa_unix_node_runner(self, node_id):
		with self.main_node_runner() as ppa_runner:
			yield run_command.UnixHclRunner(
				ppa_runner, node_id, self.get_ppa_node_description(node_id)
			)

	@contextmanager
	def ppa_windows_node_runner(self, node_id):
		with self.main_node_runner() as ppa_runner:
			yield run_command.WindowsHclRunner(
				ppa_runner, node_id, self.get_ppa_node_description(node_id)
			)

	@cached
	def poa_api(self):
		return poa_api.Client(self._ppa_settings.poa_api.url)

	def plesk_api(self):
		return plesk_api.Client(
			self._ppa_settings.plesk_api.url,
			self._ppa_settings.plesk_api.auth.username,
			self._ppa_settings.plesk_api.auth.password,
			plesk_api.api_versions['11.5'],
			pretty_print=True
		)

	def billing_api(self):
		connection_info = self._get_billing_connection_info()
		return billing_import_api.PPABImportAPI(connection_info.url, connection_info.username, connection_info.password)
	
	def _get_billing_connection_info(self):
		xml_rpc_info = self.poa_api().get_bm_xml_rpc_address()
		protocol = 'https' if xml_rpc_info.get('ssl', True) == True else 'http'
		return BillingConnectionInfo(
			url='%s://%s:%s%s' % (protocol, xml_rpc_info['host'], xml_rpc_info.get('portnum', '5224'), xml_rpc_info.get('uri', '/RPC2')),
			username=self._ppa_settings.plesk_api.auth.username,
			password=self._ppa_settings.plesk_api.auth.password,
		)

	@cached
	def get_ppa_node_description(self, node_id):
		"""Get description of service node that should be used in log messages.
		Function is cached, so use it only in stable environment, when you do not expect new nodes to be registered/assimilated"""

		node_info = self.poa_api().getHost(node_id)

		if len(node_info.ip_addresses) > 0:
			ip_address = node_info.ip_addresses[0].address
		else:
			ip_address = 'unknown IP' # it should not be possible, but if it is so due to some bug, use this just not to fail all checks

		return "PPA service node '%s' (#%s, %s)" % (node_info.hostname, node_id, ip_address)

	def check_connections(self):
		"""Check connections to POA API, Plesk API and SSH.
		   Raise MigrationError if required connection(s) are not available.
		"""	
		logger.debug(messages.CHECK_TARGET_POA_API_CONNECTION)
		try:
			self.poa_api().get_ppa_license_status() # just a sample request
		except Exception as err:
			logger.debug(u"Exception:", exc_info=err)
			raise MigrationError(
				messages.FAILED_TO_CHECK_POA_API % (self._ppa_settings.poa_api.url, err)
		   )

		connection_checker = ConnectionChecker()
		connection_checker.check_target_plesk_api(self.plesk_api(), 'PPA')
		if not self._ppa_settings.is_local:
			connection_checker.check_ssh('PPA', self._ppa_settings)

	@property
	def unix_session_dir(self):
		return self._ppa_settings.unix_session_dir

	@property
	def windows_session_dir(self):
		return self._ppa_settings.windows_session_dir

	@property
	def is_windows(self):
		return False

	@property
	def is_local(self):
		"""If main node of target server is local or remote

		Local server means that migrator works on the target node, remote means
		that migrator's node and target node are different servers
		"""
		return self._ppa_settings.is_local

BillingConnectionInfo = namedtuple('BillingConnectionInfo', ('url', 'username', 'password'))

