# authentic2 - versatile identity manager
# Copyright (C) 2010-2019 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

from django.apps import apps
from django.conf import settings
from django.db import DEFAULT_DB_ALIAS, router, transaction
from django.utils.translation import override
from django.utils.translation import ugettext as _

from authentic2.utils.misc import get_fk_model
from django_rbac.managers import defer_update_transitive_closure
from django_rbac.utils import get_operation, get_ou_model, get_role_model


def create_default_ou(app_config, verbosity=2, interactive=True, using=DEFAULT_DB_ALIAS, **kwargs):
    if not router.allow_migrate(using, get_ou_model()):
        return
    # be sure new objects names are localized using the default locale
    with override(settings.LANGUAGE_CODE):
        OrganizationalUnit = get_ou_model()
        if OrganizationalUnit.objects.exists():
            return
        # Create a default OU if none exists currently
        default_ou, created = OrganizationalUnit.objects.get_or_create(
            slug='default',
            defaults={
                'default': True,
                'name': _('Default organizational unit'),
            },
        )
        # Update all existing models having an ou field to the default ou
        for app in apps.get_app_configs():
            for model in app.get_models():
                related_model = get_fk_model(model, 'ou')
                if not related_model == OrganizationalUnit:
                    return
                model.objects.filter(ou__isnull=True).update(ou=default_ou)


def post_migrate_update_rbac(app_config, verbosity=2, interactive=True, using=DEFAULT_DB_ALIAS, **kwargs):
    # be sure new objects names are localized using the default locale
    from .management import update_content_types_roles, update_ous_admin_roles

    if not router.allow_migrate(using, get_role_model()):
        return
    with override(settings.LANGUAGE_CODE):
        with transaction.atomic():
            with defer_update_transitive_closure():
                update_content_types_roles()
                update_ous_admin_roles()


def update_rbac_on_ou_post_save(sender, instance, created, raw, **kwargs):
    from .management import update_ou_admin_roles, update_ous_admin_roles

    if get_ou_model().objects.count() < 3 and created:
        update_ous_admin_roles()
    else:
        update_ou_admin_roles(instance)


def update_rbac_on_ou_post_delete(sender, instance, **kwargs):
    from .management import update_ous_admin_roles

    if get_ou_model().objects.count() < 2:
        update_ous_admin_roles()


def update_service_role_ou(sender, instance, created, raw, **kwargs):
    get_role_model().objects.filter(service=instance).update(ou=instance.ou)


def create_default_permissions(app_config, verbosity=2, interactive=True, using=DEFAULT_DB_ALIAS, **kwargs):
    from .models import (
        ACTIVATE_OP,
        CHANGE_EMAIL_OP,
        CHANGE_PASSWORD_OP,
        MANAGE_AUTHORIZATIONS_OP,
        MANAGE_MEMBERS_OP,
        RESET_PASSWORD_OP,
    )

    if not router.allow_migrate(using, get_ou_model()):
        return

    with override(settings.LANGUAGE_CODE):
        get_operation(CHANGE_PASSWORD_OP)
        get_operation(RESET_PASSWORD_OP)
        get_operation(ACTIVATE_OP)
        get_operation(CHANGE_EMAIL_OP)
        get_operation(MANAGE_MEMBERS_OP)
        get_operation(MANAGE_AUTHORIZATIONS_OP)
