from collections import namedtuple
from parallels.utils import obj
from parallels.ppab_api.raw_api import RawPPABAPI

INT_NULL = -2147483648

Account = namedtuple('Account', (
	'vendor_account_id', # Int
	'account_id', # Int
	'class_id', # Int
	'tax_reg_id', # Str(20)
	'balance', # Double
	'tax_status' # Int
))

CreditCard = namedtuple('CreditCard', (
	# 1) Owner customer
	'customer_id',
	
	# 2) Credit card information
	'pay_system', # Str(30), one of PaySystem value
	'card_number', # Str(20)
	'card_holder_name', # Str(40)
	'cvv', # Str(4)
	'expiration_date', # Str(5), format: "DD/YY"
	'start_date', # Str(5), applicable for Maestro cards only, format: "DD/YY"
	'issue_number', # Str(4), applicable for Maestro cards only

	# 3) Contact information
	'address1', # Str(80)
	'address2', # Str(80)
	'city', # Str(40)
	'state', # Str(80)
	'zip_code', # Str(10)
	'country', # Str(2), 2-symbol identifier, in lower case, for example 'us' or 'ru'
	'email', # Str(100)
	'phone', # instance of PhoneNumber
	'fax', # instance of PhoneNumber

	# 4) Billing configuration
	'use_for_auto_payments', # Int
))

PhoneNumber = namedtuple('PhoneNumber', (
	'country_code', # Str(4)
	'area_code', # Str(10)
	'number', # Str(20)
	'extension', # Str(10)
))

PEMSubscription = namedtuple('PEMSubscription', (
	'subscription_id', # Int
	'plan_id', # Int    
	'expiration_date', # Int    
	'billing_period', # instance of BillingPeriod
	'last_bill_date', # Int
	'next_bill_date', # Int
))

BillingPeriod = namedtuple('BillingPeriod', (
	'type', # one of BillingPeriodType value
	'size'
))

BillingPeriodType = obj(DAY = 1, MONTH = 2, YEAR = 3)

User = namedtuple('User', (
	'account_id', # Int
	'login', # Str(64)
	'password', # Str(32)
	'first_name', # Str(30)
	'last_name', # Str(30)
	'email', # Str(100)
	'address1', # Str(80)
	'address2', # Str(80)
	'city', # Str(40)
	'state', # Str(80)
	'zip_code', # Str(10)
	'country', # Str(2)
	'phone', # instance of PhoneNumber
	'fax', # instance of PhoneNumber
))

SSLSubscription = namedtuple('SSLSubscription', (
	'SubscriptionID', # Int
	'ParentID', # Int
	'SubscriptionName', # Str(40)
	'Status', # Int 10 "Ordered", 15 "Trial", 30 "Active", 40 "Graced", 50 "Expired", 60 "Terminated", 70 "Canceled", 80 "Administrative Hold", 85 "Credit Hold", 89 "Credit+Administrative Hold"
	'startDate', # Int
	'ExpirationDate', # Int
	'AccountID', # Int
	'PlanID', # Int
	'Period', # Int
	'PeriodType', # Int 1  "Day(s)", 2  "Month(s)", 3  "Year(s)"
	'SetupFee', # Double
	'SubscriptionFee', # Double
	'RenewalFee', # Double
	'TransferFee', # Double
	'NonRefundableAmt', # Double
	'RefundPeriod', # Int
	'BillingPeriodType', # Int
	'BillingPeriod', # Int
	'LastBillDate', # Int
	'NextBillDate', # Int
	'DomainName', #Str
	'Certificate', #Str Services -> Certificates

	'SubscrParamValue', # Service properties, List of pairs <ParameterId, ParameterValue>
))

DomainSubscription = namedtuple('DomainSubscription', (
	'parent_id', # Int
	'status', # one of SubscriptionStatus
	'start_date', # Int
	'expiration_date', # Int
	'account_id', # Int
	'plan_id', # Int
	'subscription_period', # Int in Years
	'setup_fee', # Double
	'subscription_fee', # Double
	'renewal_fee', # Double
	'transfer_fee', # Double
	'non_refundable_amt', # Double
	'refund_period', # Int
	'billing_period_type', # Int
	'billing_period', # Int
	'last_bill_date', # Int
	'next_bill_date', # Int
	'full_domain_name', # Str(255)
	'login', # Str(40) login to registrar
	'password', # Str(32) password to registrar
	'nameservers', # instance of DomainSubscriptionNameServers
	'users', # instance of DomainSubscriptionUsers
))

