from parallels.plesk.utils.xml_rpc.expand import messages

import logging
import xml.etree.ElementTree as et

from parallels.core.utils.common.http_xml_client import HttpXmlClient
from parallels.plesk.utils.xml_rpc.expand.core import ExpandSystemError


class ExpandClientError(Exception):
	pass


class UnsupportedOperationException(Exception):
	pass


class Client(HttpXmlClient):
	logger = logging.getLogger(__name__)
	
	def __init__(self, url, username, password, api_version):
		super(Client, self).__init__(url)
		self.username = username
		self.password = password
		self.api_version = api_version
		
	def send(self, operation):
		return self.send_many(operation)[0]
	
	def send_many(self, *operations):
		if len(operations) == 0:
			raise ExpandClientError(messages.AT_LEAST_ONE_OPERATION_MUST_BE_SPECIFIED)

		if not all(o.operator_name == operations[0].operator_name for o in operations):
			raise ExpandClientError(messages.ALL_OPERATIONS_MUST_BELONG_TO_SAME_OPERATOR)
		operator_name = operations[0].operator_name

		request = self._create_packet(operations)

		response = self.send_xml(request, operator_name)
		
		operation_responses = []
		root = response.getroot()

		system_error = root.find('system_error')  # handle system-level error
		if system_error is not None:
			raise ExpandSystemError(
				code=int(system_error.findtext('code')), 
				message=system_error.findtext('text')
			)

		if root.findtext('system/status') == 'error':
			raise ExpandSystemError(
				code=int(root.findtext('system/errcode')), 
				message=root.findtext('system/errtext')
			)

		assert len(operations) == len(root)
		for operation, node in zip(operations, root):
			assert operation.operation_name == node.tag
			operation_responses.append(operation.parse(node))
		return operation_responses
	
	def _create_packet(self, operations):
		request = et.ElementTree(et.Element('packet', dict(version=self.api_version)))
		for op in operations:
			operation_node = et.Element(op.operation_name)
			operation_node.extend(op.inner_xml())
			request.getroot().append(operation_node)
		return request

	def _prepare_request_object(self, data, operator_name):
		request = super(Client, self)._prepare_request_object(data)
		request.add_header('HTTP_AUTH_LOGIN', self.username)
		request.add_header('HTTP_AUTH_PASSWD', self.password)
		request.add_header('HTTP_AUTH_OP', operator_name)
		return request