import logging
import re
import posixpath
import ntpath
from xml.etree import ElementTree as et 

from parallels.common.utils.windows_utils import path_join as windows_path_join
from parallels.common.utils import windows_utils

logger = logging.getLogger(__name__)

def get_windows_vhosts_dir(runner):
	stdout = runner.run('reg', [
		'query', "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment", "/v", "plesk_vhosts"
	])
	match = re.search("^\s*plesk_vhosts\s+REG_(EXPAND_)?SZ\s+(.*?)\s*$", stdout, re.MULTILINE)
	if match is None:
		raise Exception(u"Failed to detect Plesk virtual hosts directory, reg query output is incorrect: %s", stdout)
	vhosts_dir = match.group(2)

	logger.debug(u"Windows vhosts directory: %s", vhosts_dir)
	return vhosts_dir 

def get_windows_plesk_dir(runner):
	stdout = runner.run('reg', [
		'query', "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment", "/v", "plesk_dir"
	])
	match = re.search("^\s*plesk_dir\s+REG_(EXPAND_)?SZ\s+(.*?)\s*$", stdout, re.MULTILINE)
	if match is None:
		raise Exception(u"Failed to detect Plesk directory, reg query output is incorrect: %s", stdout)
	plesk_dir = match.group(2)

	logger.debug(u"Windows Plesk directory: %s", plesk_dir)
	return plesk_dir 

def get_windows_dump_dir(runner):
	"""Get directory with Plesk backup dumps

	Arguments:
	- runner - instance of a some Windows runner from
	  parallels.common.run_command
	"""

	dump_d = _get_registry_string_key(
		runner, 
		"HKEY_LOCAL_MACHINE\SOFTWARE\PLESK\PSA Config\Config", 
		'DUMP_D'
	)
	if not dump_d:
		dump_d = _get_registry_string_key(
			runner, 
			# Difference from the 1st query - Wow6432Node		
			"HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\PLESK\PSA Config\Config", 
			'DUMP_D'
		)
	if not dump_d:
		# Fallback to %plesk_dir%\Backup
		product_root_d = get_windows_plesk_dir(runner)
		dump_d = ntpath.join(product_root_d, u'Backup')
	if not dump_d:
		raise Exception(
			u"Failed to detect directory with Plesk backup dumps"
		)
	return dump_d

def get_windows_vhost_dir(runner, vhost_name):
	vhosts_dir = get_windows_vhosts_dir(runner)

	vhost_name = vhost_name.encode('idna')
	vhost_dir = windows_path_join(vhosts_dir, vhost_name)
	logger.debug(u"Windows vhost directory: %s", vhost_dir)

	return vhost_dir

def get_unix_vhost_dir(runner, vhost_name):
	vhosts_dir = get_unix_vhosts_dir(runner)

	vhost_name = vhost_name.encode('idna')
	vhost_dir = posixpath.join(vhosts_dir, vhost_name)
	logger.debug(u"Unix vhost directory: %s", vhost_dir)

	return vhost_dir

def get_unix_vhost_system_dir(runner, vhost_name):
	vhosts_dir = get_unix_vhosts_dir(runner)

	vhost_name = vhost_name.encode('idna')
	vhost_dir = posixpath.join(vhosts_dir, 'system', vhost_name)
	logger.debug(u"Unix vhost system directory: %s", vhost_dir)

	return vhost_dir


def get_unix_vhosts_dir(runner):
	"""Get directory with virtual hosts on Plesk for Unix server"""
	return get_unix_conf_var(
		runner, 'HTTPD_VHOSTS_D', "Virtual hosts directory"
	)


def get_unix_mailnames_dir(runner):
	"""Get directory with mail messages on Plesk for Unix server"""
	return get_unix_conf_var(
		runner, '(PLESK|QMAIL)_MAILNAMES_D', 'Mail messages directory'
	)


def get_unix_dump_dir(runner):
	"""Get directory where Plesk stores backups on Plesk for Unix server"""
	return get_unix_conf_var(
		runner, 'DUMP_D', 'Backup dumps directory'
	)


