from contextlib import contextmanager
import ntpath
import posixpath
from parallels.common.connections.unix_server import UnixServer

from parallels.utils import cached
from parallels.common.utils import plesk_utils
from parallels.common.utils import migrator_utils
from parallels.common.utils import windows_utils
from parallels.plesk_api import operator as plesk_ops
from parallels.common.utils import plesk_api_utils
from parallels.common.utils.plesk_utils import get_plesk_ips_with_cli, get_plesk_ips_with_api


class PleskServer(UnixServer):
	@contextmanager
	def runner(self):
		raise NotImplementedError()

	def description(self):
		raise NotImplementedError()

	def is_windows(self):
		raise NotImplementedError()

	def plesk_api(self):
		raise NotImplementedError()

	@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)

	def get_vhost_dir(self, domain):
		"""Get path to virtual host's directory of domain"""
		vhost_name = domain.encode('idna')
		if self.is_windows():
			return windows_utils.path_join(self.vhosts_dir, vhost_name)
		else:
			return posixpath.join(self.vhosts_dir, vhost_name)

	@property
	@cached
	def plesk_dir(self):
		if self.is_windows():
			with self.runner() as runner:
				return plesk_utils.get_windows_plesk_dir(runner)
		else:
			with self.runner() as runner:
				return plesk_utils.get_unix_product_root_dir(runner)

	@property
	@cached
	def data_dir(self):
		if self.is_windows():
			with self.runner() as runner:
				return plesk_utils.get_windows_data_dir(runner)
		else:
			with self.runner() as runner:
				#  for Plesk for Unix data dir is the same as root dir
				return plesk_utils.get_unix_product_root_dir(runner)

	@property
	@cached
	def mail_dir(self):
		if self.is_windows():
			raise NotImplementedError()
		else:
			with self.runner() as runner:
				return plesk_utils.get_unix_mailnames_dir(runner)

	@property
	@cached
	def dump_dir(self):
		if self.is_windows():
			with self.runner() as runner:
				dump_dir = plesk_utils.get_windows_dump_dir(runner)
				if not dump_dir:
					# Fallback to %plesk_dir%\Backup
					dump_dir = ntpath.join(self.plesk_dir, u'Backup')
				return dump_dir
		else:
			with self.runner() as runner:
				return plesk_utils.get_unix_dump_dir(runner)

	@cached
	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.
		"""
		if self.is_windows():
			version = plesk_api_utils.get_plesk_version(self.plesk_api())
			return tuple(map(int, (version.split("."))))
		else:
			with self.runner() as runner:
				return plesk_utils.get_plesk_version(runner)

	@property
	@cached
	def rsync_bin(self):
		with self.runner() as runner:
			return migrator_utils.detect_rsync_path(
				runner, self.description()
			)

	@property
	@cached
	def is_mailserver_qmail(self):
		if self.is_windows():
			return False
		else:
			with self.runner() as runner:
				return 'qmail' in runner.sh(
					'plesk sbin mailmng --features | grep SMTP_Server'
				).lower()

	def get_bin_util_path(self, name):
		if self.is_windows():
			return u'%s\\bin\\%s' % (self.plesk_dir, name)
		else:
			return u'%s/bin/%s' % (self.plesk_dir, name)

	@cached
	def get_db_servers(self):
		return {
			result.data.id: result.data
			for result in self.plesk_api().send(plesk_ops.DbServerOperator.Get(plesk_ops.DbServerOperator.FilterAll()))
		}

	def has_database_server(self, server_host, server_type):
		for database_server in self.get_db_servers().itervalues():
			if database_server.host == server_host and database_server.dbtype == server_type:
				return True
		return False

	def get_database_server(self, server_host, server_type):
		for database_server in self.get_db_servers().itervalues():
			if database_server.host == server_host and database_server.dbtype == server_type:
				return database_server
		return None

	def get_default_database_server(self, server_type):
		for database_server in self.get_db_servers().itervalues():
			if database_server.dbtype == server_type and database_server.default:
				return database_server
		return None

	@cached
	def get_all_ips(self, global_context):
		"""Get list of all IP addresses of Plesk server

		Information includes IP address, its type and corresponding public IP address.
		"""
		if self.get_plesk_version() >= (12, 0):
			return get_plesk_ips_with_cli(self)
		else:
			# on old Plesk/PPA 'ipmanage' utility returns incomplete set of IP addresses,
			# fallback to using Plesk API
			return get_plesk_ips_with_api(self)
