from intranet.femida.src.core.controllers import update_instance
from intranet.femida.src.core.signals import post_bulk_create
from intranet.femida.src.notifications.models import CategorySubscription
from intranet.femida.src.notifications.problems import (
    ProblemCreatedNotification,
    ProblemUpdatedNotification,
)

from . import models


def update_or_create_problem(data, initiator=None, instance=None):
    categories = data.pop('categories', [])
    is_creation = instance is None
    old_problem = None
    old_categories = []

    if is_creation:
        instance = models.Problem.objects.create(
            created_by=initiator,
            **data
        )
    else:
        old_problem = models.Problem.objects.get(id=instance.id)
        old_categories = list(old_problem.categories.all())
        update_instance(instance, data)

    instance.categories.set(categories)

    if is_creation:
        notification = ProblemCreatedNotification(instance, initiator)
        notification.send()
    else:
        notification = ProblemUpdatedNotification(
            instance=instance,
            initiator=initiator,
            old_instance=old_problem,
            old_categories=old_categories,
        )
        notification.send()

    return instance


def add_problems_from_preset(link_model_class, preset, target, target_field_name):
    problem_ids = list(
        models.PresetProblem.objects
        .filter(preset=preset)
        .order_by('position')
        .values_list('problem_id', flat=True)
    )
    return bulk_create_problem_links(
        link_model_class=link_model_class,
        problem_ids=problem_ids,
        target=target,
        target_field_name=target_field_name,
    )


def bulk_create_problem_links(link_model_class, problem_ids, target, target_field_name):
    """
    Массово привязываем задачи к объекту. Игнорируем те задачи, которые уже были привязаны к объекту

    :param link_model_class: Модель связки с задачей (Assignment или PresetProblem)
    :param problem_ids: Список id задач, которые надо привязать к целевому объекту в нужном порядке
    :param target: Целевой объект, к которому привязываем задачи (Interview или Preset)
    :param target_field_name: Название поля целевого объекта
    :return:
    """
    qs = (
        link_model_class.objects
        .filter(**{target_field_name: target})
        .values_list('position', 'problem_id')
        .order_by('position')
    )
    position = None
    existing_problem_ids = set()
    for position, problem_id in qs:
        existing_problem_ids.add(problem_id)
    # TODO: Я не стал пока засовывать логику про position в bulk_create, потому что тогда придется
    # обрабатывать общий случай, когда в одном запросе будут разные interview, что нам ни к чему.
    start_position = position + 1 if position is not None else 0

    objs = []
    for i, problem_id in enumerate(problem_ids):
        if problem_id in existing_problem_ids:
            # TODO: Возвращать непривязанные задачи на фронт
            continue
        objs.append(
            link_model_class(**{
                'problem_id': problem_id,
                'position': start_position + i,
                target_field_name: target,
            })
        )
    created_objs = link_model_class.objects.bulk_create(objs)
    post_bulk_create.send(
        sender=link_model_class,
        queryset=created_objs,
    )
    return created_objs


def subscribe_to_category(category, user):
    subscription, _ = CategorySubscription.objects.get_or_create(
        category=category,
        user=user,
    )
    return subscription


def unsubscribe_from_category(category, user):
    subscriptions = CategorySubscription.objects.filter(
        category=category,
        user=user,
    )
    subscriptions.delete()