def get_unix_product_root_dir(runner):
	"""Get directory where Plesk for Unix is installed"""
	return get_unix_conf_var(
		runner, 'PRODUCT_ROOT_D', 'Unix product root directory'
	)


def get_unix_domain_mail_dir(runner, domain_name):
	mailnames_dir = get_unix_mailnames_dir(runner)

	domain_name_idna = domain_name.encode('idna')
	domain_mail_dir = posixpath.join(mailnames_dir, domain_name_idna)
	logger.debug(u"Unix domain mailname directory for domain '%s': %s", domain_name, domain_mail_dir)

	return domain_mail_dir


def get_unix_conf_var(runner, var_name, description=None):
	"""Read variable from psa.conf config of Plesk for Unix

	Arguments:
	- runner - runner for Plesk for Unix server (from run_command module)
	- var_name - regular expression to match variable name
	- description - description of variable, just for debug logging

	Returns variable value.
	"""
	stdout = runner.run(
		'/bin/grep', ['-m1', '-E', '^%s' % var_name, '/etc/psa/psa.conf']
	)
	var_value = stdout.replace('\t', ' ').partition(' ')[2].strip().rstrip('/')
	if description is not None:
		logger.debug('%s: %s', description, var_value)
	else:
		logger.debug(
			u"Plesk for Unix configuration variable '%s' has value '%s'",
			var_name, var_value
		)
	return var_value


def get_windows_db_server_credentials(runner, db_type, db_host):
	plesk_dir = get_windows_plesk_dir(runner)

	# if port is not defined, we should specify '0'
	# otherwise database-registrar.php will fail
	if db_type == 'mssql' and ':' not in db_host:
		db_host = "%s:0" % db_host

	out = runner.sh(ur'cmd.exe /C "{php} -dauto_prepend_file="" {cu} --get-credentials %s -type %s"' % (db_host, db_type), dict(
		php=ur"%s\admin\bin\php" % (plesk_dir,),
		cu=ur"%s\admin\plib\cu\database-registrar.php" % (plesk_dir,),
	)).strip()
	xml = et.XML(out)
	return xml.findtext('username'), xml.findtext('password')

def get_unix_db_server_credentials(runner, db_type, db_host):
	plesk_dir = get_unix_product_root_dir(runner)
	out = runner.sh(
		ur'{plesk_dir}/bin/sw-engine-pleskrun {plesk_dir}/admin/plib/api-cli/database-registrar.php --get-credentials %s -type %s' % (db_host, db_type), dict(
		plesk_dir=plesk_dir
	)).strip()
	xml = et.XML(out)
	return xml.findtext('username'), xml.findtext('password')

def convert_wildcard_to_path(domain_name):
	if domain_name.startswith('*'):
		return '_%s' % (domain_name[1:],)
	else:
		return domain_name

def get_plesk_version(runner):
	version_str = runner.run('cat', ['/usr/local/psa/version'])
	return re.split('[\. ]', version_str)

def get_migrator_root_path(migrator_module):
	"""Get path to package root directory."""
	dirs = [p for p in migrator_module.__path__]
	assert all(d == dirs[0] for d in dirs)
	return dirs[0]

def set_mime_types(server, vhost_name, mime_types, vdir_name=None):
	"""Set MIME types of virtual host on Windows Plesk server

	Arguments:
	- server - instance of Windows target server (PPATargetServer or
	  PleskTargetServer)
	- vhost_name - virtual host name (subscription or addon domain)
	- mime_types - dictionary of mime types (key - extension, value - mime
	  type)
	- vdir_name - virtual directory to set mime type on; if None - mime types
	  are set on whole server
	"""
	vhost_name = vhost_name.encode('idna')

	with server.runner() as runner:
		mimetypes_file = None
		if server.plesk_version >= (12, 0):
			mimetypes_file = server.get_session_file_path(
				'%s.mimetypes' % vhost_name
			)
			runner.upload_file_content(
				mimetypes_file,
				_mime_types_as_string(mime_types)
			)
			mimetypes_arg = mimetypes_file
		else:
			mimetypes_arg = _mime_types_as_string(mime_types)

		runner.sh(
			_get_webservermng_command(
				_get_webservermng_path(server),
				'set-mime-types',
				args={
					'vhost-name':vhost_name,
					'vdir-name':vdir_name,
					'mime-types':mimetypes_arg
				}
			)
		)

		if mimetypes_file is not None:
			runner.remove_file(mimetypes_file)

