from parallels.core import messages
from parallels.core import MigrationError
from parallels.core import version_tuple
from parallels.plesk.utils.xml_rpc.plesk import operator as plesk_ops
from parallels.plesk.utils.xml_rpc.plesk.core import PleskError, PleskErrorCode
from parallels.plesk.utils.xml_rpc.plesk.operator import ServicePlanOperator


def get_plesk_version(plesk_api):
	result = plesk_api.send(
		plesk_ops.server.ServerOperator.Get([
			plesk_ops.server.ServerOperator.Dataset.STAT,
		])
	)
	return result.data.statistics.versions.plesk_version


def get_subscription_sysuser_name(plesk_api, subscription_name):
	hosting = plesk_api.send(plesk_ops.SubscriptionOperator.Get(
		filter=plesk_ops.SubscriptionOperator.FilterByName([subscription_name]),
		dataset=[plesk_ops.SubscriptionOperator.Dataset.HOSTING]
	))[0].data[1].hosting
	if isinstance(hosting, plesk_ops.SubscriptionHostingVirtual):
		return hosting.properties.get('ftp_login')
	else:
		return None


def list_mail_users(plesk_api, domain_name):
	try:
		site_id = plesk_api.send(
			plesk_ops.SiteOperator.Get(
				dataset=[plesk_ops.SiteOperator.Dataset.GEN_INFO],
				filter=plesk_ops.SiteOperator.FilterByName([domain_name])
			)
		)[0].data[0]
	except PleskError as e:
		# webspace with such name does not exist - simply return empty list
		if e.code == PleskErrorCode.OBJECT_DOES_NOT_EXIST: 
			return []
		else:
			raise

	api_response = plesk_api.send(
		plesk_ops.MailOperator.Get(
			mailbox=[plesk_ops.MailOperator.Dataset.GEN_INFO],
			filter=plesk_ops.MailOperator.FilterBySiteId([site_id])
		)
	)

	list_mail_users = []
	for mail_response in api_response:
		list_mail_users.append(mail_response.data.name)

	return list_mail_users


def list_databases(plesk_api, domain_name, database_type):
	"""List databases registered in panel for specified domain
	
	Arguments:
	- plesk_api - Plesk API client, instance of
	  parallels.plesk.utils.xml_rpc.plesk.client.Client
	- domain_name - name of domain to list databases
	- database_type - type of databases to filter ('mysql' or 'mssql' or
	  'postgresql')

	Returns:
		list of tuples, first element - database name, second element - 
		list of related database user logins
	"""
	try:
		operator = plesk_ops.DatabaseOperator.GetDb(
			filter=plesk_ops.DatabaseOperator.GetDb.FilterByWebspaceName([
				domain_name
			])
		)
		api_response = plesk_api.send(operator)

		db_list = []
		for database_api_response in api_response:
			if database_type == database_api_response.data.type:
				users = _list_database_users(
					plesk_api, database_api_response.data.id)
				db_list.append((database_api_response.data.name, users))
		return db_list
	except PleskError as e:
		# webspace with such name does not exist, or there are no databases  -
		# simply return empty list
		if e.code in (
			PleskErrorCode.OBJECT_DOES_NOT_EXIST, 
			PleskErrorCode.OWNER_DOES_NOT_EXIST
		): 
			return []
		else:
			raise


def change_dedicated_iis_app_pool_state(plesk_api, subscription_name, state):
	"""Switch on or off dedicated application pool on subscription

	Arguments:
	- plesk_api - Plesk API client, instance of
	  parallels.plesk.utils.xml_rpc.plesk.client.Client
	- subscription_name - name of Windows-based subscription to change state of
	  dedicated application pool on
	- state - boolean, True to switch application pool on, False to switch
	  application off
	"""
	result = plesk_api.send(
		plesk_ops.SubscriptionOperator.Set(
			filter=plesk_ops.SubscriptionOperator.FilterByName([
				subscription_name
			]),
			hosting=plesk_ops.SubscriptionHostingVirtual(
				properties=[
					('iis_app_pool', 'true' if state else 'false'),
				],
				ip_addresses=None # do not change
			),
			limits=None,
		)
	)

	if len(result) != 1 or not result[0].ok:
		raise MigrationError(
			messages.FAILED_S_IIS_DEDICATED_APPLICATION_POOL % (
				'enable' if state else 'disable'
			)
		)


