import xmlrpclib 
import urllib
import logging
import base64

from parallels.ppab_api import PPABAPIError
from parallels.utils.xmlrpc import LoggingTransport
from collections import namedtuple

class VoidResult:
	pass

GetVersionResult = namedtuple('GetVersionResult', ('Version',))
ImportAccountFromPEMResult = VoidResult
UserWithIDAddResult = VoidResult
UserUpdateResult = VoidResult
SubscriptionWithIDAddResult = VoidResult
SubscriptionAddResult = namedtuple('SubscriptionAddResult', ('SubscriptionID', ))
SubscrParamValueUpdateResult = VoidResult
DomainSubscrWithIDAddResult = namedtuple('DomainSubscrWithIDAddResult', ('DomainID',))
ImportSubscriptionFromPEMResult = VoidResult
CreditCard2AccountAddResult = namedtuple('CreditCard2AccountAddResult', ('card_id'))
DomainSubscrAddResult = namedtuple('DomainSubscrAddResult', ('subscription_id',	'domain_id'))
GetUsersListForAccountResult = namedtuple('GetUsersListForAccountResult', ('user_id', 'login',	'full_name',))
UserDetailsGetResult = namedtuple('UserDetailsGetResult', (
	'user_id', 'login', 'account_id', 'first_name', 'middle_name',
	'last_name', 'email', 'address1', 'address2', 'city', 'state', 'zip_code', 'country',
	'phone_country_code', 'phone_area_code', 'phone_number', 'phone_extension',
	'fax_country_code',	'fax_area_code', 'fax_number', 'fax_extension',
	'status',
))
PlanPeriodListGetResult = namedtuple('PlanPeriodListGetResult',	(
	'plan_period_id', 'period', 'period_type', 'trial',
	'setup_fee', 'subscription_fee', 'renewal_fee', 'transfer_fee',
	'non_refundable_amt', 'refund_period',
	'enabled', 'number_of_periods',	'fee_text',	'sort_number',
	'os_otfi', 'deposit_fee', 'deposit_descr',
))
InitUsersFromRegistrarResult = namedtuple('InitUsersFromRegistrarResult', ('result'))
UserAddResult = namedtuple('UserAddResult', ('user_id'))
UpdateDomainContactsResult = VoidResult
PlanDetailsGetResult = namedtuple('PlanDetailsGetResult', (
	'plan_id', 'name', 'plan_category_id', 'pcurrency',	'short_description', 'long_description',
	'st_type', 'group_id', 'is_parent_req',	'recurring_type', 'billing_period_type', 'billing_period',
	'show_priority', 'default',	'is_otfi'
))
CertAddResult = namedtuple('CertAddResult', ('CertID',))
GetSubscriptionRenewOrderAmountResult = namedtuple('GetSubscriptionRenewOrderAmountResult', ('fee',))
GetSubscriptionBillOrderAmountResult = namedtuple('GetSubscriptionBillOrderAmountResult', ('fee',))

