from contextlib import contextmanager
import posixpath

from parallels.utils import ip as ip_utils
from parallels.utils import if_not_none
from parallels.utils import cached
from parallels.common.utils.windows_utils import path_join as windows_path_join
from parallels.common.utils import poa_utils 
from parallels.common import run_command

from parallels.common.connections.target_servers import DatabaseTargetServer
from parallels.target_panel_ppa.connections.target_server import \
		PPATargetServer

class PPADatabaseTargetServer(DatabaseTargetServer):
	def __init__(self, conn, host, port, login, password):
		self._host = host
		self._port = port
		self._login = login
		self._password = password
		self.conn = conn

		try:
			self.ppa_host_id = if_not_none(
				self._ip(), 
				lambda ip: poa_utils.get_host_id_by_ip(self.conn.poa_api(), ip)
			)
		except poa_utils.HostNotFoundException:
			# database server with such IP address is not a PPA service node, 
			# so we consider that is an external database server
			self.ppa_host_id = None 

		if not self.is_external():
			if self.is_windows():
				self.session_directory = self.conn.windows_session_dir
			else:
				self.session_directory = self.conn.unix_session_dir
		else:
			# session directory management is not allowed for external
			# database servers as we don't have direct access to them
			# (we have access only by database-specific protocols)
			pass

	def host(self):
		return self._host

	def port(self):
		return self._port

	def login(self):
		return self._login

	def password(self):
		return self._password

	@cached
	def is_windows(self):
		if self.is_external():
			raise Exception(
				'Trying to get OS type for external database server. '
				'Getting OS type for external database server is not implemented.'
			)

		with self.conn.main_node_runner() as runner:
			return run_command.hcl_is_windows(runner, self.ppa_host_id, self.description())

	@cached
	def ip(self):
		return self._ip()

	def ips(self):
		return self.get_subscription_node().ips()

	def _ip(self):
		if self.host() == 'localhost':
			# pleskrestore restores 'localhost' databases to Plesk's main node
			# and Plesk's main node is PPA management node
			return self.conn.main_node_ip    
		else:
			# host may contain MS SQL instance name, e.g. 10.50.52.100\MSSQLSERVER2008
			return ip_utils.resolve(self.host().split('\\')[0]) 

	@contextmanager
	def runner(self):
		if self.is_external():
			raise Exception(
				'Trying to get runner for external database server. '
				'Running commands on external database server is not implemented.'
			)

		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

	def get_session_file_path(self, filename):
		if self.is_external():
			raise Exception(
				'Trying to get runner for external database server. '
				'Running commands on external database server is not implemented.'
			)

		with self.runner() as runner:
			runner.mkdir(self.session_directory)

		if self.is_windows():
			return windows_path_join(self.session_directory, filename)
		else:
			return posixpath.join(self.session_directory, filename)

	def session_dir(self):
		return self.get_subscription_node().session_dir()

	@property
	def mysql_bin(self):
		return 'mysql'

	@cached
	def description(self):
		if self.is_external():
			return 'external database server %s:%s' % (self.host(), self.port())
		else:
			return self.conn.get_ppa_node_description(self.ppa_host_id)

	def is_external(self):
		return self.ppa_host_id is None

	@cached
	def get_subscription_node(self):
		if self.is_external():
			raise Exception(
				'External database server is not a PPA service node, '
				'so we cannot get target server object for it.'
			)
		return PPATargetServer(self.conn, self.ppa_host_id, self.ip())

	def __hash__(self):
		return hash((self.ppa_host_id, self.host(), self.port()))

	def __eq__(self, another): 
		return all([
			isinstance(another, PPADatabaseTargetServer),
			another.host() == self.host(),
			another.port() == self.port()
		])
