"""Command line interface for migration between different panel instances"""

import argparse

from parallels.common.cli.common_cli import Command, CommandTypes, \
	MigratorHelpCommand, MigratorShellCommand, MigratorScriptCommand, \
	create_arguments_parser, run as run_migrator
from parallels.common import imapsync
from parallels.common import MigrationConfigurationFileError
from parallels.utils import obj, get_executable_file_name
from parallels.common.utils.steps_profiler import get_default_steps_profiler 
from parallels.common.utils.steps_profiler import get_default_steps_report


def run(script_dir, args):
	try:
		with get_default_steps_profiler().measure_time(
			'migration', 'Migration'
		):
			return run_migrator(
				script_dir=script_dir,
				args_parser=_create_migrator_arguments_parser(script_dir),
				create_migrator_func=lambda config: _create_migrator(config),
				args=args,
			)
	finally:
		get_default_steps_report().save_reports()


def _create_migrator_arguments_parser(script_dir):
	indent = ' ' * 4

	# ************************** Common options *******************************
	reload_source_data_opt = argparse.ArgumentParser(add_help=False)
	reload_source_data_opt.add_argument(
		'--reload-source-data', action='store_true', 
		help=u'Force fetching data from source panels, do not use already fetched data.'
	)
	# reverse "reload-source-data" option for fetch micro command,
	# which should ignore caches by default
	use_cached = argparse.ArgumentParser(add_help=False)
	use_cached.add_argument(
		'--use-cached', action='store_false', dest='reload_source_data',
		help=(
			u'Do not use reload source data if it was already fetched. '
		)
	)

	migration_list_file_opt = argparse.ArgumentParser(add_help=False)
	migration_list_file_opt.add_argument(
		'--migration-list-file', 
		help=u'Migration list filename, default value is $session_dir/migration-list'
	)
	ip_mapping_file_opt = argparse.ArgumentParser(add_help=False)
	ip_mapping_file_opt.add_argument(
		'--ip-mapping-file',
		help=u'IP mapping filename. This file contains source IP to target IP mapping.'
	)

	services_checks_opts = argparse.ArgumentParser(add_help=False)
	services_checks_opts.add_argument(
		'--skip-services-checks', action='store_true', 
		help=u'Do not check services for any issues that may affect migration'
	)
	convert_opts = argparse.ArgumentParser(add_help=False)
	convert_opts.add_argument(
		'--ignore-pre-migration-errors', action='store_true', 
		help=(
			u'Ignore pre-migration errors and continue migration even if there are some of them. '
			u'Do not use it, it is left for use by support only.'
		)
	)
	convert_opts.add_argument(
		'--allocate-only-required-resources', action='store_true', 
		help=(
			u"Switch to the strict mode in which new webspaces are created using "
			u"only the resources required for the transfer of the corresponding subscriptions."
		)
	)

	license_check_opts = argparse.ArgumentParser(add_help=False)
	license_check_opts.add_argument(
		'--skip-license-checks', action='store_true', 
		help=u'Do not check any licenses on PPA'
	)
	infrastructure_checks_opts = argparse.ArgumentParser(add_help=False)
	infrastructure_checks_opts.add_argument(
		'--skip-infrastructure-checks', action='store_true', 
		help=u'Do not check infrastructure for any issues that may affect migration'
	)
	infrastructure_checks_opts.add_argument(
		'--skip-main-node-disk-space-checks', action='store_true', 
		help=u'Do not check disk space requirements for the main node'
	)

	test_dns_opts = argparse.ArgumentParser(add_help=False)
	test_dns_opts.add_argument(
		'--skip-dns-forwarding-test', action='store_true', 
		help=u'Do not check that DNS queries for the transferred domains are correctly forwarded to PPA DNS servers'
	)

	fetch_options_opts = argparse.ArgumentParser(add_help=False)
	fetch_options_opts.add_argument(
		'--ignore-fetch-source-errors', action='store_true',
		help=u'Ignore errors on fetch-source stage of H-Sphere migration'
	)

	remove_webspace_id_opts = argparse.ArgumentParser(add_help=False)
	remove_webspace_id_opts.add_argument(
		'--webspace-id', action='store',
		help=u'Webspace ID to remove'
	)

	get_options_opts = argparse.ArgumentParser(add_help=False)
	get_options_opts.add_argument('--command')

	suspend_opts = argparse.ArgumentParser(add_help=False)
	suspend_opts.add_argument(
		'--accept-automatic-deletion', action='store_true',
		help=u'Suspend the accounts in H-Sphere even if H-Sphere is configured to delete the suspended accounts'
	)

	import_opts_list = [
		convert_opts,
		migration_list_file_opt, ip_mapping_file_opt,
		reload_source_data_opt, fetch_options_opts
	]

	commands = []
	migrator_command_name = get_executable_file_name()

	commands.extend([
		# ************************** Macro-commands ***************************
		# "Macro" commands are commands that could be used by end-user of
		# migrator. Examples are: transfer-accounts, check, copy-mail-content,
		# test-all, etc.

		# =========== Preparing to transfer: migration list and pre-checks ====
		Command(
			'generate-migration-list', CommandTypes.MACRO,
			u"Generate migration list file. "
			u"This file could be used to migrate only a specified set of plans and subscriptions, "
			u"and to map subscriptions to plans. Comment out all objects that you don't want to be migrated",
			lambda m, o: m.run('generate-migration-list'),
			[
				migration_list_file_opt, 
				reload_source_data_opt, 
				fetch_options_opts, 
				services_checks_opts, 
				license_check_opts,
			],
			[
				['--overwrite', u'Overwrite file if it already exists', 'store_true']
			]
		),
		# Run pre-migration checks, called by 'panel-transfer check'
		Command(
			'check', CommandTypes.MACRO,
			u"Check for potential migration issues", 
			lambda m, o: m.run('check'),
			import_opts_list + [
				services_checks_opts,
				license_check_opts, 
			],
		),
		# Run hosting OS resources and file content analysis, called by 'panel-transfer analyse-hosting'
		Command(
			'analyse-hosting', CommandTypes.MACRO,
			u"Analyse source and destination nodes for using effective migration strategy",
			lambda m, o: m.run('analyse-hosting'),
		),
		# ===================== Main transfer commands ========================
		Command(
			'import-resellers', CommandTypes.MACRO,
			u"Import resellers. "
			u"Note that only contact data is imported, and you should assign resellers to resellers plans manually",
			lambda m, o: m.run('import-resellers'),
			import_opts_list + [
				services_checks_opts,
				license_check_opts,
			]
		),
		Command(
			'import-plans', CommandTypes.MACRO,
			u"Import service plans", 
			lambda m, o: m.run('import-plans'),
			import_opts_list + [
				services_checks_opts,
				license_check_opts,
			]
		),
		# The main transfer operation called by 'panel-transfer transfer-accounts'
		Command(
			'transfer-accounts', CommandTypes.MACRO,
			u"Perform accounts transfer",

			lambda m, o: m.run('transfer-accounts'),
			import_opts_list + [
				license_check_opts,
				infrastructure_checks_opts,
				services_checks_opts
			]
		),
		# ===================== H-Sphere billing commands =====================
		Command(
			'transfer-billing', CommandTypes.MACRO,
			u"Transfer billing data from H-Sphere",
			lambda m, o: m.transfer_billing(o),
			import_opts_list + [
				suspend_opts,
				license_check_opts,
			]
		),
		Command(
			'check-billing', CommandTypes.MACRO,
			u"Check if billing data can be successfully transferred from H-Sphere",
			lambda m, o: m.check_billing(o),
			import_opts_list + [
				suspend_opts,
				license_check_opts,
			]
		),
		# ===================== Copy content commands =========================
		Command(
			'copy-content', CommandTypes.MACRO,
			u"Copy hosting content to destination servers",
			lambda m, o: m.run('copy-content'),
			import_opts_list + [
				services_checks_opts,
				license_check_opts,
			]
		),
		Command(
			'copy-web-content', CommandTypes.MACRO,
			u"Copy web content",
			lambda m, o: m.run('copy-web-content'),
			import_opts_list + [
				services_checks_opts,
				license_check_opts,
			]
		),
		Command(
			'copy-mail-content', CommandTypes.MACRO,
			u"Copy mail content",
			lambda m, o: m.run('copy-mail-content'),
			import_opts_list + [
				services_checks_opts,
				license_check_opts,
			]
		),
		Command(
			'copy-db-content', CommandTypes.MACRO,
			u"Copy databases content",
			lambda m, o: m.run('copy-db-content'),
			import_opts_list + [
				services_checks_opts,
				license_check_opts,
			]
		),
		# ===================== DNS commands ==================================
		Command(
			'set-low-dns-timings',  CommandTypes.MACRO,
			u"Set low DNS timing values (TTL, refresh, retry, etc). "
			u"This operation should be done in advance to insure quick transition to the new DNS server. "
			u"Low DNS timing values are set for all domains on source Plesk servers.",
			lambda m, o: m.run('set-low-dns-timings'),
			[
				reload_source_data_opt, 
				fetch_options_opts, 
				services_checks_opts,
				license_check_opts,
			],
		),
		Command(
			'set-dns-forwarding', CommandTypes.MACRO,
			u"Set up DNS forwarding",
			lambda m, o: m.run('set-dns-forwarding'),
			[
				migration_list_file_opt,
				ip_mapping_file_opt,
				services_checks_opts,
				license_check_opts,
			]
		),
		Command(
			'undo-dns-forwarding', CommandTypes.MACRO,
			u"Undo DNS forwarding on all source Plesk servers",
			lambda m, o: m.run('undo-dns-forwarding'),
			[
				migration_list_file_opt,
				ip_mapping_file_opt,
				services_checks_opts,
				license_check_opts,
			]
		),
		# ===================== Post-migration checks =========================
		Command(
			'test-all', CommandTypes.MACRO,
			u"Check that the transferred domains are working correctly: test web sites, DNS, mail and FTP",
			lambda m, o: m.run('test-all'),
			import_opts_list + [
				test_dns_opts, 
				services_checks_opts,
				license_check_opts,
			]
		),
		Command(
			'test-services', CommandTypes.MACRO,
			u"Check that services are working correctly.",
			lambda m, o: m.run('test-services'),
			import_opts_list + [
				license_check_opts,
			]
		),
		Command(
			'test-sites', CommandTypes.MACRO,
			u"Check that the web sites of the transferred domains are working correctly.",
			lambda m, o: m.run('test-sites'),
			import_opts_list + [
				services_checks_opts,
				license_check_opts,
			]
		),
		Command(
			'test-dns', CommandTypes.MACRO,
			u"Check that the DNS queries for transferred domains' records are ok.",
			lambda m, o: m.run('test-dns'),
			import_opts_list + [
				test_dns_opts, 
				services_checks_opts,
				license_check_opts,
			]
		),
		Command(
			'test-mail', CommandTypes.MACRO,
			u"Check that the mail service works fine for transferred domains: "
			u"check login to all transferred mailboxes by IMAP and SMTP.",
			lambda m, o: m.run('test-mail'),
			import_opts_list + [
				services_checks_opts,
				license_check_opts,
			]
		),
		Command(
			'test-users', CommandTypes.MACRO,
			u"Check that system users are transferred correctly "
			u"(access by FTP, SSH and RDP is checked).",
			lambda m, o: m.run('test-users'),
			import_opts_list + [
				services_checks_opts,
				license_check_opts,
			]
		),
		Command(
			'test-databases', CommandTypes.MACRO,
			u"Check that the database service works fine for transferred domains.",
			lambda m, o: m.run('test-databases'),
			import_opts_list + [
				services_checks_opts,
				license_check_opts,
			]
		),
		# ===================== Other auxiliary commands ======================
		MigratorHelpCommand(migrator_command_name, commands, indent),
		Command(
			'install-imapsync', CommandTypes.MACRO,
			u"Install imapsync on CentOS 5",
			lambda o: imapsync.install(),
			migrator_required=False
		),

		# ***************** Micro and internal commands ***********************
		# "Micro" commands are commands that are mostly used for debugging and
		# development and which are included as a part of macro commands.
		# "Internal" commands are other auxiliary commands for debugging and
		# development. In general case none of these commands should be
		# used by end-user of migration tools.
		Command(
			'fetch-source', CommandTypes.MICRO,
			u"Download configuration data from source Plesk servers",
			lambda m, o: m.fetch_source(o),
			[
				use_cached,
				fetch_options_opts,
				services_checks_opts
			]
		),
		Command(
			'convert', CommandTypes.MICRO,
			u"Convert Plesk data files to PPA format",
			lambda m, o: m.convert(o),
			[convert_opts, migration_list_file_opt, ip_mapping_file_opt]
		),
		Command(
			'import-accounts', CommandTypes.MICRO,
			u"Import clients, users and subscriptions into PPA", 
			lambda m, o: m.restore(o),
			[migration_list_file_opt, ip_mapping_file_opt, services_checks_opts]
		),
		Command(
			'convert-hosting', CommandTypes.MICRO,
			u"Convert Plesk hosting settings according to PPA subscription settings", 
			lambda m, o: m.convert_hosting(o),
			[migration_list_file_opt, ip_mapping_file_opt, services_checks_opts]
		),
		Command(
			'restore-hosting', CommandTypes.MICRO,
			u"Configure original Plesk hosting settings in PPA", 
			lambda m, o: m.restore_hosting(o),
			[migration_list_file_opt, ip_mapping_file_opt, services_checks_opts]
		),
		Command(
			'verify-hosting', CommandTypes.MICRO,
			u"Verify that subscriptions, domains and hosting settings were restored", 
			lambda m, o: m.run('verify-hosting'),
			import_opts_list + [
				services_checks_opts,
				license_check_opts,
			]
		),
		Command(
			'restore-status', CommandTypes.MICRO,
			u"Restore suspended status for the subscriptions, which were suspended in the originating system", 
			lambda m, o: m.restore_status(o),
			[migration_list_file_opt, ip_mapping_file_opt]
		),
		Command(
			'check-main-node-disk-space-requirements', CommandTypes.MICRO,
			u"Check main node disk space requirements",
			lambda m, o: m.check_main_node_disk_space_requirements(
				obj(skip_main_node_disk_space_checks=False), show_skip_message=False
			)
		),
		Command(
			'check-infrastructure', CommandTypes.MICRO,
			u"Check infrastructure (connections between nodes, disk space requirements) "
			u"for common issues that can make copy web, mail or database content stages to fail",
			lambda m, o: m.check_infrastructure(obj(skip_infrastructure_checks=False), show_skip_message=False)
		),
		Command(
			'check-nodes-licenses', CommandTypes.MICRO,
			u"Check Plesk licenses assigned to PPA nodes",
			lambda m, o: m.check_nodes_licenses()
		),
		MigratorShellCommand(),
		MigratorScriptCommand(),
		Command(
			'unpack-backups', CommandTypes.INTERNAL,
			u"Unpack all Plesk backups to $session_dir/unpacked directory",
			lambda m, o: m.unpack_backups()
		),
		Command(
			'transfer-wpb-sites', CommandTypes.MICRO,
			u"Transfer Web Presense Builder sites", 
			lambda m, o: m.transfer_wpb_sites(o),
			[migration_list_file_opt, ip_mapping_file_opt]
		),
		Command(
			'transfer-aps-packages', CommandTypes.MICRO,
			u"Transfer APS packages", 
			lambda m, o: m.transfer_aps_packages(o),
			[migration_list_file_opt, ip_mapping_file_opt]
		),
		Command(
			'transfer-vdirs', CommandTypes.MICRO,
			u"Transfer virtual directories",
			lambda m, o: m.transfer_vdirs(o),
			[migration_list_file_opt, ip_mapping_file_opt]
		),
		Command(
			'transfer-resource-limits', CommandTypes.MICRO,
			u"Transfer account resource limits and usage from H-Sphere",
			lambda m, o: m.transfer_resource_limits(o),
			[migration_list_file_opt, ip_mapping_file_opt]
		),
		# Used by bash completion
		Command(
			'list-options', CommandTypes.MICRO,
			u"Return list of available options",
			lambda o: _get_list_options(commands, o),
			[get_options_opts],
			migrator_required=False
		),
		Command(
			'remove-webspace', CommandTypes.MICRO,
			u"Remove webspace from PPA",
			lambda m, o: m.remove_webspace(o),
			[remove_webspace_id_opts, services_checks_opts]
		)
	])

	return create_arguments_parser(
		migrator_command_name=migrator_command_name,
		description=u"Migrator from Parallels Plesk(s) to Parallels Plesk Automation",
		commands=commands,
		script_dir=script_dir,
		indent=indent
	)


