import logging

from django.db import reset_queries, DEFAULT_DB_ALIAS
from django.db.models import Count, signals

from . import registry
from . import settings
from .models import Badge, Award
from .utils import log_queries

logger = logging.getLogger('badgify')


def sync_badges(**kwargs):
    """
    Iterates over registered recipes and creates missing badges.
    """
    update = kwargs.get('update', False)
    created_badges = []
    instances = registry.get_recipe_instances()

    for instance in instances:
        reset_queries()
        badge, created = instance.create_badge(update=update)
        if created:
            created_badges.append(badge)
        log_queries(instance)

    return created_badges


def sync_counts(**kwargs):
    """
    Iterates over registered recipes and denormalizes ``Badge.users.count()``
    into ``Badge.users_count`` field.
    """
    badges = kwargs.get('badges')
    excluded = kwargs.get('exclude_badges')

    instances = registry.get_recipe_instances(badges=badges, excluded=excluded)
    updated_badges, unchanged_badges = [], []

    for instance in instances:
        reset_queries()
        badge, updated = instance.update_badge_users_count()
        if updated:
            updated_badges.append(badge)
        else:
            unchanged_badges.append(badge)
        log_queries(instance)

    return (updated_badges, unchanged_badges)


def sync_awards(**kwargs):
    """
    Iterates over registered recipes and possibly creates awards.
    """
    badges = kwargs.get('badges')
    excluded = kwargs.get('exclude_badges')
    disable_signals = kwargs.get('disable_signals')
    batch_size = kwargs.get('batch_size', None)
    db_read = kwargs.get('db_read', None)

    award_post_save = True

    if disable_signals:
        settings.AUTO_DENORMALIZE = False
        award_post_save = False

    instances = registry.get_recipe_instances(badges=badges, excluded=excluded)

    for instance in instances:
        reset_queries()
        instance.create_awards(
            batch_size=batch_size,
            db_read=db_read,
            post_save_signal=award_post_save)
        log_queries(instance)


def show_stats(**kwargs):
    """
    Shows badges stats.
    """
    db_read = kwargs.get('db_read', DEFAULT_DB_ALIAS)

    badges = (Badge.objects.using(db_read)
                           .all()
                           .annotate(u_count=Count('users'))
                           .order_by('u_count'))

    for badge in badges:
        logger.info('{:<20} {:>10} users awarded | users_count: {})'.format(
            badge.name,
            badge.u_count,
            badge.users_count))


def reset_awards(**kwargs):
    """
    Resets badges stats.
    """
    filter_badges = kwargs.get('badges', None)
    exclude_badges = kwargs.get('exclude_badges', None)

    for option in [filter_badges, exclude_badges]:
        if option:
            if not isinstance(option, (list, tuple)):
                option = [option]

    signals.pre_delete.disconnect(sender=Award,
                                  dispatch_uid='badgify.award.pre_delete.decrement_badge_users_count')

    award_qs = Award.objects.all()
    badge_qs = Badge.objects.all()

    if filter_badges:
        award_qs = award_qs.filter(badge__slug__in=filter_badges)
        badge_qs = badge_qs.filter(slug__in=filter_badges)

    if exclude_badges:
        award_qs = award_qs.exclude(badge__slug__in=exclude_badges)
        badge_qs = badge_qs.exclude(slug__in=exclude_badges)

    awards_count = award_qs.count()
    award_qs.delete()
    logger.info('✓ Deleted %d awards', awards_count)

    badges_count = badge_qs.count()
    badge_qs.update(users_count=0)
    logger.info('✓ Reseted Badge.users_count field of %d badge(s)', badges_count)