def get_mime_types(server, vhost_name, vdir_name=None):
	"""Get MIME types of virtual host on Windows Plesk server

	Arguments:
	- server - instance of Windows target server (PPATargetServer or
	  PleskTargetServer)
	- vhost_name - virtual host name (subscription or addon domain)
	- vdir_name - virtual directory name
	"""
	vhost_name = vhost_name.encode('idna')
	with server.runner() as runner:
		mime_types_str = runner.sh(
			_get_webservermng_command(
				_get_webservermng_path(server),
				'get-mime-types',
				args={	
					'vhost-name':vhost_name,
					'vdir-name':vdir_name,
				}
			)
		)
	return _parse_mime_types_string(mime_types_str)

def get_error_documents(server, vhost_name, vdir_name=None):
	"""Get error documents of virtual host on Windows Plesk server

	Arguments:
	- server - instance of Windows target server (PPATargetServer or
	  PleskTargetServer)
	- vhost_name - virtual host name (subscription or addon domain)
	- vdir_name - virtual directory name
	"""
	vhost_name = vhost_name.encode('idna')

	with server.runner() as runner:
		error_documents_str = runner.sh(
			_get_webservermng_command(
				_get_webservermng_path(server),
				'get-error-docs',
				args={	
					'vhost-name':vhost_name,
					'vdir-name':vdir_name
				}
			)
		)
	return error_documents_str

def get_vdir_info(server, vhost_name):
	websrvmng_path = windows_path_join(server.plesk_dir, ur'admin\bin\websrvmng')
	with server.runner() as runner:
		vdir_info = runner.sh('{websrvmng_path} --list-vdirs --vhost-name={vhost_name}', dict(
			websrvmng_path=websrvmng_path, vhost_name=vhost_name.encode('idna')
		))
	return vdir_info

def _mime_types_as_string(mime_types):
	return u"".join([ 
		u"%s=%s;" % (ext, mime_type) 
		for ext, mime_type in mime_types.iteritems() 
	])

def _parse_mime_types_string(mime_types_string):
	mime_types = {}
	for mime_type_str in mime_types_string.split(';'):
		mime_type_str = mime_type_str.strip()
		if mime_type_str == '':
			continue
		ext, mime_type = mime_type_str.split('=')
		mime_types[ext] = mime_type
	return mime_types

def _get_webservermng_path(server):
		target_plesk_dir = server.plesk_dir
		return windows_path_join(
			target_plesk_dir,  ur'admin\bin\websrvmng'
		)

def _get_webservermng_command(websrvmng_path, action, args):
	command = '{websrvmng_path} --{action}'.format(
		websrvmng_path=windows_utils.quote_arg(
			websrvmng_path
		),
		action=action
	)

	for key, value in args.iteritems():
		if value is not None:
			command += ' --{key}={value}'.format(
				key=key,
				value=windows_utils.quote_arg(value)
			)

	return command

def _get_registry_string_key(runner, path, name):
	_, stdout, stderr = runner.run_unchecked('reg', [
		'query', 
		path, "/v", name
	])
	error_signature = (
		'ERROR: The system was unable to find the '
		'specified registry key or value.'
	)
	if error_signature in stderr:
		return None

	match = re.search(
		"^\s*%s\s+REG_(EXPAND_)?SZ\s+(.*?)\s*$" % (re.escape(name)), 
		stdout, re.MULTILINE
	)
	if match is None:
		return None
	return match.group(2)

def convert_wildcard_domain_path(domain_path):
	"""Convert wildcard domain path: in domain physical path '*' symbol replaced to '_'.

	Arguments:
	- domain_path - vhost path to domain
	"""
	return domain_path.replace('*', '_')
