from parallels.pvps import messages
import logging
import uuid

from parallels.core import MigrationError
from parallels.core.utils.steps_profiler import sleep
from parallels.ppa.import_api.model import WebspaceStatus
from parallels.pvps.models.existing_objects_model import PVPSSubscription
from parallels.core.utils.common import poll_data, polling_intervals
from parallels.core.actions.base.subscription_action import SubscriptionAction


logger = logging.getLogger(__name__)


class CreatePVPSSubscription(SubscriptionAction):
	def get_description(self):
		return messages.CREATE_PVPS_SUBSCRIPTION_IN_TARGET_PANEL

	def get_repeat_count(self):
		# Do not repeat create PVPS subscription action in case of any failure.
		#
		# If we simply repeat it, several PVPS subscriptions could be created.
		# For example, if activate subscription POA API call finished successfully, but
		# some POA task failed at the first creation of subscription, exception will
		# be thrown, action runner will try to run the action again.
		# So, activate subscription API method will be called once more.
		# 
		# It is possible to catch an issue before or at activate subscription API call,
		# but probability is rather low, so it is reasonable just
		# not to repeat the action in case of failure.
		return 1

	def get_failure_message(self, global_context, subscription):
		"""
		:type global_context: parallels.core.global_context.GlobalMigrationContext
		:type subscription: parallels.core.migrated_subscription.MigratedSubscription
		"""
		return (
			messages.FAILED_TO_CREATE_PVPS_SUBSCRIPTION)

	def filter_subscription(self, global_context, subscription):
		"""
		:type global_context: parallels.core.global_context.GlobalMigrationContext
		:type subscription: parallels.core.migrated_subscription.MigratedSubscription
		"""
		return True

	def run(self, global_context, subscription):
		"""
		:type global_context: parallels.core.global_context.GlobalMigrationContext
		:type subscription: parallels.core.migrated_subscription.MigratedSubscription
		"""

		if subscription.model.target_subscription is not None:
			if not global_context.target_panel_obj.is_target_server_installed(
				global_context.conn.target.poa_api(),
				subscription.model.target_subscription.id
			):
				raise MigrationError(
					messages.UNABLE_TO_RETRIEVE_INFO_ABOUT_PVPS.format(
						name=subscription.model.target_subscription.name,
						id=subscription.model.target_subscription.id
					)
				)
			else:
				return

		owner_id = subscription.model_client.target_client.id
		plan_id = subscription.model.plan_id

		logger.info(
			messages.CREATE_VPS_SUBSCRIPTION_FOR_CUSTOMER,
			subscription.name, subscription.model_client.login
		)

		subscription_id, request_id = self._activate_subscription(
			global_context, owner_id, plan_id, subscription.model
		)

		self._wait_for_provisioning(global_context, subscription.name, request_id)
		subscription.model.target_subscription = PVPSSubscription(
			id=subscription_id, name=subscription.name, owner_id=owner_id, plan_id=plan_id
		)

	@staticmethod
	def _wait_for_provisioning(global_context, subscription_name, request_id, timeout=60*60):
		"""
		:type global_context: parallels.core.global_context.GlobalMigrationContext
		"""

		def get_status():
			request_status, status_message = global_context.import_api.get_request_status(request_id)
			if request_status == WebspaceStatus.FAILED:
				raise MigrationError(messages.PROVISIONING_FAILED % (subscription_name, status_message))

			return request_status if request_status != WebspaceStatus.PENDING else None

		status = poll_data(
			get_status,
			polling_intervals(
				starting=5,
				max_attempt_interval=60,
				max_total_time=timeout
			),
			sleep=lambda t: sleep(t, messages.WAITING_FOR_PVPS_PROVISIONING)
		)

		if status is None:  # WebspaceStatus.PENDING
			raise MigrationError(messages.TIMED_OUT_WAITING_UNTIL_PVPS_PROVISIONING % subscription_name)

	def _activate_subscription(self, global_context, account_id, plan_id, subscription):
		"""
		:type global_context: parallels.core.global_context.GlobalMigrationContext
		"""
		poa_api = global_context.conn.target.poa_api()

		request_id = uuid.uuid4()
		txn_id = poa_api.txnBegin(request_id)
		subscription_id = poa_api.activate_subscription(
			account_id, plan_id, subscription_name=subscription.name, txn_id=txn_id
		)
		poa_api.txnCommit(txn_id)
		return subscription_id, request_id
