from contextlib import contextmanager

from parallels.utils import cached, is_run_on_windows
from parallels.common import run_command
from parallels.common.connections.nested_context_ssh import NestedContextSSH
from parallels.common.utils import session_dir

class SourceServer(object):
	"""This is a class to interact with source server
	and get various common information about the source server.
	"""

	def __init__(
		self, node_id, node_settings, migrator_server=None, winexe_runas=False
	):
		"""Object constructor.
		
		Args:
			node_id: server's section name specified in config.ini
			node_settings: server properties specified in config.ini
			migrator_server: server where migration tools are installed,
				do not pass if you don't need local execution of commands
				on Windows server
			winexe_runas: boolean, if True then use "run as" option when
				running commands with winexe
		"""
		self.node_id = node_id
		self.node_settings = node_settings
		self._migrator_server = migrator_server
		self._nested_main_node_ssh = NestedContextSSH(
				self.node_settings, "source server '%s'" % node_id
		)
		self._winexe_runas = winexe_runas

		if self.node_settings.is_windows:
			self._session_dir = session_dir.WindowsSessionDir(
				self.runner, self.node_settings.session_dir
			)
		else:
			self._session_dir = session_dir.UnixSessionDir(
				self.runner, self.node_settings.session_dir
			)

	@contextmanager
	def runner(self):
		"""Get runner object to execute commands on the source server"""
		if self.is_windows():
			yield self._get_windows_runner()
		else:
			with self._nested_main_node_ssh.runner() as runner:
				yield runner

	@cached
	def _get_windows_runner(self):
		if is_run_on_windows():
			if self.node_settings.agent_settings.enabled:
				return run_command.WindowsAgentRunner(
					self.node_settings
				)
			else:
				return run_command.PAExecRunner(
					self.node_settings, self._migrator_server, self
				)
		else:
			if self._migrator_server is not None:
				local_temp_dir = self._migrator_server.session_dir()
			else:
				local_temp_dir = None

			if self.node_settings.agent_settings.enabled:
				return run_command.WindowsAgentRunner(
					self.node_settings
				)
			else:
				return run_command.WinexeRunner(
					self.node_settings,
					local_temp_dir=local_temp_dir,
					runas=self._winexe_runas
				)

	def is_windows(self):
		"""Return True if source server is Windows server, 
		False if source server is Unix server"""
		return self.node_settings.is_windows

	def description(self):
		"""Return brief server description which should help end customer
		to identify server and which should be used in all messages"""
		return "the source server '%s' (%s)" % (self.node_id, self.ip())

	def ip(self):
		"""Return main IP address of the server"""
		return self.node_settings.ip

	def user(self):
		"""Return user we use to communicate with the server"""
		if not self.is_windows():
			return self.node_settings.ssh_auth.username
		else:
			return self.node_settings.windows_auth.username
	
	@property
	def mail_settings(self):
		"""Return copy mail content configuration"""
		return self.node_settings.mail_settings

	def settings(self):
		return self.node_settings

	def mail_imap_encryption(self):
		"""Return IMAP sync encryption mode (no encryption/SSL/TLS) 

		See parallels.common.connections.config.MailImapEncryption 
		for possible values
		"""
		return self.mail_settings.mail_imap_encryption

	def get_session_file_path(self, filename):
		"""Return full path to a temporary file on the server by
		short file name. Function considers session directory settings
		and ensures that session directory exists on the server"""
		return self._session_dir.get_file_path(filename)

	def session_dir(self):
		"""Return path to session directory - directory where 
		we should store all temporary files"""
		return self._session_dir.get_session_dir_path()

	def __repr__(self):
		return 'SourceServer(%r)' % (self.node_id,)

	def __hash__(self):
		"""Implemented for using objects as dicitionary keys, 
		for the purpose of grouping"""
		return hash(self.node_id)

	def __eq__(self, another):
		"""Implement for using objects as dicitionary keys, 
		for the purpose of grouping"""
		return self.node_id == another.node_id

class SingleServerPanelSourceServer(SourceServer):
	"""Source server of a single-server control panel.
	
	This represents a server, on which, from migrator point of view, all
	hosting resources are stored on local file system. This can be either a single
	server hosting panel (Plesk, Confixx), or a panel, for which each server is
	configured in a separate section of 'config.ini' file (such as Plesk Expand).
	"""

	def __init__(self, node_id, node_settings, migrator_server):
		super(SingleServerPanelSourceServer, self).__init__(
				node_id, node_settings, migrator_server
		)

	def get_unix_vhost_dirs(self, runner, domain_name):
		"""Return a list of directories with domain hosting content."""
		raise NotImplementedError()

	def get_unix_domain_mail_dirs(self, runner, domain_name):
		"""Return a list of directories with mail content."""
		raise NotImplementedError()
