# CLI common functions

import argparse

from parallels.common.cli.migrator_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(
				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'
	)

	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 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, reload_source_data_opt, fetch_options_opts]

	commands = []
	migrator_command_name = get_executable_file_name()

	commands.extend([
		### common macro-commands ###
		MigratorHelpCommand(migrator_command_name, commands, indent),
		# 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 + [license_check_opts, services_checks_opts],
		),
		Command(
			'generate-migration-list', CommandTypes.MACRO,
			u"Generate migration list file. This file could be used to migrate only a specified set of plans and subscriptions, and to map subscriptions to plans. Comment out all objects that you don't want to be migrated",
			lambda m, o: m.generate_migration_list(o),
			[migration_list_file_opt, reload_source_data_opt, fetch_options_opts, services_checks_opts],
			[
				['--overwrite', u'Overwrite file if it already exists', 'store_true']
			]
		),
		Command(
			'import-resellers', CommandTypes.MACRO,
			u"Import resellers. Note that only contact data is imported, and you should assign resellers to resellers plans manually", 
			lambda m, o: m.import_resellers(o),
			import_opts_list + [services_checks_opts]
		),
		Command(
			'import-plans', CommandTypes.MICRO,
			u"Import service plans", 
			lambda m, o: m.import_plans(o),
			import_opts_list + [services_checks_opts]
		),
		Command(
			'install-imapsync', CommandTypes.MACRO,
			u"Install imapsync on CentOS 5",
			lambda o: imapsync.install(),
			migrator_required=False
		),
		Command(
			'set-low-dns-timings',  CommandTypes.MACRO,
			u"Set low DNS timing values (TTL, refresh, retry, etc). This operation should be done in advance to insure quick transition to the new DNS server. 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],
		),
		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.test_all(o),
			import_opts_list + [test_dns_opts, services_checks_opts]
		),

		### common micro-commands ###
		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]
		),
		Command(
			'import-accounts', CommandTypes.MICRO,
			u"Import clients, users and subscriptions into PPA", 
			lambda m, o: m.restore(o),
			[migration_list_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, 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, services_checks_opts]
		),
		Command(
			'verify-hosting', CommandTypes.MICRO,
			u"Verify that subscriptions, domains and hosting settings were restored", 
			lambda m, o: m.verify_hosting(o),
			[migration_list_file_opt]
		),
		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]
		),
		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) 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(
			'copy-mail-content', CommandTypes.MICRO,
			u"Copy mail content", 
			lambda m, o: m.copy_mail_content(o),
			import_opts_list + [services_checks_opts]
		),
		Command(
			'copy-db-content', CommandTypes.MICRO,
			u"Copy databases content", 
			lambda m, o: m.copy_db_content(o),
			import_opts_list + [services_checks_opts]
		),
		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(
			'test-services', CommandTypes.MACRO,
			u"Check that services are working correctly.",
			lambda m, o: m.test_services(o),
			import_opts_list
		),
		Command(
			'test-sites', CommandTypes.MACRO,
			u"Check that the web sites of the transferred domains are working correctly.",
			lambda m, o: m.test_sites(o),
			import_opts_list + [services_checks_opts]
		),
		Command(
			'test-dns', CommandTypes.MACRO,
			u"Check that the DNS queries for transferred domains' records are ok.",
			lambda m, o: m.test_dns(o),
			import_opts_list + [test_dns_opts, services_checks_opts]
		),
		Command(
			'test-mail', CommandTypes.MACRO,
			u"Check that the mail service works fine for transferred domains: check login to all transferred mailboxes by IMAP and SMTP.",
			lambda m, o: m.test_mail(o),
			import_opts_list + [services_checks_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.test_users(o),
			import_opts_list + [services_checks_opts]
		),
		Command(
			'test-databases', CommandTypes.MACRO,
			u"Check that the database service works fine for transferred domains.",
			lambda m, o: m.test_databases(o),
			import_opts_list + [services_checks_opts]
		),

		### transfer macro commands ###

		# 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
			]
		),
		Command(
			'transfer-billing', CommandTypes.MACRO,
			u"Transfer billing data from H-Sphere",
			lambda m, o: m.transfer_billing(o),
			import_opts_list + [suspend_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]
		),

		### transfer micro commands ###
		Command(
			'prepare-service-nodes', CommandTypes.MICRO,
			u"Prepare Windows PPA service nodes for migration: install and configure rsync client",
			lambda m, o: m.prepare_service_nodes(o),
			[migration_list_file_opt]
		),
		Command(
			'copy-content', CommandTypes.MICRO,
			u"Copy hosting content to destination servers",
			lambda m, o: m.copy_content(o),
			import_opts_list + [services_checks_opts]
		),
		Command(
			'set-dns-forwarding', CommandTypes.MACRO,
			u"Set up DNS forwarding",
			lambda m, o: m.run('set-dns-forwarding'),
			[migration_list_file_opt, services_checks_opts]
		),
		Command(
			'transfer-wpb-sites', CommandTypes.MICRO,
			u"Transfer Web Presense Builder sites", 
			lambda m, o: m.transfer_wpb_sites(o),
			[migration_list_file_opt]
		),
		Command(
			'transfer-aps-packages', CommandTypes.MICRO,
			u"Transfer APS packages", 
			lambda m, o: m.transfer_aps_packages(o),
			[migration_list_file_opt]
		),
		Command(
			'transfer-vdirs', CommandTypes.MICRO,
			u"Transfer virtual directories",
			lambda m, o: m.transfer_vdirs(o),
			[migration_list_file_opt]
		),
		Command(
			'undo-dns-forwarding', CommandTypes.MICRO,
			u"Undo DNS forwarding on all source Plesk servers", 
			lambda m, o: m.run('undo-dns-forwarding'),
			[migration_list_file_opt, services_checks_opts]
		),
		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]
		),
		# 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)
	else:
		raise MigrationConfigurationFileError(
			u"Source type '%s' specified in 'source-type' option of '[GLOBAL]' section 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)
