import logging
import uuid

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


logger = logging.getLogger(__name__)


class CreatePVPSSubscription(SubscriptionAction):
	def get_description(self):
		return "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.common.global_context.GlobalMigrationContext
		:type subscription: parallels.common.migrated_subscription.MigratedSubscription
		"""
		return (
			"Failed to create PVPS subscription in target panel, "
			"migration can not proceed to the next steps"
		)

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

	def run(self, global_context, subscription):
		"""
		:type global_context: parallels.common.global_context.GlobalMigrationContext
		:type subscription: parallels.common.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(
					u'Unable to retrieve information about the Plesk in VPS installation for this subscription.\n'
					u'Please check failed tasks in the Task Manager: \n'
					u'    Go to Provider\'s Panel > Operations > Tasks, and see if there are failed tasks '
					u'for the subscription "{name}" (id:{id}).\n'
					u'    Resolve issues and re-run the failed tasks. '
					u'After the tasks were completed, start the migration again.'.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(
			u"Create VPS subscription '%s' for customer '%s'",
			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.common.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(u"Provisioning of '%s' is failed: %s" % (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, 'Waiting for Plesk in VPS provisioning...')
		)

		if status is None:  # WebspaceStatus.PENDING
			raise MigrationError(u"Timed out waiting until Plesk in VPS %s is provisioned" % subscription_name)

	def _activate_subscription(self, global_context, account_id, plan_id, subscription):
		"""
		:type global_context: parallels.common.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