DomainInfoGetResult = namedtuple('DomainInfoGetResult', (
	'account_id', 'vendor_id', 'domain_id', 'full_domain_zone', 'domain_zone_id', 'status', 'external_dns', 'registration_period', 'start_date', 'expiration_date',
	'primary_name_server', 'secondary_name_server', 'third_name_server', 'fourth_name_server', 'company_name',
	'personal_fname', 'personal_mname', 'personal_lname', 'personal_email', 'personal_phone', 'personal_fax',
	'admin_fname', 'admin_mname', 'admin_lname', 'admin_email', 'admin_phone', 'admin_fax',
	'billing_fname', 'billing_mname', 'billing_lname', 'billing_email', 'billing_phone', 'billing_fax',
	'tech_fname', 'tech_mname', 'tech_lname', 'tech_email', 'tech_phone', 'tech_fax',
))
ADomainGetResult = namedtuple('ADomainGetResult', (
	'domain_id', 'check_result_attempts', 'domain_zone_id', 'full_domain_name', 'tech_domain_name', 'last_operation_status', 'last_renewal_date',
	'primary_name_server', 'secondary_name_server', 'third_name_server', 'fourth_name_server', 'registered_to_account_id',
	'owner_users_id', 'owner_handle_id', 'is_owner_contact_replaced', 'admin_users_id', 'admin_handle_id', 'is_admin_contact_replaced',
	'billing_users_id', 'billing_handle_id', 'is_billing_contact_replaced', 'tech_users_id', 'tech_handle_id', 'is_tech_contact_replaced',
	'registrar_id', 'registration_date', 'status', 'subscription_id', 'time_to_check_result', 'update_date',
))
GetCertListResult = namedtuple('GetCertListResult', (
	'cert_id', 'name', 'plugin_name', 'product_name', 'subscription_id', 'status', 'expire_date',
))
CertSTGetResult = namedtuple('CertSTGetResult', (
	'service_template_id', 'plugin_id', 'cert_product_id'
))
CertProductGetResult = namedtuple('CertProductGetResult', (
	'cert_product_id', 'container_id', 'identity_code'
))
PlanGetResult = namedtuple('PlanGetResult', (
	'plan_id', 'name', 'service_template_id', 'plan_category', 'service_terms', 'short_description', 'long_description', 'unique_group',
	'published', 'attach_usage_statistics', 'account', 'notification_tmpl', 'customer_class', 'recurring_type',
	'billing_period_type', 'billing_period', 'billing_period_description',
	'auto_renew_description', 'auto_renew', 'show_priority', 'default', 'notif_schedule', 'v_is_otf_plan', 'v_h_image', 'v_show_hide_advanced',
))
DomainExtDataAddResult = VoidResult

def parse_result(result_class, response):
	if result_class == VoidResult:
		return VoidResult()	# has no fields to enumerate

	properties_dict = {}
	for number, field in enumerate(result_class._fields):
		properties_dict[field] = response[number]
	return result_class(**properties_dict)

