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

from parallels.core import messages

import argparse

from parallels.core.cli.common_cli import Command, CommandTypes, \
	MigratorHelpCommand, MigratorShellCommand, MigratorScriptCommand, \
	create_arguments_parser, run as run_migrator
from parallels.core import imapsync
from parallels.core import MigrationConfigurationFileError
from parallels.core.utils.common import obj, get_executable_file_name
from parallels.core.utils.steps_profiler import get_default_steps_profiler
from parallels.core.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=messages.FORCE_FETCHING_DATA_FROM_SOURCE_PANELS)
	# 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=(
			messages.DO_NOT_USE_RELOAD_SOURCE_DATA)
	)

	migration_list_file_opt = argparse.ArgumentParser(add_help=False)
	migration_list_file_opt.add_argument(
		'--migration-list-file', 
		help=messages.MIGRATION_LIST_FILENAME_DEFAULT_VALUE_IS)
	ip_mapping_file_opt = argparse.ArgumentParser(add_help=False)
	ip_mapping_file_opt.add_argument(
		'--ip-mapping-file',
		help=messages.IP_MAPPING_FILENAME_FILE_CONTAINS_SOURCE)

	services_checks_opts = argparse.ArgumentParser(add_help=False)
	services_checks_opts.add_argument(
		'--skip-services-checks', action='store_true', 
		help=messages.DO_NOT_CHECK_SERVICES_FOR_ANY)
	convert_opts = argparse.ArgumentParser(add_help=False)
	convert_opts.add_argument(
		'--ignore-pre-migration-errors', action='store_true', 
		help=(
			messages.IGNORE_PREMIGRATION_ERRORS_AND_CONTINUE_MIGRATION)
	)
	convert_opts.add_argument(
		'--allocate-only-required-resources', action='store_true', 
		help=(
			messages.SWITCH_STRICT_MODE_IN_WHICH_NEW)
	)

	license_check_opts = argparse.ArgumentParser(add_help=False)
	license_check_opts.add_argument(
		'--skip-license-checks', action='store_true', 
		help=messages.DO_NOT_CHECK_ANY_LICENSES_PPA)
	infrastructure_checks_opts = argparse.ArgumentParser(add_help=False)
	infrastructure_checks_opts.add_argument(
		'--skip-infrastructure-checks', action='store_true', 
		help=messages.DO_NOT_CHECK_INFRASTRUCTURE_FOR_ANY)
	infrastructure_checks_opts.add_argument(
		'--skip-main-node-disk-space-checks', action='store_true', 
		help=messages.DO_NOT_CHECK_DISK_SPACE_REQUIREMENTS)

	test_dns_opts = argparse.ArgumentParser(add_help=False)
	test_dns_opts.add_argument(
		'--skip-dns-forwarding-test', action='store_true', 
		help=messages.DO_NOT_CHECK_THAT_DNS_QUERIES)

	fetch_options_opts = argparse.ArgumentParser(add_help=False)
	fetch_options_opts.add_argument(
		'--ignore-fetch-source-errors', action='store_true',
		help=messages.IGNORE_ERRORS_FETCHSOURCE_STAGE_HSPHERE_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'
	)

	create_full_backup_opts = argparse.ArgumentParser(add_help=False)
	create_full_backup_opts.add_argument(
		'--source-file', action='store',
		help=u'Path to source light backup file'
	)
	create_full_backup_opts.add_argument(
		'--source-format', action='store',
		help=u'Format of source file: YAML, JSON or XML (default is YAML)',
		default='yaml'
	)
	create_full_backup_opts.add_argument(
		'--target-file', action='store',
		help=u'Path to target full backup file'
	)

	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=messages.SUSPEND_ACCOUNTS_IN_HSPHERE_EVEN_IF)

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

	post_migration_opts_list = [
		migration_list_file_opt,
		ip_mapping_file_opt,
		services_checks_opts,
		license_check_opts,
		reload_source_data_opt,
		convert_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,
			messages.GENERATE_MIGRATION_LIST_FILE_FILE_COULD,
			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', messages.OVERWRITE_FILE_IF_IT_ALREADY_EXISTS, 'store_true']
			]
		),
		# Run pre-migration checks, called by 'panel-transfer check'
		Command(
			'check', CommandTypes.MACRO,
			messages.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,
			messages.ANALYSE_SOURCE_AND_DESTINATION_NODES_FOR,
			lambda m, o: m.run('analyse-hosting'),
		),
		# ===================== Main transfer commands ========================
		Command(
			'import-resellers', CommandTypes.MACRO,
			messages.IMPORT_RESELLERS_NOTE_THAT_ONLY_CONTACT,
			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,
			messages.TRANSFER_BILLING_DATA_FROM_HSPHERE,
			lambda m, o: m.transfer_billing(o),
			import_opts_list + [
				suspend_opts,
				license_check_opts,
			]
		),
		Command(
			'check-billing', CommandTypes.MACRO,
			messages.CHECK_IF_BILLING_DATA_CAN_SUCCESSFULLY,
			lambda m, o: m.check_billing(o),
			import_opts_list + [
				suspend_opts,
				license_check_opts,
			]
		),
		# ===================== Copy content commands =========================
		Command(
			'copy-content', CommandTypes.MACRO,
			messages.COPY_HOSTING_CONTENT_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,
			messages.SET_LOW_DNS_TIMING_VALUES_TTL,
			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,
				reload_source_data_opt,
				convert_opts,
			]
		),
		Command(
			'undo-dns-forwarding', CommandTypes.MACRO,
			messages.UNDO_DNS_FORWARDING_ALL_SOURCE_PLESK,
			lambda m, o: m.run('undo-dns-forwarding'),
			[
				migration_list_file_opt,
				ip_mapping_file_opt,
				services_checks_opts,
				license_check_opts,
				reload_source_data_opt,
				convert_opts,
			]
		),
		# ===================== Post-migration checks =========================
		Command(
			'test-all', CommandTypes.MACRO,
			messages.CHECK_THAT_TRANSFERRED_DOMAINS_ARE_WORKING,
			lambda m, o: m.run('test-all'),
			import_opts_list + [
				test_dns_opts, 
				services_checks_opts,
				license_check_opts,
			]
		),
		Command(
			'test-services', CommandTypes.MACRO,
			messages.CHECK_THAT_SERVICES_ARE_WORKING_CORRECTLY,
			lambda m, o: m.run('test-services'),
			import_opts_list + [
				license_check_opts,
			]
		),
		Command(
			'test-sites', CommandTypes.MACRO,
			messages.CHECK_THAT_WEB_SITES_TRANSFERRED_DOMAINS,
			lambda m, o: m.run('test-sites'),
			import_opts_list + [
				services_checks_opts,
				license_check_opts,
			]
		),
		Command(
			'test-dns', CommandTypes.MACRO,
			messages.CHECK_THAT_DNS_QUERIES_FOR_TRANSFERRED_1,
			lambda m, o: m.run('test-dns'),
			import_opts_list + [
				test_dns_opts, 
				services_checks_opts,
				license_check_opts,
			]
		),
		Command(
			'test-mail', CommandTypes.MACRO,
			messages.CHECK_THAT_MAIL_SERVICE_WORKS_FINE,
			lambda m, o: m.run('test-mail'),
			import_opts_list + [
				services_checks_opts,
				license_check_opts,
			]
		),
		Command(
			'test-users', CommandTypes.MACRO,
			messages.CHECK_THAT_SYSTEM_USERS_ARE_TRANSFERRED,
			lambda m, o: m.run('test-users'),
			import_opts_list + [
				services_checks_opts,
				license_check_opts,
			]
		),
		Command(
			'test-databases', CommandTypes.MACRO,
			messages.CHECK_THAT_DATABASE_SERVICE_WORKS_FINE,
			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,
			messages.DOWNLOAD_CONFIGURATION_DATA_FROM_SOURCE_PLESK,
			lambda m, o: m.fetch_source(o),
			[
				use_cached,
				fetch_options_opts,
				services_checks_opts
			]
		),
		Command(
			'convert', CommandTypes.MICRO,
			messages.CONVERT_PLESK_DATA_FILES_PPA_FORMAT,
			lambda m, o: m.convert(o),
			[convert_opts, migration_list_file_opt, ip_mapping_file_opt]
		),
		Command(
			'import-accounts', CommandTypes.MICRO,
			messages.IMPORT_CLIENTS_USERS_AND_SUBSCRIPTIONS_INTO, 
			lambda m, o: m.restore(o),
			[migration_list_file_opt, ip_mapping_file_opt, services_checks_opts]
		),
		Command(
			'convert-hosting', CommandTypes.MICRO,
			messages.CONVERT_PLESK_HOSTING_SETTINGS_ACCORDING_PPA, 
			lambda m, o: m.convert_hosting(o),
			[migration_list_file_opt, ip_mapping_file_opt, services_checks_opts]
		),
		Command(
			'restore-hosting', CommandTypes.MICRO,
			messages.CONFIGURE_ORIGINAL_PLESK_HOSTING_SETTINGS_IN, 
			lambda m, o: m.restore_hosting(o),
			[migration_list_file_opt, ip_mapping_file_opt, services_checks_opts]
		),
		Command(
			'verify-hosting', CommandTypes.MICRO,
			messages.VERIFY_THAT_SUBSCRIPTIONS_DOMAINS_AND_HOSTING, 
			lambda m, o: m.run('verify-hosting'),
			import_opts_list + [
				services_checks_opts,
				license_check_opts,
			]
		),
		Command(
			'restore-status', CommandTypes.MICRO,
			messages.RESTORE_SUSPENDED_STATUS_FOR_SUBSCRIPTIONS_WHICH, 
			lambda m, o: m.restore_status(o),
			[migration_list_file_opt, ip_mapping_file_opt]
		),
		Command(
			'check-main-node-disk-space-requirements', CommandTypes.MICRO,
			messages.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,
			messages.CHECK_INFRASTRUCTURE_CONNECTIONS_BETWEEN_NODES_DISK,
			lambda m, o: m.run('check-infrastructure'),
			import_opts_list + [
				infrastructure_checks_opts,
			]
		),
		Command(
			'check-nodes-licenses', CommandTypes.MICRO,
			messages.CHECK_PLESK_LICENSES_ASSIGNED_PPA_NODES,
			lambda m, o: m.check_nodes_licenses()
		),
		MigratorShellCommand(),
		MigratorScriptCommand(),
		Command(
			'unpack-backups', CommandTypes.INTERNAL,
			messages.UNPACK_ALL_PLESK_BACKUPS_SESSION_DIRUNPACKED,
			lambda m, o: m.unpack_backups()
		),
		Command(
			'create-full-backup-from-light', CommandTypes.INTERNAL,
			messages.CREATE_BACKUP_FROM_LIGHT_BACKUP,
			lambda m, o: m.create_full_backup_from_light_backup(),
			[create_full_backup_opts]
		),
		Command(
			'transfer-wpb-sites', CommandTypes.MICRO,
			messages.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.run('transfer-aps-packages'),
			post_migration_opts_list

		),
		Command(
			'restore-aps-applications', CommandTypes.MICRO,
			u"Restore APS applications",
			lambda m, o: m.run('restore-aps-applications'),
			post_migration_opts_list
		),
		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,
			messages.TRANSFER_ACCOUNT_RESOURCE_LIMITS_AND_USAGE,
			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,
			messages.RETURN_LIST_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=messages.MIGRATOR_FROM_PARALLELS_PLESKS_PARALLELS_PLESK,
		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.source.plesk.migrator
		return parallels.source.plesk.migrator.Migrator(config)
	elif source_type == 'confixx':
		import parallels.source.confixx.migrator
		return parallels.source.confixx.migrator.Migrator(config)
	elif source_type == 'cpanel':
		import parallels.source.cpanel.migrator
		return parallels.source.cpanel.migrator.Migrator(config)
	elif source_type == 'ppcpl':
		import parallels.source.ppcpl.migrator
		return parallels.source.ppcpl.migrator.Migrator(config)
	elif source_type == 'hsphere':
		import parallels.source.hsphere.migrator
		return parallels.source.hsphere.migrator.Migrator(config)
	elif source_type == 'helm':
		import parallels.source.helm4.migrator
		return parallels.source.helm4.migrator.Migrator(config)
	elif source_type == 'helm3':
		import parallels.source.helm3.migrator
		return parallels.source.helm3.migrator.Migrator(config)
	elif source_type == 'expand':
		import parallels.source.expand.migrator
		return parallels.source.expand.migrator.Migrator(config)
	elif source_type == 'pbas':
		import parallels.source.pbas.migrator
		return parallels.source.pbas.migrator.Migrator(config)
	elif source_type == 'ppa-web-sn':
		import parallels.source.shm_web_sn.migrator
		return parallels.source.shm_web_sn.migrator.Migrator(config)
	elif source_type == 'custom':
		import parallels.source.custom.migrator
		return parallels.source.custom.migrator.Migrator(config)
	else:
		raise MigrationConfigurationFileError(
			messages.SOURCE_TYPE_S_SPECIFIED_IN_SOURCETYPE % (
				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)