from contextlib import contextmanager

from parallels.utils import cached
from parallels.common import run_command
from parallels.common.utils import plesk_utils
from parallels.common.utils import windows_utils
from parallels.common.connections.target_servers import TargetServer
from parallels.common.panel.server.plesk_family import HostingServer
from parallels.common.utils import session_dir
from parallels.common.utils import plesk_api_utils

class PPATargetServer(TargetServer):

	def __init__(self, conn, ppa_host_id, ip):
		self.conn = conn
		self.ppa_host_id = ppa_host_id
		self.ip_address = ip

		if self.is_windows():
			self._session_dir = session_dir.WindowsSessionDir(
				self.runner, self.conn.windows_session_dir
			)
		else:
			self._session_dir = session_dir.UnixSessionDir(
				self.runner, self.conn.unix_session_dir
			)

	def get_hosting_server(self):
		return HostingServer(self)

	@contextmanager
	def runner(self):
		if self.is_windows():
			with self.conn.ppa_windows_node_runner(self.ppa_host_id) as runner:
				yield runner
		else:
			with self.conn.ppa_unix_node_runner(self.ppa_host_id) as runner:
				yield runner

	@cached
	def is_windows(self):
		"""Determine if POA host is Windows or Linux. Return True for Windows, False for Linux.
		As it is unlikely that this information changes during migration tool execution, we can safely cache it.
		"""
		with self.conn.main_node_runner() as runner:
			return run_command.hcl_is_windows(
				runner,
				self.ppa_host_id,
				self.description()
			)

	@cached
	def description(self):
		return self.conn.get_ppa_node_description(self.ppa_host_id)

	def ip(self):
		return self.ip_address

	@cached
	def ips(self):
		poa_api = self.conn.poa_api()
		ip_addresses = poa_api.getHost(self.ppa_host_id).ip_addresses
		return set([
			ip_info.address 
			for ip_info in ip_addresses
		])

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

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

	def get_plesk_version(self):
		"""Return a tuple with Plesk server version.
		
		In PPA, Plesk version the same across all service nodes; we get Plesk
		version from management node.
		"""
		with self.conn.main_node_runner() as runner:
			return plesk_utils.get_plesk_version(runner)

	@property
	@cached
	def plesk_version(self):
		"""Return a tuple with Plesk server version."""
		version = plesk_api_utils.get_plesk_version(
			self.conn.plesk_api()
		)
		return tuple(map(int, (version.split("."))))

	@property
	@cached
	def vhosts_dir(self):
		if self.is_windows():
			get_vhosts_dir_function = plesk_utils.get_windows_vhosts_dir
		else:
			get_vhosts_dir_function = plesk_utils.get_unix_vhosts_dir

		with self.runner() as runner:
			return get_vhosts_dir_function(runner)

	@property
	@cached
	def plesk_dir(self):
		if self.is_windows():
			with self.runner() as runner:
				return windows_utils.detect_plesk_dir(runner.sh)
		else:
			raise NotImplementedError()

	def __repr__(self):
		return 'PPATargetServer(%s)' % (self.ppa_host_id)

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

	def __eq__(self, another): 
		return isinstance(another, PPATargetServer) and another.ppa_host_id == self.ppa_host_id
