"""Copies some events from Activity to analytics.Event"""
# Generated by Django 4.2.4 on 2024-04-20 23:55
# pylint: disable=missing-docstring, invalid-name
from datetime import timedelta
from django.db import migrations
from django.db.models import Q
from django.utils.translation import gettext_lazy as _
from askbot.utils.console import ProgressBar

SESSION_IDLE_TIMEOUT = timedelta(minutes=30)

EVENT_TYPES = (
    (1, _('registered')), # Activity.activity_type == 51
    (4, _('question viewed')), # Activity.activity_type == 52
    (6, _('upvoted')), # Activity.activity_type == 9
    (7, _('downvoted')), # Activity.activity_type == 10
    (9, _('asked')), # Activity.activity_type == 1
    (10, _('answered')), # Activity.activity_type == 2
    (11, _('commented question')), # Activity.activity_type == 3
    (12, _('commented answer')), # Activity.activity_type == 4
    (13, _('retagged question')), # Activity.activity_type == 15
)

def delete_analytics_objects(apps, schema_editor): #pylint: disable=missing-docstring, unused-argument
    """Deletes all Session and Event objects"""
    Event = apps.get_model('askbot', 'Event')
    Event.objects.all().delete()
    Session = apps.get_model('askbot', 'Session')
    Session.objects.all().delete()


def consolidate_sessions(sessions):
    """
    Selects the earliest session to keep.
    Updates the updated_at to the latest of all.
    Assigns all events from other sessions to the selected one.
    Deletes other sessions.
    Returns the selected session.
    """
    session = min(sessions, key=lambda sess: sess.created_at)
    session.updated_at = max(sessions, key=lambda sess: sess.updated_at).updated_at
    other_sessions = filter(session.__ne__, sessions)
    for sess in other_sessions:
        events = sess.event_set.all()
        events.update(session=session)
        sess.delete()

    return session


def update_session_timestamps(session, timestamp):
    """Updates session's updated_at and created_at fields"""
    session.updated_at = max(timestamp, session.updated_at)
    session.created_at = min(timestamp, session.created_at)
    session.save()


def get_consolidated_user_session(user_id, timestamp, apps): #pylint: disable=missing-docstring
    """Returns first user session which is within SESSION_IDLE_TIMEOUT of the given timestamp
    for this user.
    If such session does not exist, creates it.
    Sessions are glued together if they are within SESSION_IDLE_TIMEOUT of each other.
    """
    Session = apps.get_model('askbot', 'Session')
    dt_range = (timestamp - SESSION_IDLE_TIMEOUT, timestamp + SESSION_IDLE_TIMEOUT)
    user_q = Q(user_id=user_id)
    updated_at_q = Q(updated_at__range=dt_range)
    created_at_q = Q(created_at__range=dt_range)
    sessions = Session.objects.filter(user_q & (updated_at_q | created_at_q))
    sessions_count = sessions.count()

    if sessions_count > 1:
        session = consolidate_sessions(sessions)
        update_session_timestamps(session, timestamp)
    elif sessions_count == 1:
        session = sessions[0]
        update_session_timestamps(session, timestamp)
    else:
        session = Session.objects.create(user_id=user_id,
                                         created_at=timestamp,
                                         updated_at=timestamp)

    return session


def populate_user_registered_events(apps): #pylint: disable=missing-docstring
    """For each user, creates a user registered event.
    With the timestamp of user's registration date.
    """
    User = apps.get_model('auth', 'User')
    Event = apps.get_model('askbot', 'Event')
    ContentType = apps.get_model('contenttypes', 'ContentType')
    content_type = ContentType.objects.get_for_model(User)

    users = User.objects.all().only('pk', 'date_joined')
    count = users.count()
    message = 'Populating user registered events'
    for user in ProgressBar(users.iterator(), count, message):
        timestamp = user.date_joined
        session = get_consolidated_user_session(user.pk, timestamp, apps)
        Event.objects.create(
            session=session,
            event_type=1,
            timestamp=user.date_joined,
            content_type=content_type,
            object_id=user.pk
        )


def populate_question_viewed_events(apps): #pylint: disable=missing-docstring
    """For each QuestionView record,
    creates a corresponding analytics.Event object"""
    QuestionView = apps.get_model('askbot', 'QuestionView')
    Event = apps.get_model('askbot', 'Event')
    Post = apps.get_model('askbot', 'Post')
    ContentType = apps.get_model('contenttypes', 'ContentType')
    content_type = ContentType.objects.get_for_model(Post)

    views = QuestionView.objects.all().only('when', 'who_id', 'question_id')
    count = views.count()
    message = 'Populating Question View events'
    for view in ProgressBar(views.iterator(), count, message):
        timestamp = view.when
        session = get_consolidated_user_session(view.who_id, timestamp, apps)
        Event.objects.create(
            session=session,
            event_type=4,
            timestamp = timestamp,
            content_type=content_type,
            object_id=view.question_id
        )