DomainSubscriptionInfo = namedtuple('DomainSubscriptionInfo', (
	'subscription_id',
	'domain_id',
))

SubscriptionStatus = obj(
	ORDERED = 10,
	TRIAL = 15, 
	ACTIVE = 30, 
	GRACED = 40, 
	EXPIRED = 50, 
	TERMINATED = 60, 
	CANCELED = 70, 
	ADMINISTRATIVE_HOLD = 80, 
	CREDIT_HOLD = 85, 
	CREDIT_AND_ADMINISTRATIVE_HOLD = 89,
)

CertificateStatus = obj(
	ISSUED = 2,
)

DomainSubscriptionNameServers = namedtuple('DomainSubscriptionNameServers', (
	'primary', # Str(40)
	'secondary', # Str(40)
	'third', # Str(40)
	'fourth' # Str(40)
))

DomainSubscriptionUsers = namedtuple('DomainSubscriptionUsers', (
	'owner_id', # Int customer account user ID. The user contact information is used as owner contact by registrar;    
	'admin_id', # Int customer account user ID. The user contact information is used as administrative contact by registrar;
	'billing_id', # Int customer account user ID. The user contact information is used as billing contact by registrar;
	'tech_id', # Int customer account user ID. The user contact information is used as technical contact by registrar;
))

DummySubscription = namedtuple('DummySubscription', (
	'SubscriptionID', # Int
	'ParentID', # Int
	'SubscriptionName', # Str(40)
	'Status', # Int 10 "Ordered", 15 "Trial", 30 "Active", 40 "Graced", 50 "Expired", 60 "Terminated", 70 "Canceled", 80 "Administrative Hold", 85 "Credit Hold", 89 "Credit+Administrative Hold"
	'startDate', # Int
	'ExpirationDate', # Int
	'AccountID', # Int
	'PlanID', # Int
	'Period', # Int
	'PeriodType', # Int 1  "Day(s)", 2  "Month(s)", 3  "Year(s)"
	'SetupFee', # Double
	'SubscriptionFee', # Double
	'RenewalFee', # Double
	'TransferFee', # Double
	'NonRefundableAmt', # Double
	'RefundPeriod', # Int
	'BillingPeriodType', # Int
	'BillingPeriod', # Int
	'LastBillDate', # Int
	'NextBillDate', # Int
	'SubscrParamValue', # Service properties, List of pairs <ParameterId, ParameterValue>
))

BillingModel = obj(
	BILLING_MODEL_BBP = 10,	#before billing period
	BILLING_MODEL_ABP = 20,	#after billing period
	BILLING_MODEL_BSP = 30	#before subscription period
)