def is_subscription_suspended(plesk_api, subscription_name):
	if version_tuple(plesk_api.api_version) <= (1,6,0,2):
		if version_tuple(plesk_api.api_version) < (1,6,0,0):
			filter = plesk_ops.DomainOperator.FilterByName8([subscription_name])
		else:
			filter = plesk_ops.DomainOperator.FilterByDomainName([subscription_name])
		return plesk_api.send(
			plesk_ops.DomainOperator.Get(
				filter=filter,
				dataset=[plesk_ops.DomainOperator.Dataset.GEN_INFO]
			)
		)[0].data[1].gen_info.status != 0
	else:
		return plesk_api.send(plesk_ops.SubscriptionOperator.Get(
			filter=plesk_ops.SubscriptionOperator.FilterByName([subscription_name]),
			dataset=[plesk_ops.SubscriptionOperator.Dataset.GEN_INFO]
		))[0].data[1].gen_info.status != 0


def check_if_subscription_exists(plesk_api, subscription_name):
	"""
	:type plesk_api: parallels.plesk.utils.xml_rpc.plesk.client.Client
	:type subscription_name: basestring
	"""
	request = plesk_ops.SubscriptionOperator.Get(
		plesk_ops.SubscriptionOperator.FilterByName([subscription_name]),
		[
			plesk_ops.SubscriptionOperator.Dataset.GEN_INFO,
		]
	)
	results = plesk_api.send(request)

	for result in results:
		if not hasattr(result, 'code') or result.code != 1013:
			if result.data[1].gen_info.name == subscription_name:
				return True

	return False


def get_service_template_allowed_hosting_settings(plesk_api):
	"""Get set of allowed hosting settings for hosting service templates on specified Plesk

	:type plesk_api: parallels.plesk.utils.xml_rpc.plesk.client.Client
	:rtype: set[basestring]
	"""
	request = ServicePlanOperator.GetPhysicalHostingDescriptor(
		filter=ServicePlanOperator.FilterAll()
	)
	result = plesk_api.send(request)
	return {prop.name for prop in result.data.properties}


def get_service_template_allowed_permissions(plesk_api):
	"""Get set of allowed permissions for hosting service templates on specified Plesk

	:type plesk_api: parallels.plesk.utils.xml_rpc.plesk.client.Client
	:rtype: set[basestring]
	"""
	request = ServicePlanOperator.GetPermissionDescriptor(
		filter=ServicePlanOperator.FilterAll()
	)
	result = plesk_api.send(request)
	return {prop.name for prop in result.data.properties}


def get_service_template_allowed_limits(plesk_api):
	"""Get set of allowed limits for hosting service templates on specified Plesk

	:type plesk_api: parallels.plesk.utils.xml_rpc.plesk.client.Client
	:rtype: set[basestring]
	"""
	request = ServicePlanOperator.GetLimitDescriptor(
		filter=ServicePlanOperator.FilterAll()
	)
	result = plesk_api.send(request)
	return {prop.name for prop in result.data.properties}


def get_subscription_db_servers(plesk_api, subscription_name):
	request = plesk_ops.SubscriptionOperator.DbServers.List(
		plesk_ops.SubscriptionOperator.FilterByName([subscription_name])
	)
	return request_single_optional_item(plesk_api, request, default=[])


def request_single_optional_item(plesk_api, request, default=None):
	response = plesk_api.send(request)
	for result in response:
		if not hasattr(result, 'code') or result.code != 1013:
			return result.data

	return default


def _list_database_users(plesk_api, database_id):
	operator = plesk_ops.DatabaseOperator.GetDbUsers(
		filter=plesk_ops.DatabaseOperator.GetDbUsers.FilterByDbIds([
			database_id
		])
	)
	api_response = plesk_api.send(operator)
	return [
		user_api_response.data.login
		for user_api_response in api_response
	]
