from parallels.core.logging import get_logger
from parallels.core.actions.utils.multithreading_properties import MultithreadingProperties
from parallels.core.panels import TargetPanels
from parallels.core.utils.paths.converters.source import SourceWebPathConverter
from parallels.core.utils.paths.converters.target import TargetWebPathConverter
from parallels.plesk import messages
from parallels.core.utils.common import group_by, is_empty
from parallels.core.utils.database_server_type import DatabaseServerType
from parallels.core.migrator_config import read_adjust_applications_enabled
from parallels.core.actions.base.subscription_action import SubscriptionAction
from parallels.core.application.adjuster import Adjuster, DatabaseServers

logger = get_logger(__name__)


class AdjustApplications(SubscriptionAction):
    def get_description(self):
        """Get short description of action as string

        :rtype: str
        """
        return messages.ACTION_ADJUST_APPLICATIONS_DESCRIPTION

    def get_failure_message(self, global_context, subscription):
        """
        :type global_context: parallels.core.global_context.GlobalMigrationContext
        :type subscription: parallels.core.migrated_subscription.MigratedSubscription
        """
        return messages.ACTION_ADJUST_APPLICATIONS_FAILURE

    def is_critical(self):
        """If action is critical or not"""
        return False

    def get_multithreading_properties(self):
        """Get how multithreading should be applied for that action

        :rtype: parallels.core.actions.utils.multithreading_properties.MultithreadingProperties
        """
        return MultithreadingProperties(can_use_threads=True, use_threads_by_default=True)

    def filter_subscription(self, global_context, subscription):
        """
        :type global_context: parallels.core.global_context.GlobalMigrationContext
        :type subscription: parallels.core.migrated_subscription.MigratedSubscription
        """
        return read_adjust_applications_enabled(global_context.config)

    def run(self, global_context, subscription):
        """Run action on given subscription

        :type global_context: parallels.core.global_context.GlobalMigrationContext
        :type subscription: parallels.core.migrated_subscription.MigratedSubscription
        :rtype: None
        """

        adjuster = Adjuster()

        # First, adjust database connection settings
        vhost_dir = global_context.conn.target.plesk_server.get_vhost_dir(subscription.name)

        target_mysql_server = subscription.db_target_servers.get('mysql')
        target_mssql_server = subscription.db_target_servers.get('mssql')
        source_mysql_servers = set()
        source_mssql_servers = set()

        databases_by_subscription = group_by(
            global_context.migrator._list_databases_to_copy(),
            lambda db_info: db_info.subscription_name
        )
        subscription_databases = databases_by_subscription.get(subscription.name, [])
        for db_info in subscription_databases:
            if db_info.source_database_server.type() == DatabaseServerType.MYSQL:
                source_mysql_servers.add(db_info.source_database_server)
            elif db_info.source_database_server.type() == DatabaseServerType.MSSQL:
                source_mssql_servers.add(db_info.source_database_server)

        database_servers = DatabaseServers(
            source_mysql_servers, target_mysql_server, source_mssql_servers, target_mssql_server
        )

        database_name_mapping = {}
        for database in subscription.databases:
            if database.source_name != database.target_name:
                database_name_mapping[database.source_name] = database.target_name

        database_user_name_mapping = {}
        for database_user in subscription.database_users:
            if database_user.source_name != database_user.target_name:
                database_user_name_mapping[database_user.source_name] = database_user.target_name

        source_ips = [
            ip for ip in
            (subscription.raw_dump.ip, subscription.raw_dump.ipv6)
            if not is_empty(ip)
        ]

        logger.debug(messages.DEBUG_START_ADJUST_DATABASE)
        issues = adjuster.adjust_database(
            subscription.web_target_server, vhost_dir, database_servers, source_ips,
            database_name_mapping, database_user_name_mapping
        )
        logger.debug(messages.DEBUG_END_ADJUST_DATABASE)

        # Then, adjust application paths, for example if location of virtual host directory differs for source
        # and target server, and there are files that have hardcoded absolute path.
        if global_context.target_panel_type == TargetPanels.PLESK_MULTI_SERVER:
            logger.debug(messages.DEBUG_SKIP_ADJUST_PATHS_PLESK_MULTI_SERVER)
        elif global_context.migrator.web_files.need_to_copy_files(global_context, subscription):
            logger.debug(messages.DEBUG_START_ADJUST_PATHS)
            path_mapping = {}
            target_server = subscription.web_target_server
            for item in global_context.migrator.web_files.list_files_to_copy(global_context, subscription):
                if not item.fix_application_paths:
                    continue

                source_server = global_context.migrator.get_domain_source_web_server(subscription, item.domain_name)
                vhosts_dir = global_context.migrator.web_files.get_source_vhosts_dir(global_context, source_server)
                source_path = SourceWebPathConverter(vhosts_dir).expand(item.source_path, source_server)
                target_path = TargetWebPathConverter().expand(item.target_path, target_server)
                path_mapping[source_path] = target_path

            issues += adjuster.adjust_paths(target_server, path_mapping, vhost_dir)
            logger.debug(messages.DEBUG_END_ADJUST_PATHS)

        # Finally, compose report tree
        subscription_report = global_context.application_adjust_report.subtarget('Subscription', subscription.name)
        for issue in issues:
            subscription_report.add_issue_obj(issue)
