from parallels.core import messages
import logging
import urllib2
import urllib
from xml.etree import ElementTree
import itertools
from parallels.core.run_command import BaseRunnerException
from parallels.core.utils.common_constants import PLESK_PANEL_DEFAULT_PORT
from parallels.core.utils.unix_utils import format_command_list
from parallels.core.utils.common import safe_string_repr, default

logger = logging.getLogger(__name__)


class CLIGateAuthPassword(object):
	def __init__(self, password):
		self.password = password


class PleskCLIRunnerBase(object):
	"""Base class to run Plesk utilities"""

	def run(self, utility_name, arguments, env=None):
		raise NotImplementedError()


class PleskCLIRunnerCLI(PleskCLIRunnerBase):
	"""Run Plesk utilities with runner provided by server object"""

	def __init__(self, plesk_server):
		self.plesk_server = plesk_server

	def run(self, utility_name, arguments, env=None):
		"""
		:type utility_name: basestring
		:type arguments: list[basestring]
		"""
		with self.plesk_server.runner() as runner:
			return runner.run(
				self.plesk_server.get_bin_util_path(utility_name),
				arguments,
				env=env
			)

	def run_unchecked(self, utility_name, arguments, env=None):
		with self.plesk_server.runner() as runner:
			return runner.run_unchecked(
				self.plesk_server.get_bin_util_path(utility_name),
				arguments,
				env=env
			)


class PleskCLIRunnerLocalCLIGate(PleskCLIRunnerBase):
	"""Run Plesk utilities with the help of CLIGate"""

	def __init__(self, cligate_auth, is_windows, ip, port=None):
		"""Class constructor

		:param cligate_auth: type of CLIGate auth, allowed value: CLIGateAuthPassword
		"""
		self._cligate_auth = cligate_auth
		self._is_windows = is_windows
		self._ip = ip
		self._port = default(port, PLESK_PANEL_DEFAULT_PORT)

	def run(self, utility_name, arguments, env=None):
		url_parts = []
		for argument in itertools.chain([utility_name], arguments):
			url_parts.append(('ARG[]', argument))
		url_parts.append(('ENV[LANG]', 'en_US.UTF-8'))
		url_parts.append(('ENV[ALLOW_WEAK_PASSWORDS]', '1' if self._is_windows else ''))
		url_parts.append(('ENV[PLESK_RESTORE_MODE]', '1'))

		if env is not None:
			for key, value in env.iteritems():
				url_parts.append(('ENV[%s]' % key, value))

		if isinstance(self._cligate_auth, CLIGateAuthPassword):
			url_parts.append(('PSW', self._cligate_auth.password))
		else:
			raise NotImplementedError(messages.PROVIDED_CLIGATE_AUTH_IS_NOT_SUPPORTED)

		url_args_str = "&".join(
			'%s=%s' % (
				name,
				urllib.quote_plus(value.encode('utf-8')),
			)
			for name, value in url_parts
		)

		url_str = "https://%s:%s/cligate.php?%s" % (self._ip, self._port, url_args_str)
		logger.debug(messages.DEBUG_CLIGATE_REQUEST, url_str)
		result = urllib2.urlopen(url_str).read()
		result_xml = ElementTree.fromstring(result)

		exit_code = int(result_xml.findtext('./return'))
		stdout = result_xml.findtext('./stdout')
		stderr = result_xml.findtext('./stderr')
		if stderr is None:
			stderr = ''

		cmd_str = format_command_list(utility_name, arguments)
		if exit_code != 0:
			raise BaseRunnerException(
				messages.CLIGATE_COMMAND_FAILED % (
					cmd_str, exit_code, safe_string_repr(stdout), safe_string_repr(stderr)
				)
			)
		return stdout
