from django.contrib import messages
from django.core.paginator import Paginator
from django.db.models import Count, Q
from django.http import JsonResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.utils import timezone
from django.views import View
from django.views.decorators.http import require_POST

from apps.core.mixins import EditeurRequiredMixin, safe_next
from apps.core.ratelimit import client_ip, ratelimit
from apps.editorial.models import Article

from .forms import CommentForm
from .models import Comment, CommentVote, Reaction

# --- Public -------------------------------------------------------------------


@require_POST
@ratelimit("comment", 5)
def comment_create(request, pk):
    article = get_object_or_404(Article.objects.published(), pk=pk)
    form = CommentForm(request.POST, user=request.user)
    if form.is_valid():
        comment = form.save(commit=False)
        comment.article = article
        comment.ip_address = client_ip(request) or None
        if request.user.is_authenticated:
            comment.user = request.user
        parent_id = form.cleaned_data.get("parent_id")
        if parent_id:
            comment.parent = Comment.objects.filter(pk=parent_id, article=article).first()
        comment.save()
        messages.success(request, "Votre commentaire a été envoyé et sera publié après modération.")
    else:
        messages.error(request, "Commentaire non envoyé : " + " ".join(e for errs in form.errors.values() for e in errs))
    return redirect(article.get_absolute_url() + "#commentaires")


def _identity(request):
    if request.user.is_authenticated:
        return {"user": request.user, "ip_address": None}
    return {"user": None, "ip_address": client_ip(request) or None}


@require_POST
@ratelimit("react", 20)
def reaction_toggle(request, pk):
    article = get_object_or_404(Article.objects.published(), pk=pk)
    ident = _identity(request)
    existing = Reaction.objects.filter(article=article, **ident).first()
    if existing:
        existing.delete()
        message, liked = "Votre avis a été retiré.", False
    else:
        Reaction.objects.create(article=article, **ident)
        message, liked = "Merci pour votre avis !", True
    return JsonResponse({
        "success": True,
        "message": message,
        "likesCount": article.reactions.count(),
        "hasLiked": liked,
    })


@require_POST
@ratelimit("vote", 30)
def comment_vote(request, pk):
    comment = get_object_or_404(Comment.objects.approved(), pk=pk, article__in=Article.objects.published())
    vote = request.POST.get("vote")
    if vote not in CommentVote.Vote.values:
        return JsonResponse({"success": False, "message": "Vote invalide."}, status=422)
    ident = _identity(request)
    existing = CommentVote.objects.filter(comment=comment, **ident).first()
    if existing and existing.vote == vote:
        existing.delete()
        user_vote = None
    elif existing:
        existing.vote = vote
        existing.save(update_fields=["vote", "updated_at"])
        user_vote = vote
    else:
        CommentVote.objects.create(comment=comment, vote=vote, **ident)
        user_vote = vote
    counts = comment.votes.aggregate(up=Count("pk", filter=Q(vote="up")), down=Count("pk", filter=Q(vote="down")))
    return JsonResponse({"success": True, "userVote": user_vote, **counts})


# --- Modération ---------------------------------------------------------------

class CommentListView(EditeurRequiredMixin, View):
    def get(self, request):
        statut = request.GET.get("statut", "en_attente")
        qs = Comment.objects.select_related("article", "user", "parent")
        if statut in Comment.Status.values:
            qs = qs.filter(statut=statut)
        if q := request.GET.get("q"):
            qs = qs.filter(
                Q(contenu__icontains=q) | Q(nom__icontains=q) | Q(email__icontains=q)
                | Q(user__name__icontains=q) | Q(article__titre__icontains=q)
            )
        counts = dict(Comment.objects.values_list("statut").annotate(n=Count("pk")))
        tabs = [(k, label, counts.get(k, 0)) for k, label in Comment.Status.choices] + [("tous", "Tous", sum(counts.values()))]
        page = Paginator(qs, 20).get_page(request.GET.get("page"))
        return render(request, "backoffice/engagement/comments.html", {"page_obj": page, "tabs": tabs, "current": statut})


class CommentModerateView(EditeurRequiredMixin, View):
    ACTIONS = {"approve": Comment.Status.APPROUVE, "reject": Comment.Status.REJETE, "spam": Comment.Status.SPAM}

    def post(self, request):
        action = request.POST.get("action")
        ids = request.POST.getlist("ids")
        qs = Comment.objects.filter(pk__in=ids)
        if action == "delete":
            n = qs.count()
            qs.delete()
            messages.success(request, f"{n} commentaire(s) supprimé(s).")
        elif action in self.ACTIONS:
            n = qs.update(statut=self.ACTIONS[action], modere_par=request.user, modere_le=timezone.now())
            messages.success(request, f"{n} commentaire(s) mis à jour.")
        else:
            messages.warning(request, "Aucune action sélectionnée.")
        return redirect(safe_next(request, "/admin/commentaires/"))
