from parallels.core.utils.common_constants import SSH_DEFAULT_PORT
from parallels.pvps import messages
from parallels.core import MigrationError, MigrationNoContextError
from parallels.core.migrator_config import SSHAuthPassword
from parallels.core.target_panel_base import TargetPanelBase
from parallels.core.utils.config_utils import Auth
from parallels.ppa.connections.target_connections import PPATargetConnections
from parallels.ppa.import_api.import_api import WebspacesRequestsHolder
from parallels.pvps.connections.target_server import PleskInVPSTargetServer, PleskInVPSWindowsSettings
from parallels.pvps.connections.target_server import PleskInVPSUnixSettings
from parallels.pvps.import_api.import_api import PVPSImportAPI
from parallels.pvps.utils import poa_pvps_utils
from parallels.pvps.utils.poa_pvps_utils import get_panel_aps_status
from parallels.core.utils.common import cached, generate_random_password, find_only
from parallels.plesk.panel import Panel as TargetPanelPlesk


class Panel(TargetPanelBase):
	def name(self):
		return messages.PVPS_TARGET_TITLE

	def has_custom_subscriptions_feature(self):
		"""Whether subscriptions not assigned to plan are allowed"""
		return False

	def has_admin_reseller_plan_feature(self):
		"""Whethere admin reseller plans are supported for that target panel migration

		:rtype: bool
		"""
		return False

	def has_admin_subscriptions_feature(self):
		"""Whether subscriptions assigned directly to admin are allowed"""
		return False

	def has_reseller_subscriptions_feature(self):
		"""Whether subscriptions assigned directly to reseller are allowed"""
		return False

	def has_dns_forwarding(self):
		"""Whether panel should support DNS forwarding migration feature"""
		return False

	def is_transfer_resource_limits_by_default(self):
		"""Whether resource limits should be transferred by default"""
		return False

	def is_write_failed_subscriptions_list(self):
		"""Whether to write failed subscriptions list file after migration

		:rtype: bool
		"""
		return False

	def use_source_plans(self):
		"""Return True if we should use plans (service templates) of source panel, False otherwise"""
		return False

	@cached
	def get_subscription_target_services(self, global_context, subscription_name):
		"""Get location of subscriptions's hosting services on target panel

		:type global_context: parallels.core.global_context.GlobalMigrationContext
		:type subscription_name: basestring
		:rtype: parallels.core.subscription_target_info.TargetServices
		"""
		return TargetPanelPlesk().get_subscription_target_services(global_context, subscription_name)

	def get_subscription_nodes(self, global_context, subscription_name):
		"""Get servers of subscription on target panel

		:type global_context: parallels.core.global_context.GlobalMigrationContext
		:type subscription_name: basestring
		:rtype: parallels.core.subscription_target_info.TargetServers
		"""
		return TargetPanelPlesk().get_subscription_nodes(global_context, subscription_name)

	def get_subscription_plesk_node(self, global_context, subscription_name):
		return self._get_plesk_node(global_context, subscription_name)

	@cached
	def _get_plesk_node(self, global_context, subscription_name):
		"""
		:type global_context: parallels.core.global_context.GlobalMigrationContext
		:type subscription_name: basestring
		"""
		model_subscription = find_only(
			global_context.target_model.iter_all_subscriptions(),
			lambda s: s.name == subscription_name,
			messages.FAILED_TO_FIND_SUBSCRIPTION_IN_MODEL)
		if model_subscription.target_subscription is None:
			return None
		subscription_id = model_subscription.target_subscription.id
		if self.is_target_server_installed(global_context.conn.target.poa_api(), subscription_id):
			return PleskInVPSTargetServer(self._get_target_server_settings(global_context, subscription_id))

		raise MigrationError(
			messages.UNABLE_TO_RETRIEVE_INFO_ABOUT_PLESK_FOR_SUBSCRIPTION.format(
				name=subscription_name, id=subscription_id
			)
		)

	def get_service_nodes(self, conn):
		# TODO list service nodes for service checks
		return []

	def get_connections(self, global_context):
		"""Get target panel connections"""
		return PPATargetConnections(global_context.config, default_apache_restart_interval='1800')

	def get_import_api(self, global_context):
		return PVPSImportAPI(
			global_context.conn.target,
			WebspacesRequestsHolder(
				global_context.migrator_server.get_session_file_path('webspaces_requests.yaml')
			)
		)

	def get_hosting_check_messages_panel_id(self):
		return 'plesk'

	@staticmethod
	def is_target_server_installed(poa_api, subscription_id):
		"""
		:type poa_api: parallels.ppa.utils.xml_rpc.poa.poa_api.Client
		"""
		return get_panel_aps_status(poa_api, subscription_id) == u'aps:ready'

	def check_version(self, global_context):
		"""Check that target panel version is ok for migration purposes. Raise MigrationError othewise.

		Raised exception should contain information about supported versions for migration.

		:type global_context: parallels.core.global_context.GlobalMigrationContext
		:rtype: None
		"""
		plesk_version = global_context.conn.target.plesk_server.get_plesk_version()
		plesk_version_str = '.'.join([str(i) for i in plesk_version])

		if plesk_version < (11, 6):
			raise MigrationNoContextError(
				messages.PVPS_MIGRATION_NOT_SUPPORTED_FOR_OLD_PPA % (
					plesk_version_str
				)
			)

	@staticmethod
	def _change_vps_password(global_context, subscription_id):
		"""
		:type global_context: parallels.core.global_context.GlobalMigrationContext
		:rtype string
		:return new generated password of VPS
		"""
		password = generate_random_password()
		poa_api = global_context.conn.target.poa_api()
		poa_pvps_utils.set_pvps_password(poa_api, subscription_id, password)
		return password

	@staticmethod
	def _get_vps_platform_and_ip(global_context, subscription_id):
		"""
		:type global_context: parallels.core.global_context.GlobalMigrationContext
		"""
		poa_api = global_context.conn.target.poa_api()
		app_instance_id = poa_api.get_subscription_app_instances(subscription_id)
		app_settings = poa_api.get_app_instance_settings(app_instance_id)
		plesk_ip = app_settings['pleskPublicIp'].pop()
		platform = app_settings['configurationDetails.platform'].pop()
		return platform, plesk_ip

	def _get_target_server_settings(self, global_context, subscription_id):
		"""
		:type global_context: parallels.core.global_context.GlobalMigrationContext
		"""
		vps_platform, vps_ip = self._get_vps_platform_and_ip(global_context, subscription_id)
		vps_password = self._change_vps_password(global_context, subscription_id)
		if vps_platform == 'linux':
			return PleskInVPSUnixSettings(
				ip=vps_ip,
				ssh_auth=SSHAuthPassword(
					port=SSH_DEFAULT_PORT,
					username='root',
					password=vps_password
				),
				panel_auth=Auth(username='root', password=vps_password),
				session_dir=global_context.conn.target.unix_session_dir
			)
		else:
			auth = Auth(username='administrator', password=vps_password)
			return PleskInVPSWindowsSettings(
				ip=vps_ip,
				windows_auth=auth,
				panel_auth=auth,
				session_dir=global_context.conn.target.windows_session_dir
			)
