from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.core.paginator import Paginator
from django.db.models import Count
from django.http import HttpResponseForbidden
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse

from common.markdown import render_markdown

from .forms import AnnouncementForm
from .models import Announcement, AnnouncementComment, AnnouncementRead, Notification


def _staff_only(user):
    return user.is_authenticated and user.is_staff


def announcement_list(request):
    """Every published global announcement."""
    announcements = (
        Announcement.objects.filter(published=True)
        .annotate(comments_count=Count('comments'))
        .order_by('-created_at', '-pk')
    )
    paginator = Paginator(announcements, 10)
    page = paginator.get_page(request.GET.get('page'))

    read_ids = set()
    if request.user.is_authenticated:
        read_ids = set(
            AnnouncementRead.objects.filter(user=request.user)
            .values_list('announcement_id', flat=True)
        )
    return render(request, 'announcements/announcement_list.html', {
        'page': page,
        'read_ids': read_ids,
    })


def announcement_detail(request, pk):
    """One announcement + its comment section. Marks it read on view."""
    announcement = get_object_or_404(
        Announcement.objects.annotate(comments_count=Count('comments')),
        pk=pk, published=True,
    )
    if request.user.is_authenticated:
        AnnouncementRead.objects.get_or_create(
            announcement=announcement, user=request.user,
        )
    comments = announcement.comments.select_related('user').all()
    return render(request, 'announcements/announcement_detail.html', {
        'announcement': announcement,
        'comments': comments,
        'can_comment': request.user.is_authenticated,
    })


@login_required
def announcement_create(request):
    if not _staff_only(request.user):
        return HttpResponseForbidden('Only staff can manage announcements.')
    if request.method == 'POST':
        form = AnnouncementForm(request.POST)
        if form.is_valid():
            form.instance.created_by = request.user
            form.save()
            messages.success(request, 'Announcement published.')
            return redirect('announcements:detail', pk=form.instance.pk)
    else:
        form = AnnouncementForm()
    return render(request, 'announcements/announcement_form.html', {
        'form': form,
        'editing': False,
    })


@login_required
def announcement_edit(request, pk):
    if not _staff_only(request.user):
        return HttpResponseForbidden('Only staff can manage announcements.')
    announcement = get_object_or_404(Announcement, pk=pk)
    if request.method == 'POST':
        form = AnnouncementForm(request.POST, instance=announcement)
        if form.is_valid():
            form.save()
            messages.success(request, 'Announcement updated.')
            return redirect('announcements:detail', pk=announcement.pk)
    else:
        form = AnnouncementForm(instance=announcement)
    return render(request, 'announcements/announcement_form.html', {
        'form': form,
        'announcement': announcement,
        'editing': True,
    })


@login_required
def announcement_delete(request, pk):
    if not _staff_only(request.user):
        return HttpResponseForbidden('Only staff can manage announcements.')
    announcement = get_object_or_404(Announcement, pk=pk)
    if request.method == 'POST':
        announcement.delete()
        messages.success(request, 'Announcement deleted.')
    return redirect('announcements:list')


@login_required
def announcement_add_comment(request, pk):
    announcement = get_object_or_404(Announcement, pk=pk)
    if request.method == 'POST':
        body = request.POST.get('body', '').strip()
        if body:
            AnnouncementComment.objects.create(
                announcement=announcement, user=request.user, body=body,
            )
            messages.success(request, 'Comment added.')
        else:
            messages.error(request, 'Comment cannot be empty.')
    return redirect('announcements:detail', pk=announcement.pk)


@login_required
def announcement_edit_comment(request, pk, comment_id):
    announcement = get_object_or_404(Announcement, pk=pk)
    comment = get_object_or_404(
        AnnouncementComment, pk=comment_id, announcement=announcement,
    )
    if comment.user_id != request.user.pk and not request.user.is_staff:
        return HttpResponseForbidden('You can only edit your own comments.')
    if request.method == 'POST':
        body = request.POST.get('body', '').strip()
        if body:
            comment.body = body
            comment.save(update_fields=['body', 'updated_at'])
            messages.success(request, 'Comment updated.')
        else:
            messages.error(request, 'Comment cannot be empty.')
    return redirect('announcements:detail', pk=announcement.pk)


@login_required
def announcement_delete_comment(request, pk, comment_id):
    announcement = get_object_or_404(Announcement, pk=pk)
    comment = get_object_or_404(
        AnnouncementComment, pk=comment_id, announcement=announcement,
    )
    if comment.user_id != request.user.pk and not request.user.is_staff:
        return HttpResponseForbidden('You can only delete your own comments.')
    if request.method == 'POST':
        comment.delete()
        messages.success(request, 'Comment deleted.')
    return redirect('announcements:detail', pk=announcement.pk)


@login_required
def personal_notifications(request):
    """The user's own personal announcement page."""
    notifications = Notification.objects.filter(user=request.user)
    paginator = Paginator(notifications, 20)
    page = paginator.get_page(request.GET.get('page'))
    return render(request, 'announcements/notifications.html', {
        'page': page,
    })


@login_required
def mark_all_notifications_read(request):
    if request.method == 'POST':
        Notification.objects.filter(user=request.user, read=False).update(read=True)
        messages.success(request, 'All notifications marked as read.')
    return redirect(request.POST.get('next') or reverse('announcements:personal'))