def _create_migrator(config):
	source_type = config.get('GLOBAL', 'source-type')
	# TODO autodiscovery?
	if source_type == 'plesk':
		import parallels.plesks_migrator.migrator
		return parallels.plesks_migrator.migrator.Migrator(config)
	elif source_type == 'confixx':
		import parallels.confixx_migrator.migrator
		return parallels.confixx_migrator.migrator.Migrator(config)
	elif source_type == 'cpanel':
		import parallels.cpanel_migrator.migrator
		return parallels.cpanel_migrator.migrator.Migrator(config)
	elif source_type == 'ppcpl':
		import parallels.ppcpl_migrator.migrator
		return parallels.ppcpl_migrator.migrator.Migrator(config)
	elif source_type == 'hsphere':
		import parallels.hsphere_migrator.migrator
		return parallels.hsphere_migrator.migrator.Migrator(config)
	elif source_type == 'helm':
		import parallels.helm_migrator.migrator
		return parallels.helm_migrator.migrator.Migrator(config)
	elif source_type == 'helm3':
		import parallels.helm3_migrator.migrator
		return parallels.helm3_migrator.migrator.Migrator(config)
	elif source_type == 'expand':
		import parallels.expand_migrator.migrator
		return parallels.expand_migrator.migrator.Migrator(config)
	elif source_type == 'pbas':
		import parallels.pbas_migrator.migrator
		return parallels.pbas_migrator.migrator.Migrator(config)
	elif source_type == 'ppa-web-sn':
		import parallels.shm_move_webspaces.migrator
		return parallels.shm_move_webspaces.migrator.Migrator(config)
	else:
		raise MigrationConfigurationFileError(
			u"Source type '%s' specified in 'source-type' option of '[GLOBAL]' section "
			u"of configuration file is not supported. "
			"Supported types: 'plesk', 'expand', 'hsphere', 'pbas', 'confixx', 'ppcpl'" % (
				source_type
			)
		)


def _get_list_options(commands, options):
	return_list = []
	main_list = [c.name for c in commands if c.name != 'list-options']
	if options.command is None:
		return_list = main_list
	elif options.command in main_list:
		command = [c for c in commands if c.name == options.command][0]

		def get_actions_from_argument_parser(arg_parser):
			# There are not any other way to get action's name from ArgumentParser object
			# so we've used private properties
			return [a.option_strings[0] for a in arg_parser._actions]

		for p in command.parents:
			return_list.extend(get_actions_from_argument_parser(p))

		for (name, _, _) in command.args:
			return_list.append(name)

	print ' '.join(return_list)