from django.apps import apps
from django.contrib.auth import get_permission_codename

from django_object_authority.settings import DEFAULT_PERMISSIONS, PERMISSION_FOR_APPLICATIONS, PERMISSION_FOR_MODELS


def create_update_permissions(applications=None, models=None, permissions=None, **kwargs):
    """
    Create the default permissions (view, add, change, delete) for each model of application or those models
    you specify in settings or as local parameter of those application also specified in settings or as local parameter.

    :param applications: list of application names
    :param models: list of model names
    :param permissions: list of permission you want to create or update
    :param kwargs: extra args that post-migrate signal sends.
    :return:
    """

    created_perm, updated_perm = 0, 0

    # get default applications from settings or all from installed apps.
    default_apps = PERMISSION_FOR_APPLICATIONS or apps.all_models.keys()
    available_applications = set(default_apps)
    if applications:
        available_applications = set(applications) & set(apps.all_models.keys())

    # get filtering models
    default_models = PERMISSION_FOR_MODELS or models
    available_models = set()
    for app in available_applications:
        # iterate for all available applications to filter by models.
        # filtering expected a list of models and those models don't belong to many to many model relations.
        app_models = set()
        for model_name, model in apps.all_models.get(app).items():
            included = default_models is None or (default_models and model_name in set(default_models))
            if included and not is_autogenerated(model):
                app_models.add(model)

        # set all application models if filter is not defined or not apply
        if not app_models:
            app_models = set(apps.all_models.get(app).values())
        available_models.update(app_models)

    # Update default permissions with the list specified as parameter
    permissions = DEFAULT_PERMISSIONS if not permissions else list(permissions, ) + list(DEFAULT_PERMISSIONS)

    for model in available_models:
        result = _create_permissions(model, permissions)
        created_perm += result[0]
        updated_perm += result[1]

    return created_perm, updated_perm


def _create_permissions(model, permissions):
    """Get permission or create it."""
    from django.contrib.auth.models import Permission
    from django.contrib.contenttypes.models import ContentType

    created, updated = 0, 0
    if permissions is not None:
        for perm in permissions:
            kwargs = {
                'codename': "{}_{}".format(perm, model._meta.model_name),
                'content_type': ContentType.objects.get_for_model(model)
            }
            defaults = {'name': "Can {} {}".format(perm.replace('_', ' '), model._meta.verbose_name)}
            obj, _created = Permission.objects.get_or_create(defaults=defaults, **kwargs)

            # count number of permission has created
            if _created:
                created += 1
            else:
                updated += 1

    return created, updated


def get_full_permission_codename(action, opts):
    """
    Returns the full codename of the permission for the specified action.
    """
    app_label = getattr(opts, 'app_label', '')
    return '{}.{}'.format(app_label, get_permission_codename(action, opts))


def is_autogenerated(model):
    """
    Check if model is auto generated.
    Is supposed that auto generated django models always has underscore in his name to separate two linked tables and
    there is not possible define a custom model with underscores.

    :param model:
    :return:
    """
    return '_' in model._meta.model_name