class PPABImportAPI(object):
	def __init__(self, url, user, password):
		self.url = url
		self.user = user
		self.password = password
		self.api = RawPPABAPI(url, user, password)

	def begin_transaction(self):
		return self.api.BeginTransaction()

	def commit_transaction(self):
		return self.api.CommitTransaction()

	def rollback_transaction(self):
		return self.api.RollbackTransaction()

	def import_account(self, account):
		"""
		account - instance of Account
		"""
		self.api.ImportAccountFromPEM_API(account.vendor_account_id, account.account_id, account.class_id, account.tax_reg_id, account.balance, account.tax_status)

	def import_credit_card(self, credit_card):
		"""
		credit_card - instance of CreditCard
		"""
		self.api.CreditCard2AccountAdd_API(
			credit_card.customer_id, 
			
			credit_card.card_holder_name, credit_card.card_number, credit_card.pay_system,
			credit_card.expiration_date, credit_card.cvv, credit_card.start_date, credit_card.issue_number, 
			
			credit_card.email, 
			credit_card.address1, credit_card.address2, credit_card.city, credit_card.state, credit_card.zip_code, credit_card.country, 
			credit_card.phone.country_code, credit_card.phone.area_code, credit_card.phone.number, credit_card.phone.extension, 
			credit_card.fax.country_code, credit_card.fax.area_code, credit_card.fax.number, credit_card.fax.extension, 

			credit_card.use_for_auto_payments
		)

	def import_pem_subscription(self, subscription):
		"""
		subscription - instance of PEMSubscription
		"""
		self.api.ImportSubscriptionFromPEM_API(
			subscription.subscription_id, subscription.plan_id, subscription.expiration_date,
			subscription.billing_period.size, subscription.billing_period.type, subscription.last_bill_date, subscription.next_bill_date
		)

	def import_ssl_subscription(self, subscription):
		if subscription.SubscriptionID == None:
			r = self.api.SubscriptionAdd_API(
				INT_NULL if subscription.ParentID == None else subscription.ParentID,
				subscription.SubscriptionName, subscription.Status,
				self._get_serv_status(subscription.Status), subscription.startDate, subscription.ExpirationDate, subscription.AccountID,
				subscription.PlanID, subscription.Period, subscription.PeriodType, subscription.SetupFee,
				subscription.SubscriptionFee, subscription.RenewalFee, subscription.TransferFee, subscription.NonRefundableAmt,
				subscription.RefundPeriod, subscription.BillingPeriodType, subscription.BillingPeriod, subscription.LastBillDate, subscription.NextBillDate
			)
			subscription_id = r.SubscriptionID
		else:
			subscription_id = subscription.SubscriptionID

			self.api.SubscriptionWithIDAdd_API(
				INT_NULL if subscription.SubscriptionID == None else subscription.SubscriptionID,
				INT_NULL if subscription.ParentID == None else subscription.ParentID,
				subscription.SubscriptionName, subscription.Status,
				self._get_serv_status(subscription.Status), subscription.startDate, subscription.ExpirationDate, subscription.AccountID,
				subscription.PlanID, subscription.Period, subscription.PeriodType, subscription.SetupFee,
				subscription.SubscriptionFee, subscription.RenewalFee, subscription.TransferFee, subscription.NonRefundableAmt,
				subscription.RefundPeriod, subscription.BillingPeriodType, subscription.BillingPeriod, subscription.LastBillDate, subscription.NextBillDate
			)

		if subscription.SubscrParamValue != None:
			for s in subscription.SubscrParamValue:
				self.api.SubscrParamValueUpdate_API(subscription_id, s[0], s[1])

			params = { key: value for (key, value) in subscription.SubscrParamValue }
			self.api.CertAdd(
				subscription_id,
				CertificateStatus.ISSUED,
				subscription.DomainName,
				params.get('CSRID', ""),
				params.get('PKEYID', ""),
				subscription.Certificate
			)
		return subscription_id

	def find_ssl_subscription_id(self, domain_name):
		ssl_certs = self.api.GetCertList(0, 0)
		for cert in ssl_certs:
			if cert.name == domain_name:
				return cert.subscription_id
		return None

	def add_domain_subscription(self, subscription):
		"""
		subscription - instance of DomainSubscription
		return subscription_info with two fields: subscription_id and domain_id
		"""
		return self.api.DomainSubscrAdd_API(
			INT_NULL if subscription.parent_id is None else subscription.parent_id, 
			subscription.status, self._get_serv_status(subscription.status),
			subscription.start_date, subscription.expiration_date, subscription.account_id, subscription.plan_id,
			subscription.subscription_period, subscription.setup_fee, subscription.subscription_fee, subscription.renewal_fee,
			subscription.transfer_fee, subscription.non_refundable_amt, subscription.refund_period, subscription.billing_period_type,
			subscription.billing_period, subscription.last_bill_date, subscription.next_bill_date, INT_NULL,
			subscription.full_domain_name, subscription.login, subscription.password, 
			subscription.nameservers.primary, subscription.nameservers.secondary, subscription.nameservers.third, subscription.nameservers.fourth, 
			subscription.users.owner_id, subscription.users.admin_id, subscription.users.billing_id, subscription.users.tech_id 
		)

	def add_domain_ext_data(self, domain_id, domain_ext_data):
		for key, value in domain_ext_data:
			if value is not None:
				self.api.DomainExtDataAdd_API(domain_id, key, str(value))

	def add_subscr_param_values(self, subscription_id, param_values):
		for param, value in param_values:
			if value is not None:
				self.api.SubscrParamValueUpdate_API(subscription_id, param, str(value))

	def get_domain_subscription_info(self, domain_name):
		domain_info = self.api.DomainInfoGet_API(domain_name)
		adomain_info = self.api.ADomainGet(domain_info.domain_id)
		return DomainSubscriptionInfo(subscription_id=adomain_info.subscription_id, domain_id=domain_info.domain_id)

	def plan_period_list_get(self, plan_id):
		return self.api.PlanPeriodListGet_API(plan_id, 1)

	def plan_details_get(self, plan_id):
		return self.api.PlanDetailsGet_API(plan_id)

	def get_users_list_for_account(self, account_id):
		return self.api.GetUsersListForAccount_API(account_id, 1)

	def get_user_details(self, user_id):
		return self.api.UserDetailsGet_API(user_id)

	def add_user(self, user):
		"""
		user - instance of User
		"""
		return self.api.UserAdd_API(
			user.account_id, INT_NULL, user.login, user.password,
			user.first_name, '', user.last_name, user.email, 
			user.address1, user.address2, user.city, user.state, user.zip_code, user.country,
			user.phone.country_code, user.phone.area_code, user.phone.number, user.phone.extension,
			user.fax.country_code, user.fax.area_code, user.fax.number, user.fax.extension, 
			0
		)

	def init_users_from_registrar_api(self, domain_id):
		"""
		return boolean:
		- True if user information was imported succesfully from domain registrars
		- False otherwise
		"""
		return self.api.InitUsersFromRegistrar_API(domain_id).result != 0

	def update_domain_contacts(self, domain_id, owner_id, admin_id, billing_id, tech_id):
		self.api.UpdateDomainContacts_API(domain_id, owner_id, admin_id, billing_id, tech_id)

	def import_dummy_subscription(self, subscription):
		self.api.SubscriptionWithIDAdd_API(
			subscription.SubscriptionID, INT_NULL if subscription.ParentID == None else subscription.ParentID, subscription.SubscriptionName, subscription.Status,
			self._get_serv_status(subscription.Status), subscription.startDate, subscription.ExpirationDate, subscription.AccountID,
			subscription.PlanID, subscription.Period, subscription.PeriodType, subscription.SetupFee,
			subscription.SubscriptionFee, subscription.RenewalFee, subscription.TransferFee, subscription.NonRefundableAmt,
			subscription.RefundPeriod, subscription.BillingPeriodType, subscription.BillingPeriod, subscription.LastBillDate, subscription.NextBillDate
		)

		if subscription.SubscrParamValue != None:
			for s in subscription.SubscrParamValue:
				self.api.SubscrParamValueUpdate_API(subscription.SubscriptionID, s[0], s[1])

	def get_next_order_amount(self, subscription_id, plan_id, period, period_type, next_bill_date, expiration_date):
		plan = self.api.PlanDetailsGet_API(plan_id)

		if plan.recurring_type == BillingModel.BILLING_MODEL_BSP:
			renew_amount = self.api.GetSubscriptionRenewOrderAmount_API(subscription_id, period, period_type, "")
			return renew_amount.fee

		elif plan.recurring_type == BillingModel.BILLING_MODEL_BBP:
			if next_bill_date >= expiration_date:
				renew_amount = self.api.GetSubscriptionRenewOrderAmount_API(subscription_id, period, period_type, "")
				bill_amount = self.api.GetSubscriptionBillOrderAmount_API(subscription_id)
				return renew_amount.fee + bill_amount.fee
			else:
				bill_amount = self.api.GetSubscriptionBillOrderAmount_API(subscription_id)
				return bill_amount.fee

		elif plan.recurring_type == BillingModel.BILLING_MODEL_ABP:
			if next_bill_date >= expiration_date:
				renew_amount = self.api.GetSubscriptionRenewOrderAmount_API(subscription_id, period, period_type, "")
				bill_amount = self.api.GetSubscriptionBillOrderAmount_API(subscription_id)
				return renew_amount.fee + bill_amount.fee
			else:
				bill_amount = self.api.GetSubscriptionBillOrderAmount_API(subscription_id)
				return bill_amount.fee

		else:
			raise Exception("The billing model (%s) of PPA Billing plan %s is not supported by the migration tool" % (plan.recurring_type, plan_id))

	def _get_serv_status(self, status):
		ServStatus = obj(NOT_PROVISIONED=10, STOPPED=30, RUNNING=50)

		if status == SubscriptionStatus.ORDERED:
			return ServStatus.NOT_PROVISIONED
		elif status in (SubscriptionStatus.TRIAL, SubscriptionStatus.ACTIVE, SubscriptionStatus.GRACED):
			return ServStatus.RUNNING 
		elif status in (
			SubscriptionStatus.EXPIRED, SubscriptionStatus.TERMINATED, SubscriptionStatus.CANCELED, 
			SubscriptionStatus.ADMINISTRATIVE_HOLD, SubscriptionStatus.CREDIT_HOLD,
			SubscriptionStatus.CREDIT_AND_ADMINISTRATIVE_HOLD
			):
			return ServStatus.STOPPED 

		return None 

	def get_version(self):
		return self.api.GetVersion_API()

	def get_cert_product(self, plan_id):
		plan = self.api.PlanGet(plan_id)
		cert_st = self.api.CertSTGet(plan.service_template_id)
		cert_product = self.api.CertProductGet(cert_st.cert_product_id)
		return cert_product