class RawPPABAPI(object):
	logger = logging.getLogger(__name__)

	resultTypes = {
		#Legacy
		"GetVersion_API"                  : GetVersionResult,
		#Import of Account/User
		"ImportAccountFromPEM_API"        : ImportAccountFromPEMResult,
		"UserWithIDAdd_API"               : UserWithIDAddResult,
		"UserUpdate_API"                  : UserUpdateResult,
		#Import of Subscription
		"SubscriptionWithIDAdd_API"       : SubscriptionWithIDAddResult,
		"SubscriptionAdd_API"             : SubscriptionAddResult,
		"SubscrParamValueUpdate_API"      : SubscrParamValueUpdateResult,
		"DomainSubscrWithIDAdd_API"       : DomainSubscrWithIDAddResult,
		"ImportSubscriptionFromPEM_API"   : ImportSubscriptionFromPEMResult,
		#Import of CreditCard
		"CreditCard2AccountAdd_API"       : CreditCard2AccountAddResult,

		"DomainSubscrAdd_API"             : DomainSubscrAddResult,
		"GetUsersListForAccount_API"      : GetUsersListForAccountResult,
		"InitUsersFromRegistrar_API"      : InitUsersFromRegistrarResult,
		"UserDetailsGet_API"              : UserDetailsGetResult,
		"UserAdd_API"                     : UserAddResult,
		"UpdateDomainContacts_API"        : UpdateDomainContactsResult,
		"PlanPeriodListGet_API"           : PlanPeriodListGetResult,
		"PlanDetailsGet_API"              : PlanDetailsGetResult,
		"CertAdd"                         : CertAddResult,
		"GetSubscriptionRenewOrderAmount_API": GetSubscriptionRenewOrderAmountResult,
		"GetSubscriptionBillOrderAmount_API" : GetSubscriptionBillOrderAmountResult,
		"DomainInfoGet_API"		  : DomainInfoGetResult,
		"ADomainGet"			  : ADomainGetResult,
		"GetCertList"			  : GetCertListResult,
		"CertSTGet"			  : CertSTGetResult,
		"CertProductGet"		  : CertProductGetResult,
		"PlanGet"			  : PlanGetResult,
		"DomainExtDataAdd_API"		  : DomainExtDataAddResult,
	} 

	def __init__(self, url, user, password):
		proto, _ = urllib.splittype(url)

		if proto == 'https':
			transport = xmlrpclib.SafeTransport(use_datetime=0)
		elif proto == 'http':
			transport = xmlrpclib.Transport(use_datetime=0)
		else:
			assert False, u"Only http and https protocols are supported by xmlrpclib"

		if self.logger.isEnabledFor(logging.DEBUG):
			transport = LoggingTransport(transport, self.logger)

		self.server = xmlrpclib.ServerProxy(url, transport=transport) 
		self.auser = user
		self.passwd = password
		self.trnsId = None
		self.method = None 

	def __getattr__(self, name):
		self.method = name
		return self
  
	def __call__(self, *args):
		try:
			#BeginTransaction
			if self.method == "BeginTransaction":
				params = {"Container": "BM_Container", "Object": "BM_Object", "Method": "GetVersion_API", "AutoCommit": "No"}
				if self.auser != '':
					params['Username'] = self.auser
					params['Password'] = self.passwd
				response = self.server.Execute(params)
				self.trnsId = response["TransactionID"]
				return []  

			#CommitTransaction 
			elif self.method == "CommitTransaction":
				params = {"TransactionID": self.trnsId}
				if self.auser != '':
					params['Username'] = self.auser
					params['Password'] = self.passwd
				response = self.server.CommitTransaction(params)
				self.trnsId = None
				return []

			#RollbackTransaction
			elif self.method == "RollbackTransaction":
				params = {"TransactionID": self.trnsId}
				if self.auser != '':
					params['Username'] = self.auser
					params['Password'] = self.passwd
				response = self.server.RollbackTransaction(params)
				self.trnsId = None
				return []
      
			#Execute
			params = {"Container": "BM_Container", "Object": "BM_Object", "Method": self.method, "Params": args}
			if self.method in ("DomainSubscrWithIDAdd_API", "DomainSubscrAdd_API", "InitUsersFromRegistrar_API", "UpdateDomainContacts_API", "DomainExtDataAdd_API"):
				params = {"Container": "DOMAINGATE_Container", "Object": "DOMAINGATE_Object", "Method": self.method, "Params": args}
			elif self.method in ("ImportAccountFromPEM_API", "ImportSubscriptionFromPEM_API"):
				params = {"Container": "PEMGATE_Container", "Object": "PEMGATE_Object", "Method": self.method, "Params": args}
			elif self.method in ("CertAdd", "CertSTGet", "CertProductGet"):
				params = {"Container": "CERTGATE_Container", "Object": "CERTGATE_Object", "Method": self.method, "Params": args}
			elif self.method in ("DomainInfoGet_API", "ADomainGet"):
				params = {"Server": "DOMAINGATE", "Method": self.method, "Params": args}
			elif self.method in ("GetCertList"):
				params = {"Server": "CERTGATE", "Method": self.method, "Params": args}
			if self.auser != '':
				params['Username'] = self.auser
				params['Password'] = self.passwd
			if self.trnsId != None:
				params['TransactionID'] = self.trnsId
				params['AutoCommit'] = "No"

			response = self.server.Execute(params)
			results = response["Result"][0]

			self.logger.debug("PPAB API call: %s%s->%s", self.method, args, results)
		except xmlrpclib.Fault as e:
			self.logger.debug("Exception:", exc_info=True)
			short_message = base64.decodestring(e.faultString)
			err_code_pos = short_message.find('Error Code:')
			if err_code_pos > 0:
				short_message = short_message[:err_code_pos].strip()

			raise PPABAPIError(
				message=u"PPAB API call '%s%s' failed: %s" % (
					self.method, args, base64.decodestring(e.faultString)
				),
				short_message=short_message,
				method_name=self.method, method_args=args, inner=e
			)
		except Exception as e:
			self.logger.debug("Exception:", exc_info=True)
			self.logger.debug("PPAB API call: %s%s raised exception: %s", self.method, args, str(e))
			raise

		ResultType = self.resultTypes[self.method]
		if list == type(results) and len(results) > 0 and list == type(results[0]):
			return [parse_result(ResultType, result) for result in results]
		elif len(results) > 0:
			return parse_result(ResultType, results)
		else:
			return []

