from parallels.plesk.source.cpanel import messages
import logging
import posixpath

import settings
from parallels.core.dump.data_model import DatabaseServer
from parallels.core.utils import plesk_utils
from parallels.plesk.source.cpanel.pmm_agent import CpanelPmmMigrationAgent
from parallels.plesk.source.cpanel.server import CpanelSourceServer
from parallels.plesk.source.cpanel.content.mail import CpanelSourceMailDirectory
from parallels.plesk.source.cpanel.content.mail import CpanelCopyMailRsync
from parallels.plesk.source.cpanel.utils import CpanelConfiguration
from parallels.plesk.utils.xml_rpc.plesk.operator.subscription import Ips
from parallels.plesk.source.plesk.infrastructure_checks import checks as infrastructure_checks
from parallels.core.utils.common import cached, group_by
import connections
import parallels.core.migrator
import parallels.plesk.source.cpanel.utils
from parallels.plesk.source.legacy.migrator import PmmUnixMigrator
from parallels.plesk.source.cpanel.global_context import CpanelGlobalMigrationContext
from parallels.core.utils.database_utils import copy_db_content_linux, LinuxDatabaseTransferOptions


class Migrator(PmmUnixMigrator):
	logger = logging.getLogger(__name__)

	def __init__(self, config):
		super(Migrator, self).__init__(config, settings)
		self.global_context.dump_agent = self._get_dump_agent()

	def _load_connections_configuration(self):
		return connections.MigratorConnections(self.global_context, self._get_target_panel())

	def _create_global_context(self):
		context = CpanelGlobalMigrationContext()
		context.source_has_dns_forwarding = False
		return context

	# ======================== copy mail content ==============================

	def _copy_mail_content_single_subscription(self, migrator_server, subscription, issues):
		self.logger.info(messages.LOG_MIGRATE_MAIL_CONTENT_OF_SUBSCRIPTION, subscription.name)
		copy_action = CpanelCopyMailRsync(CpanelSourceMailDirectory)
		copy_action.copy_mail(
				self.global_context, migrator_server, subscription, issues)

	# ======================== databases =====================================

	def _get_src_db_server(self, db, backup):
		if db.dbtype == 'mysql':
			configuration = self._get_cpanel_configuration()
			user, password = configuration.pmm_agent.get_mysql_credentials()
			port = '3306'
		else:
			user = 'postgres'
			password = None
			port = '5432'
		return DatabaseServer(
			None,
			host='localhost',
			port=port,
			login=user,
			password=password,
			dbtype=db.dbtype,
			is_default=True,
		)

	def _get_mailserver_ip_by_subscription_name(self, source_settings, subscription_name):
		return Ips(None, None)


	def transfer_subscription_databases(self, subscription):
		databases_by_subscription = group_by(
			self._list_databases_to_copy(),
			lambda db_info: db_info.subscription_name
		)
		subscription_databases = databases_by_subscription.get(subscription.name, [])

		original_owner = subscription.target_sysuser_name

		for db_info in subscription_databases:
			self._copy_database_content(db_info, original_owner)

	def _copy_database_content(self, db_info, original_owner):
		"""Transfer MySQL, PostgreSQL database content.

		Original owner is a login name of database user that has admin
		permissions on all client's databases. In cPanel, that is a login of
		cPanel client.
		"""

		safe = self._get_safe_lazy()

		if not self._check_database_exists_on_target(db_info):
			return

		repeat_error = messages.UNABLE_COPY_CONTENT_DATABASE_S_SUBSCRIPTION_1 % (
			db_info.database_name, db_info.subscription_name
		)

		keyfile = self.global_context.ssh_key_pool.get(
				db_info.source_database_server, db_info.target_database_server
			).key_pathname
		transfer_options = LinuxDatabaseTransferOptions(
			keyfile=keyfile, dump_format='text', owner=original_owner)

		def _repeat_copy_linux_content():
			copy_db_content_linux(db_info, transfer_options)

		safe.try_subscription_with_rerun(
			_repeat_copy_linux_content, db_info.subscription_name,
			messages.FAILED_COPY_CONTENT_DATABASE_S_1 % db_info.database_name,
			is_critical=False, repeat_error=repeat_error, use_log_context=False)

	# ======================== infrastructure checks ==========================

	def _get_check_service_connection_commands(self):
		return {
			'web': self._check_unix_copy_web_content_rsync_connections,
			'mail': self._check_unix_copy_mail_content_rsync_connections,
			'db': self._check_unix_copy_db_content_scp_connections
		}

	def _check_infrastructure_connections(self, report, safe):
		self.logger.info(messages.LOG_CHECK_CONNECTION_REQUIREMENTS)

		for service in ('web', 'mail', 'db'):
			self._check_connection(service, report, safe)

	def _check_connection(self, service, report, safe):
		service_checkers = self._get_check_service_connection_commands()
		report_messages = {
			'title':
				messages.REPORT_TITLE_INFRASTRUCTURE_CHECKS_CONNECTIONS,
			'error':
				messages.FAILED_TO_CHECK_CONNECTIONS}
		report = report.subtarget(report_messages['title'] % service, None)
		checks = infrastructure_checks.InfrastructureChecks()
		with safe(report, report_messages['error'] % service):
			service_checkers[service](checks, report)

	def _check_disk_space(self, report, safe):
		self.logger.info(messages.LOG_CHECK_DISK_SPACE_REQUIREMENTS)
		disk_space_report = report.subtarget(messages.REPORT_TARGET_DISK_SPACE_REQUIREMENTS, None)
		super(Migrator, self)._check_disk_space_unix(disk_space_report)

	# ======================== utility functions ==============================

	@cached
	def _get_cpanel_configuration(self):
		return CpanelConfiguration(
			self.conn.cpanel,
			self._get_dump_agent()
		)

	@cached # to avoid re-deploying, have a single PMM agent object
	def _get_dump_agent(self):
		migrator_pmm_dir = posixpath.join(
			plesk_utils.get_migrator_root_path(parallels.plesk.source.cpanel),
			'extras', 'pmm')
		return CpanelPmmMigrationAgent(
			self.global_context,
			self.conn.cpanel,
			migrator_pmm_dir,
			self._get_settings()
		)

	@cached
	def _get_source_node(self, node_id):
		node_settings = self._get_source_servers()[node_id]
		return CpanelSourceServer(node_id, node_settings)
