"""
This module contains the signal handlers of the :py:mod:`userdbs` app.

The module starts Celery_ tasks.

.. _Celery: http://www.celeryproject.org/

"""
import logging

from django.db.models.signals import post_delete, post_save
from django.dispatch import receiver
from passlib.pwd import genword

from mysqltasks.tasks import (
    create_mysql_database,
    create_mysql_user,
    delete_mysql_database,
    delete_mysql_user,
    set_mysql_userpassword,
)
from pgsqltasks.tasks import (
    create_pgsql_database,
    create_pgsql_user,
    delete_pgsql_database,
    delete_pgsql_user,
    set_pgsql_userpassword,
)
from taskresults.models import TaskResult

from .models import DB_TYPES, DatabaseUser, UserDatabase, password_set

_LOGGER = logging.getLogger(__name__)


@receiver(password_set, sender=DatabaseUser)
def handle_dbuser_password_set(sender, instance, password, **kwargs):
    """
    Signal handler triggered by password changes for
    :py:class:`userdbs.models.DatabaseUser` instances.

    :param sender: the sender of the signal
    :param instance: the Database user instance
    :param str password: the new password for the database user

    This signal handler starts Celery_ tasks depending on the db_type value of
    the database user.

    .. blockdiag::
        :desctable:

        blockdiag {
            node_width = 200;

            A -> B;
            A -> C;

            A [ label = "", shape = beginpoint,
                description = "this signal handler" ];
            B [ label = "set mysql userpassword", color = "PowderBlue",
                description = ":py:func:`set_mysql_userpassword()
                <mysqltasks.tasks.set_mysql_userpassword>` called with
                database username and password" ];
            C [ label = "set pgsql userpassword", color = "DodgerBlue",
                description = ":py:func:`set_pgsql_userpassword()
                <pgsqltasks.tasks.set_pgsql_userpassword>` called with
                database username and password" ];
        }

    """
    if instance.db_type == DB_TYPES.mysql:
        taskresult = TaskResult.objects.create_task_result(
            "handle_dbuser_password_set",
            set_mysql_userpassword.s(instance.name, password),
            "mysql password change",
        )
        _LOGGER.info(
            "MySQL password change has been requested in task %s", taskresult.task_id
        )
    elif instance.db_type == DB_TYPES.pgsql:
        taskresult = TaskResult.objects.create_task_result(
            "handle_dbuser_password_set",
            set_pgsql_userpassword.s(instance.name, password),
            "pgsql password change",
        )
        _LOGGER.info(
            "PostgreSQL password change has been requested in task %s",
            taskresult.task_id,
        )
    else:
        _LOGGER.warning(
            "Password change has been requested for unknown database %s"
            " the request has been ignored.",
            instance.db_type,
        )


@receiver(post_save, sender=DatabaseUser)
def handle_dbuser_created(sender, instance, created, **kwargs):
    """
    Signal handler triggered after the creation of or updates to
    :py:class:`userdbs.models.DatabaseUser` instances.

    :param sender: the sender of the signal
    :param instance: the DatabaseUser instance
    :param bool created: whether this signal handler is called for a newly
        created instance

    This signal handler starts Celery_ tasks depending on the db_type value of
    the database user.

    .. blockdiag::
        :desctable:

        blockdiag {
            node_width = 200;

            A -> B;
            A -> C;

            A [ label = "", shape = beginpoint,
                description = "this signal handler" ];
            B [ label = "create mysql user", color = "PowderBlue",
                description = ":py:func:`create_mysql_user()
                <mysqltasks.tasks.create_mysql_user>` called with database
                username and password" ];
            C [ label = "create pgsql user", color = "DodgerBlue",
                description = ":py:func:`create_pgsql_user
                <pgsqltasks.tasks.create_pgsql_user>` called with database
                username and password" ];
        }

    """
    if created:
        password = kwargs.get("password", genword())
        # TODO: send GPG encrypted mail with this information
        if instance.db_type == DB_TYPES.mysql:
            taskresult = TaskResult.objects.create_task_result(
                "handle_dbuser_created",
                create_mysql_user.s(instance.name, password),
                "mysql user creation",
            )
            _LOGGER.info(
                "A new MySQL user %s creation has been requested in task %s",
                instance.name,
                taskresult.task_id,
            )
        elif instance.db_type == DB_TYPES.pgsql:
            taskresult = TaskResult.objects.create_task_result(
                "handle_dbuser_created",
                create_pgsql_user.s(instance.name, password),
                "pgsql user creation",
            )
            _LOGGER.info(
                "A new PostgreSQL user %s creation has been requested in task" " %s",
                instance.name,
                taskresult.task_id,
            )
        else:
            _LOGGER.warning(
                "created DatabaseUser for unknown database type %s", instance.db_type
            )
    _LOGGER.debug(
        "database user %s has been %s", instance, created and "created" or "updated"
    )


