from django.conf import settings as dj_settings
from django.core.mail import EmailMessage, get_connection
from django.utils import timezone

from apps.core.background import run_in_background
from rest_framework import viewsets
from rest_framework.decorators import action
from rest_framework.parsers import JSONParser, MultiPartParser, FormParser
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response

from apps.core.permissions import HasMenuPermission, IsOwnerOrFullScope, user_has_full_scope

from .models import (
    MeetingRoom, Meeting, MeetingInvitation, MeetingMinutes, MeetingPresentation,
)
from .serializers import (
    MeetingRoomSerializer, MeetingSerializer, MeetingInvitationSerializer,
    MeetingMinutesSerializer, MeetingPresentationSerializer,
)


def _notify(user, *, title, message, link="", priority="medium", related_user=None):
    """Fire an in-app meeting notification, never break the request on failure."""
    if not user:
        return
    try:
        from apps.notifications.handler import NotificationHandler
        NotificationHandler.send(
            user=user,
            title=title,
            message=message,
            notification_type="meeting",
            priority=priority,
            icon="calendar",
            link=link,
            related_user=related_user,
            broadcast=True,
        )
    except Exception:
        pass


class MeetingRoomViewSet(viewsets.ModelViewSet):
    """CRUD for bookable meeting rooms."""
    queryset = MeetingRoom.objects.all()
    serializer_class = MeetingRoomSerializer
    permission_classes = [IsAuthenticated]

    def get_queryset(self):
        qs = super().get_queryset()
        active = self.request.query_params.get("is_active")
        if active is not None:
            qs = qs.filter(is_active=active.lower() in ("1", "true", "yes"))
        return qs


