from django.contrib.auth.decorators import login_required
from django.shortcuts import redirect, render
from django.urls import reverse
from django.views.decorators.http import require_http_methods

from catalog.models import Edition, Item, ItemCategory, PodcastEpisode
from common.models.misc import int_
from journal.models import JournalIndex, JournalQueryParser, Piece, ShelfType
from takahe.models import Post, PostInteraction, TimelineEvent
from takahe.utils import Takahe
from users.models import APIdentity

PAGE_SIZE = 10


def _sidebar_context(user):
    podcast_ids = [
        p.item_id
        for p in user.shelf_manager.get_latest_members(
            ShelfType.PROGRESS, ItemCategory.Podcast
        )
    ]
    recent_podcast_episodes = PodcastEpisode.objects.filter(
        program_id__in=podcast_ids
    ).order_by("-pub_date")[:10]
    books_in_progress = Edition.objects.filter(
        id__in=[
            p.item_id
            for p in user.shelf_manager.get_latest_members(
                ShelfType.PROGRESS, ItemCategory.Book
            )[:10]
        ]
    )
    tvshows_in_progress = Item.objects.filter(
        id__in=[
            p.item_id
            for p in user.shelf_manager.get_latest_members(
                ShelfType.PROGRESS, ItemCategory.TV
            )[:10]
        ]
    )
    return {
        "recent_podcast_episodes": recent_podcast_episodes,
        "books_in_progress": books_in_progress,
        "tvshows_in_progress": tvshows_in_progress,
    }


def _add_interaction_to_events(events, identity_id):
    interactions = PostInteraction.objects.filter(
        identity_id=identity_id,
        post_id__in=[event.subject_post_id for event in events],
        type__in=["like", "boost"],
        state__in=["new", "fanned_out"],
    ).values_list("post_id", "type")
    for event in events:
        if event.subject_post_id:
            event.subject_post.liked_by_current_user = (  # type: ignore
                event.subject_post_id,
                "like",
            ) in interactions
            event.subject_post.boosted_by_current_user = (  # type: ignore
                event.subject_post_id,
                "boost",
            ) in interactions


@require_http_methods(["GET"])
@login_required
def feed(request, typ=0):
    if not request.user.registration_complete:
        return redirect(reverse("users:register"))
    user = request.user
    data = _sidebar_context(user)
    data["feed_type"] = typ
    return render(request, "feed.html", data)


def focus(request):
    return feed(request, typ=1)


@require_http_methods(["GET"])
@login_required
def search(request):
    if not request.user.registration_complete:
        return redirect(reverse("users:register"))
    user = request.user
    data = _sidebar_context(user)
    return render(request, "search_feed.html", data)


@login_required
@require_http_methods(["GET"])
def search_data(request):
    identity_id = request.user.identity.pk
    page = int_(request.GET.get("lastpage")) + 1
    q = JournalQueryParser(request.GET.get("q", default=""), page, page_size=PAGE_SIZE)
    q.filter_by_owner(request.user.identity)
    q.filter("post_id", ">0")
    q.sort(["created:desc"])
    index = JournalIndex.instance()
    if q:
        r = index.search(q)
        events = [
            SearchResultEvent(p)
            for p in r.posts.select_related("author")
            .prefetch_related("attachments")
            .order_by("-id")
        ]
        _add_interaction_to_events(events, identity_id)
    else:
        events = []
    return render(
        request,
        "feed_events.html",
        {"events": events, "page": page},
    )


@login_required
@require_http_methods(["GET"])
def data(request):
    since_id = int_(request.GET.get("last", 0))
    typ = int_(request.GET.get("typ", 0))
    identity_id = request.user.identity.pk
    events = TimelineEvent.objects.filter(
        identity_id=identity_id,
        type__in=[TimelineEvent.Types.post, TimelineEvent.Types.boost],
    )
    match typ:
        case 1:
            events = events.filter(
                subject_post__type_data__object__has_key="relatedWith"
            )
        case _:  # default: no replies
            events = events.filter(subject_post__in_reply_to__isnull=True)
    if since_id:
        events = events.filter(id__lt=since_id)
    events = list(
        events.select_related(
            "subject_post",
            "subject_post__author",
            # "subject_post__author__domain",
            "subject_identity",
            # "subject_identity__domain",
            "subject_post_interaction",
            "subject_post_interaction__identity",
            # "subject_post_interaction__identity__domain",
        )
        .prefetch_related(
            "subject_post__attachments",
            # "subject_post__mentions",
            # "subject_post__emojis",
        )
        .order_by("-id")[:PAGE_SIZE]
    )
    _add_interaction_to_events(events, identity_id)
    return render(
        request,
        "feed_events.html",
        {"feed_type": typ, "events": events},
    )


@require_http_methods(["GET"])
@login_required
def notification(request):
    if not request.user.registration_complete:
        return redirect(reverse("users:register"))
    user = request.user
    podcast_ids = [
        p.item_id
        for p in user.shelf_manager.get_latest_members(
            ShelfType.PROGRESS, ItemCategory.Podcast
        )
    ]
    recent_podcast_episodes = PodcastEpisode.objects.filter(
        program_id__in=podcast_ids
    ).order_by("-pub_date")[:10]
    books_in_progress = Edition.objects.filter(
        id__in=[
            p.item_id
            for p in user.shelf_manager.get_latest_members(
                ShelfType.PROGRESS, ItemCategory.Book
            )[:10]
        ]
    )
    tvshows_in_progress = Item.objects.filter(
        id__in=[
            p.item_id
            for p in user.shelf_manager.get_latest_members(
                ShelfType.PROGRESS, ItemCategory.TV
            )[:10]
        ]
    )
    return render(
        request,
        "notification.html",
        {
            "recent_podcast_episodes": recent_podcast_episodes,
            "books_in_progress": books_in_progress,
            "tvshows_in_progress": tvshows_in_progress,
        },
    )


class NotificationEvent:
    def __init__(self, tle) -> None:
        self.event = tle
        self.type = tle.type
        self.template = tle.type
        self.created = tle.created
        self.identity = (
            APIdentity.objects.filter(pk=tle.subject_identity.pk).first()
            if tle.subject_identity
            else None
        )
        self.post = tle.subject_post
        if self.type == "mentioned":
            # for reply, self.post is the original post
            self.reply = self.post
            self.replies = [self.post]
            self.post = self.post.in_reply_to_post() if self.post else None
        self.piece = Piece.get_by_post_id(self.post.id) if self.post else None
        self.item = getattr(self.piece, "item") if hasattr(self.piece, "item") else None
        if self.piece and self.template in ["liked", "boosted", "mentioned"]:
            cls = self.piece.__class__.__name__.lower()
            self.template += "_" + cls


class SearchResultEvent:
    def __init__(self, post: Post):
        self.type = "post"
        self.subject_post = post
        self.subject_post_id = post.id
        self.created = post.created
        self.published = post.published
        self.identity = post.author


@login_required
@require_http_methods(["GET"])
def events(request):
    match request.GET.get("type"):
        case "follow":
            types = ["followed", "follow_requested"]
        case "mention":
            types = ["mentioned"]
        case _:
            types = ["liked", "boosted", "mentioned", "followed", "follow_requested"]
    es = Takahe.get_events(request.user.identity.pk, types)
    last = request.GET.get("last")
    if last:
        es = es.filter(created__lt=last)
    nes = [NotificationEvent(e) for e in es[:PAGE_SIZE]]
    return render(
        request,
        "events.html",
        {"events": nes},
    )