@receiver(post_delete, sender=DatabaseUser)
def handle_dbuser_deleted(sender, instance, **kwargs):
    """
    Signal handler triggered after the deletion of
    :py:class:`userdbs.models.DatabaseUser` instances.

    :param sender: the sender of the signal
    :param instance: the DatabaseUser instance

    This signal handler starts Celery_ tasks depending on the db_type value of
    the database user.

    .. blockdiag::
        :desctable:

        blockdiag {
            node_width = 200;

            A -> B;
            A -> C;

            A [ label = "", shape = beginpoint,
                description = "this signal handler" ];
            B [ label = "delete mysql user", color = "PowderBlue",
                description = ":py:func:`delete_mysql_user()
                <mysqltasks.tasks.delete_mysql_user>` called with username
                from instance.name" ];
            C [ label = "delete pgsql user", color = "DodgerBlue",
                description = ":py:func:`delete_pgsql_user()
                <pgsqltasks.tasks.delete_pgsql_user>` called with username
                from instance.name" ];
        }
    """
    if instance.db_type == DB_TYPES.mysql:
        taskresult = TaskResult.objects.create_task_result(
            "handle_dbuser_deleted",
            delete_mysql_user.s(instance.name),
            "mysql user deletion",
        )
        _LOGGER.info(
            "MySQL user %s deletion has been requested in task %s",
            instance.name,
            taskresult.task_id,
        )
    elif instance.db_type == DB_TYPES.pgsql:
        taskresult = TaskResult.objects.create_task_result(
            "handle_dbuser_deleted",
            delete_pgsql_user.s(instance.name),
            "pgsql user deletion",
        )
        _LOGGER.info(
            "PostgreSQL user %s deletion has been requested in task %s",
            instance.name,
            taskresult.task_id,
        )
    else:
        _LOGGER.warning(
            "deleted DatabaseUser %s for unknown database type %s",
            instance.name,
            instance.db_type,
        )
    _LOGGER.debug("database user %s has been deleted", instance)


@receiver(post_save, sender=UserDatabase)
def handle_userdb_created(sender, instance, created, **kwargs):
    """
    Signal handler triggered after the creation of or updates to
    :py:class:`userdbs.models.UserDatabase` instances.

    :param sender: the sender of the signal
    :param instance: the UserDatabase instance
    :param bool created: whether this signal handler has been called for a
        newly created instance

    This signal handler starts Celery_ tasks depending on the db_type value of
    the database user owning the UserDatabase instance.

    .. blockdiag::
        :desctable:

        blockdiag {
            node_width = 200;

            A -> B;
            A -> C;

            A [ label = "", shape = beginpoint,
                description = "this signal handler" ];
            B [ label = "create mysql database", color = "PowderBlue",
                description = ":py:func:`create_mysql_database()
                <mysqltasks.tasks.create_mysql_database>` called with database
                name and username" ];
            C [ label = "create pgsql database", color = "DodgerBlue",
                description = ":py:func:`create_pgsql_database()
                <pgsqltasks.tasks.create_pgsql_database>` called with database
                name and username" ];
        }
    """
    if created:
        if instance.db_user.db_type == DB_TYPES.mysql:
            taskresult = TaskResult.objects.create_task_result(
                "handle_userdb_created",
                create_mysql_database.s(instance.db_name, instance.db_user.name),
                "mysql database creation",
            )
            _LOGGER.info(
                "The creation of a new MySQL database %s has been requested in"
                " task %s",
                instance.db_name,
                taskresult.task_id,
            )
        elif instance.db_user.db_type == DB_TYPES.pgsql:
            taskresult = TaskResult.objects.create_task_result(
                "handle_userdb_created",
                create_pgsql_database.s(instance.db_name, instance.db_user.name),
                "pgsql database creation",
            )
            _LOGGER.info(
                "The creation of a new PostgreSQL database %s has been"
                " requested in task %s",
                instance.db_name,
                taskresult.task_id,
            )
        else:
            _LOGGER.warning(
                "created UserDatabase for unknown database type %s",
                instance.db_user.db_type,
            )
    _LOGGER.debug(
        "database %s has been %s", instance, created and "created" or "updated"
    )


@receiver(post_delete, sender=UserDatabase)
def handle_userdb_deleted(sender, instance, **kwargs):
    """
    Signal handler triggered after the deletion of
    :py:class:`userdbs.models.UserDatabase` instances.

    :param sender: the sender of the signal
    :param instance: the UserDatabase instance

    This signal handler starts Celery_ tasks depending on the db_type value of
    the database user owning the UserDatabase instance.

    .. blockdiag::
        :desctable:

        blockdiag {
            node_width = 200;

            A -> B;
            A -> C;

            A [ label = "", shape = beginpoint,
                description = "this signal handler" ];
            B [ label = "delete mysql database", color = "PowderBlue",
                description = ":py:func:`delete_mysql_user()
                <mysqltasks.tasks.delete_mysql_user>` called with database
                name and username" ];
            C [ label = "delete pgsql database", color = "DodgerBlue",
                description = ":py:func:`delete_pgsql_user()
                <pgsqltasks.tasks.delete_pgsql_user>` called with database
                name" ];
        }
    """
    if instance.db_user.db_type == DB_TYPES.mysql:
        taskresult = TaskResult.objects.create_task_result(
            "handle_userdb_deleted",
            delete_mysql_database.s(instance.db_name, instance.db_user.name),
            "mysql database deletion",
        )
        _LOGGER.info(
            "The deletion of MySQL database %s has been requested in task %s",
            instance.db_name,
            taskresult.task_id,
        )
    elif instance.db_user.db_type == DB_TYPES.pgsql:
        taskresult = TaskResult.objects.create_task_result(
            "handle_userdb_deleted",
            delete_pgsql_database.s(instance.db_name),
            "pgsql database deletion",
        )
        _LOGGER.info(
            "The deletion of PostgreSQL database %s has been requested in " " task %s",
            instance.db_name,
            taskresult.task_id,
        )
    else:
        _LOGGER.warning(
            "deleted UserDatabase %s of unknown type %s",
            instance.db_name,
            instance.db_type,
        )
        pass
    _LOGGER.debug("database %s has been deleted", instance)