class MeetingViewSet(viewsets.ModelViewSet):
    """CRUD for meetings / room bookings."""
    rbac_domain = "meetings"
    queryset = Meeting.objects.select_related("room", "event", "project", "organizer").prefetch_related("attendees")
    serializer_class = MeetingSerializer
    permission_classes = [IsAuthenticated, HasMenuPermission, IsOwnerOrFullScope]
    parser_classes = [JSONParser, MultiPartParser, FormParser]

    def get_queryset(self):
        qs = super().get_queryset()
        user = self.request.user
        # Without full meetings scope, see only meetings you organize or attend.
        if not user_has_full_scope(user, "meetings"):
            from django.db.models import Q
            qs = qs.filter(Q(organizer=user) | Q(attendees=user)).distinct()
        params = self.request.query_params
        for field in ("project", "event", "room", "status"):
            val = params.get(field)
            if val:
                qs = qs.filter(**{field: val})
        return qs

    def perform_create(self, serializer):
        user = self.request.user if self.request.user.is_authenticated else None
        meeting = serializer.save(organizer=user)
        # Notify attendees (M2M is set during save) that they're on a new meeting.
        actor_name = (user.get_full_name() or user.email) if user else "Someone"
        when = timezone.localtime(meeting.start_time).strftime("%d %b %Y %H:%M")
        for attendee in meeting.attendees.all():
            if user and attendee.id == user.id:
                continue
            _notify(
                attendee,
                title="New meeting",
                message=f"{actor_name} scheduled “{meeting.title}” on {when}.",
                link=f"/meetings/{meeting.id}",
                related_user=user,
            )

    # ---- Invitations ----
    @action(detail=True, methods=["get", "post"], url_path="invitations")
    def invitations(self, request, pk=None):
        if request.method == "GET":
            qs = MeetingInvitation.objects.filter(meeting_id=pk).select_related("contact").order_by("-created_at")
            return Response(MeetingInvitationSerializer(qs, many=True).data)
        ser = MeetingInvitationSerializer(data={**request.data, "meeting": pk})
        ser.is_valid(raise_exception=True)
        user = request.user if request.user.is_authenticated else None
        inv = ser.save(invited_by=user)
        # Notify the invited internal user (external invitees get email instead).
        if inv.user_id and (not user or inv.user_id != user.id):
            meeting = inv.meeting
            actor_name = (user.get_full_name() or user.email) if user else "Someone"
            when = timezone.localtime(meeting.start_time).strftime("%d %b %Y %H:%M")
            _notify(
                inv.user,
                title="Meeting invitation",
                message=f"{actor_name} invited you to “{meeting.title}” on {when}.",
                link=f"/meetings/{meeting.id}",
                related_user=user,
            )
        return Response(ser.data, status=201)

    @action(detail=True, methods=["post"], url_path="send-invites")
    def send_invites(self, request, pk=None):
        """Email the meeting invitation to pending (or specified) invitees."""
        meeting = self.get_object()
        ids = request.data.get("invitation_ids")
        qs = MeetingInvitation.objects.filter(meeting_id=pk)
        if ids:
            qs = qs.filter(id__in=ids)
        else:
            qs = qs.filter(sent_at__isnull=True)

        when = timezone.localtime(meeting.start_time).strftime("%A, %d %b %Y %H:%M")
        room = meeting.room.name if meeting.room_id else "TBA"
        from_email = getattr(dj_settings, "DEFAULT_FROM_EMAIL", "no-reply@karajohub.local")
        subject = f"Meeting Invitation: {meeting.title}"
        messages = []
        invitations = list(qs)
        for inv in invitations:
            body = (
                f"Hi {inv.name or 'there'},\n\n"
                f"You are invited to: {meeting.title}\n"
                f"When: {when}\n"
                f"Where: {room}\n\n"
                f"{meeting.description or ''}\n\n"
                f"Please reply to confirm your attendance."
            )
            messages.append(EmailMessage(subject, body, from_email, [inv.email]))

        # Mark queued now (previous behavior set sent_at even on silent SMTP
        # failure), then push the SMTP round-trips off the request thread on
        # one shared connection instead of one per email.
        now = timezone.now()
        MeetingInvitation.objects.filter(id__in=[i.id for i in invitations]).update(sent_at=now)

        def _send_batch(batch):
            connection = get_connection(fail_silently=True)
            connection.send_messages(batch)

        if messages:
            run_in_background(_send_batch, messages)
        return Response({"sent": len(messages)})

    # ---- Minutes ----
    @action(detail=True, methods=["get", "put", "patch"], url_path="minutes")
    def minutes(self, request, pk=None):
        meeting = self.get_object()
        obj = MeetingMinutes.objects.filter(meeting_id=pk).first()
        if request.method == "GET":
            if not obj:
                return Response({"meeting": str(meeting.id), "body": "", "action_items": []})
            return Response(MeetingMinutesSerializer(obj).data)
        # PUT/PATCH upsert
        user = request.user if request.user.is_authenticated else None
        ser = MeetingMinutesSerializer(obj, data={**request.data, "meeting": pk}, partial=True)
        ser.is_valid(raise_exception=True)
        ser.save(recorded_by=user)
        return Response(ser.data, status=200)

    # ---- Presentations ----
    @action(detail=True, methods=["get", "post"], url_path="presentations")
    def presentations(self, request, pk=None):
        if request.method == "GET":
            qs = MeetingPresentation.objects.filter(meeting_id=pk).select_related("presenter").order_by("-created_at")
            return Response(MeetingPresentationSerializer(qs, many=True, context={"request": request}).data)
        ser = MeetingPresentationSerializer(data={**request.data, "meeting": pk}, context={"request": request})
        ser.is_valid(raise_exception=True)
        user = request.user if request.user.is_authenticated else None
        ser.save(uploaded_by=user)
        return Response(ser.data, status=201)


class MeetingInvitationViewSet(viewsets.ModelViewSet):
    """Update (RSVP) / delete individual invitations."""
    rbac_domain = "meetings"
    queryset = MeetingInvitation.objects.select_related("meeting", "contact")
    serializer_class = MeetingInvitationSerializer
    permission_classes = [IsAuthenticated, HasMenuPermission]

    def perform_update(self, serializer):
        old_status = serializer.instance.status
        new_status = serializer.validated_data.get("status", old_status)
        extra = {}
        responded = new_status != old_status and new_status in ("accepted", "declined", "tentative")
        if responded:
            extra["responded_at"] = timezone.now()
        inv = serializer.save(**extra)
        # Notify the meeting organizer of the RSVP.
        if responded and inv.meeting and inv.meeting.organizer_id:
            _notify(
                inv.meeting.organizer,
                title="Meeting RSVP",
                message=f"{inv.name or inv.email} {new_status} “{inv.meeting.title}”.",
                link=f"/meetings/{inv.meeting_id}",
                priority="low",
            )


class MeetingPresentationViewSet(viewsets.ModelViewSet):
    """Update / delete individual presentations."""
    rbac_domain = "meetings"
    queryset = MeetingPresentation.objects.select_related("meeting", "presenter")
    serializer_class = MeetingPresentationSerializer
    permission_classes = [IsAuthenticated, HasMenuPermission]
    parser_classes = [JSONParser, MultiPartParser, FormParser]
