from parallels.core import get_logger
from parallels.core.connections.database_servers.base import DatabaseServer
from parallels.core.converter.database_user import TargetDatabaseUserConflictingObject
from parallels.core.utils.common import format_multiline_list
from parallels.core.utils.database_server_type import DatabaseServerType
from parallels.core.utils.name_conflicts import ConflictingHostingObjectDescription, SourceConflictingObject
from parallels.core.utils.plesk_utils import get_database_users_of_server
from parallels.plesk.source.web import messages

logger = get_logger(__name__)


class DatabaseUserConflictingObjectDescription(ConflictingHostingObjectDescription):
    def __init__(self, global_context):
        """
        :type global_context: parallels.plesk.source.web.global_context.WebGlobalMigrationContext
        """
        self._global_context = global_context

    def get_type_id(self):
        """Get string identifier of object type, used not to mix renames in persistent storage.

        For example, for system user it could return 'sysuser', for customer it could return 'customer',
        for reseller it could return 'reseller' and so on.

        :rtype: str | unicode
        """
        return 'database_user'

    def list_target_objects(self):
        """Get list of objects on target panel, which may conflict with migrated objects from source panels

        :rtype: list[parallels.core.utils.name_conflicts.TargetConflictingObject]
        """
        target_database_users = set()

        for database_user in self._global_context.list_all_migrated_database_users():
            target_db_server = database_user.database.target_server

            if target_db_server is None:
                continue

            assert isinstance(target_db_server, DatabaseServer)

            users = get_database_users_of_server(
                self._global_context.conn.target.plesk_server, DatabaseServerType.MYSQL, target_db_server.host()
            )

            for user in users:
                target_database_users.add((
                    user['subscription_name'], user['user_name'], DatabaseServerType.MYSQL,
                    target_db_server.host(), target_db_server.port()
                ))

            panel_database_user_names = {user['user_name'] for user in users}
            logger.fdebug(
                messages.PANEL_DATABASE_USERS,
                db_server=target_db_server.description(),
                database_users=format_multiline_list(sorted(panel_database_user_names))
            )
            real_database_user_names = set(target_db_server.list_database_users())
            other_database_user_names = real_database_user_names - panel_database_user_names
            logger.fdebug(
                messages.OTHER_DATABASE_USERS,
                db_server=target_db_server.description(),
                database_users=format_multiline_list(sorted(other_database_user_names))
            )
            for db_user_name in other_database_user_names:
                target_database_users.add((
                    None, db_user_name, DatabaseServerType.MYSQL,
                    target_db_server.host(), target_db_server.port()
                ))
        return [
            TargetDatabaseUserConflictingObject(
                subscription_name, database_user_name, database_type, database_host, database_port
            )
            for subscription_name, database_user_name, database_type, database_host, database_port
            in target_database_users
        ]

    def list_source_objects(self):
        """Get list of objects in source panels, for which we should resolve name conflicts

        :rtype: list[parallels.plesk.source.web.conflicts.database_user.SourceDatabaseUserConflictingObject]
        """
        source_database_users = []

        for database_user in self._global_context.list_all_migrated_database_users():
            source_database_users.append(SourceDatabaseUserConflictingObject(database_user))

        return source_database_users


class SourceDatabaseUserConflictingObject(SourceConflictingObject):
    """
    :type _database_user: parallels.plesk.source.web.migrated_database_user.WebMigratedDatabaseUser
    """

    def __init__(self, database_user):
        """
        :type database_user: parallels.plesk.source.web.migrated_database_user.WebMigratedDatabaseUser
        """
        self._database_user = database_user

    def get_name(self):
        """Get name of the object - that one that should be changed in case of conflicts.

        :rtype: str | unicode
        """
        return self._database_user.target_username

    def get_key(self):
        """Get key of the object - what differs 2 objects with the same name.

        Objects with the same key and the same name conflict with each other.
        Objects with different keys never conflict.

        Returns tuple (database type, target database server host, target database server port)

        :rtype: tuple | None
        """
        target_db_server = self._database_user.database.target_server

        if target_db_server is None:
            # if target database server is unknown - resolve conflicts with any database user name
            return DatabaseServerType.MYSQL, None, None

        assert isinstance(target_db_server, DatabaseServer)

        return DatabaseServerType.MYSQL, target_db_server.host(), target_db_server.port()

    def get_subscription_name(self):
        """Get name of subscription which owns the object.

        :rtype: str | unicode
        """
        return self._database_user.target_subscription_name

    def set_name(self, new_name):
        """Rename object in case of conflicts

        :type new_name: str | unicode
        """
        self._database_user.set_target_username(new_name)