def populate_voted_events(apps, activity_type=None, event_type=None, message=None): #pylint: disable=too-many-locals
    """For each vote activity, creates a corresponding analytics Event object"""
    Activity = apps.get_model('askbot', 'Activity')
    Event = apps.get_model('askbot', 'Event')
    Post = apps.get_model('askbot', 'Post')
    Vote = apps.get_model('askbot', 'Vote')
    ContentType = apps.get_model('contenttypes', 'ContentType')
    content_type = ContentType.objects.get_for_model(Post)

    activity_fields = ('active_at', 'user_id', 'object_id', 'content_type')
    acts = Activity.objects.filter(activity_type=activity_type).only(*activity_fields)
    count = acts.count()
    for act in ProgressBar(acts.iterator(), count, message):
        session = get_consolidated_user_session(act.user_id, act.active_at, apps)

        if act.content_type.model != 'vote':
            continue

        vote = Vote.objects.filter(pk=act.object_id).only('voted_post_id')

        if not vote.exists():
            continue

        post_id = vote[0].voted_post_id

        Event.objects.create(
            session=session,
            event_type=event_type,
            timestamp=act.active_at,
            content_type=content_type,
            object_id=post_id
        )


def populate_posted_events(apps, post_type=None, event_type=None, message=None): #pylint: disable=too-many-locals
    """For each question, creates a corresponding analytics Event object"""
    Event = apps.get_model('askbot', 'Event')
    Post = apps.get_model('askbot', 'Post')
    ContentType = apps.get_model('contenttypes', 'ContentType')
    content_type = ContentType.objects.get_for_model(Post)

    posts = Post.objects.filter(post_type=post_type).only('id', 'added_at', 'author_id')
    count = posts.count()
    for post in ProgressBar(posts.iterator(), count, message):
        timestamp = post.added_at
        session = get_consolidated_user_session(post.author_id, timestamp, apps)
        Event.objects.create(
            session=session,
            event_type=event_type,
            timestamp = timestamp,
            content_type=content_type,
            object_id=post.id
        )


def populate_commented_events(apps, parent_post_type=None, event_type=None, message=None): #pylint: disable=missing-docstring
    Event = apps.get_model('askbot', 'Event')
    Post = apps.get_model('askbot', 'Post')
    ContentType = apps.get_model('contenttypes', 'ContentType')
    content_type = ContentType.objects.get_for_model(Post)

    comments = Post.objects.filter(
                        post_type='comment',
                        parent__post_type=parent_post_type
                    ).only('id', 'added_at', 'author_id')
    count = comments.count()
    for question in ProgressBar(comments.iterator(), count, message):
        timestamp = question.added_at
        session = get_consolidated_user_session(question.author_id, timestamp, apps)
        Event.objects.create(
            session=session,
            event_type=event_type,
            timestamp = timestamp,
            content_type=content_type,
            object_id=question.id
        )


def populate_retagged_question_events(apps): #pylint: disable=missing-docstring
    Event = apps.get_model('askbot', 'Event')
    Activity = apps.get_model('askbot', 'Activity')
    Post = apps.get_model('askbot', 'Post')
    act_fields = ('active_at', 'user_id', 'object_id', 'content_type')
    acts = Activity.objects.filter(activity_type=15).only(*act_fields)
    count = acts.count()
    message = 'Populating retagged question events'
    ContentType = apps.get_model('contenttypes', 'ContentType')
    post_content_type_id = ContentType.objects.get_for_model(Post).id
    for act in ProgressBar(acts.iterator(), count, message):
        if act.content_type_id != post_content_type_id:
            continue

        session = get_consolidated_user_session(act.user_id, act.active_at, apps)
        Event.objects.create(
            session=session,
            event_type=13,
            timestamp=act.active_at,
            content_type=act.content_type,
            object_id=act.object_id
        )


def populate_analytics_events(apps, schema_editor): #pylint: disable=unused-argument, missing-docstring
    populate_user_registered_events(apps)
    populate_question_viewed_events(apps)
    populate_voted_events(apps, activity_type=9, event_type=6,
                          message='Populating upvoted events')
    populate_voted_events(apps, activity_type=10, event_type=7,
                          message='Populating downvoted events')
    populate_posted_events(apps, post_type='question', event_type=9,
                           message='Populating asked events')
    populate_posted_events(apps, post_type='answer', event_type=10,
                           message='Populating answered events')
    populate_commented_events(apps, parent_post_type='question',
                              event_type=11, message='Populating commented question events')
    populate_commented_events(apps, parent_post_type='answer',
                              event_type=12, message='Populating commented answer events')
    populate_retagged_question_events(apps)


class Migration(migrations.Migration):

    dependencies = [
        ('contenttypes', '0002_remove_content_type_name'),
        ('askbot', '0026_alter_activity_activity_type_and_more'),
    ]

    operations = [
        migrations.RunPython(populate_analytics_events, reverse_code=delete_analytics_objects),
    ]
