from contextlib import contextmanager

from parallels import plesk_api
from parallels.common.connections.checker import ConnectionChecker
from parallels.common.connections.ssh.connection_pool import SSHConnectionPool
from parallels.common.connections.target_servers import TargetServer
from parallels.common.connections.plesk_server import PleskServer
from parallels.common.panel.server.plesk_family import HostingServer
from parallels.common.run_command import SSHRunner, WinexeRunner
from parallels.common.utils.session_dir import WindowsSessionDir, UnixSessionDir
from parallels.utils import cached


class PleskInVPSTargetServer(TargetServer, PleskServer):
	def __init__(self, settings):
		"""
		:type settings: PleskInVPSTargetSettings
		"""
		self.settings = settings
		session_dir = WindowsSessionDir if self.is_windows() else UnixSessionDir
		self.session_dir_obj = session_dir(self.runner, settings.session_dir)

	@contextmanager
	def runner(self):
		if self.is_windows():
			yield WinexeRunner(self.settings, False, '/tmp/')
		else:
			ssh_conn = SSHConnectionPool.get_instance().get(self.settings, self.description())
			with ssh_conn.ssh() as ssh:
				yield SSHRunner(ssh, self.description())

	@cached
	def plesk_api(self):
		return plesk_api.Client(
			'https://%s:8443/enterprise/control/agent.php' % self.settings.ip,
			self.settings.panel_auth.username,
			self.settings.panel_auth.password,
			plesk_api.api_versions['11.5'],
			pretty_print=True
		)

	def get_hosting_server(self):
		return HostingServer(self)

	def get_session_file_path(self, filename):
		return self.session_dir_obj.get_file_path(filename)

	def session_dir(self):
		return self.session_dir_obj.get_session_dir_path()

	def is_windows(self):
		return self.settings.is_windows

	def description(self):
		return "target Plesk in VPS server '%s'" % (self.ip())

	def ip(self):
		return self.settings.ip

	def ips(self):
		return {self.ip()}

	@property
	@cached
	def plesk_version(self):
		return self.get_plesk_version()

	def check_connections(self):
		connection_checker = ConnectionChecker()
		connection_checker.check_target_plesk_api(self.plesk_api(), self.description())
		if not self.settings.is_windows:
			connection_checker.check_ssh(self.description(), self.settings)
		else:
			with self.runner() as runner:
				runner.check(self.description())

	def __hash__(self):
		return hash((self.ip()))

	def __eq__(self, another):
		"""
		:type another: PleskInVPSTargetServer
		"""
		return isinstance(another, PleskInVPSTargetServer) and another.ip() == self.ip()


class PleskInVPSTargetSettings(object):
	def __init__(self, ip, panel_auth, session_dir):
		"""
		:type ip: basestring
		:type panel_auth: parallels.common.utils.config_utils.Auth
		:type session_dir: basestring
		"""
		self.ip = ip
		self.panel_auth = panel_auth
		self.session_dir = session_dir
		self.is_windows = None


class PleskInVPSUnixSettings(PleskInVPSTargetSettings):
	def __init__(self, ip, ssh_auth, panel_auth, session_dir):
		"""
		:type ip: basestring
		:type ssh_auth: parallels.common.migrator_config.SSHAuthPassword
		:type panel_auth: parallels.common.utils.config_utils.Auth
		:type session_dir: basestring
		"""
		super(PleskInVPSUnixSettings, self).__init__(ip, panel_auth, session_dir)
		self.ssh_auth = ssh_auth
		self.is_windows = False


class PleskInVPSWindowsSettings(PleskInVPSTargetSettings):
	def __init__(self, ip, windows_auth, panel_auth, session_dir):
		"""
		:type ip: basestring
		:type windows_auth: parallels.common.utils.config_utils.Auth
		:type panel_auth: parallels.common.utils.config_utils.Auth
		:type session_dir: basestring
		"""
		super(PleskInVPSWindowsSettings, self).__init__(ip, panel_auth, session_dir)
		self.windows_auth = windows_auth
		self.is_windows = True
