from datetime import timedelta

from django.db.models import Q
from django.utils import timezone
from rest_framework.parsers import FormParser, JSONParser, MultiPartParser
from rest_framework import permissions, status
from rest_framework.response import Response
from rest_framework.views import APIView

from apps.accounts.models import User
from apps.support.models import SupportTicket, SupportTicketAttachment, SupportTicketMessage
from apps.support.services import apply_sla_and_escalation, notify_ticket_event
from apps.support.serializers import SupportTicketDetailSerializer, SupportTicketListSerializer


MAX_ATTACHMENT_SIZE_BYTES = 10 * 1024 * 1024


def _collect_attachments(request):
    files = list(request.FILES.getlist("attachments"))
    if not files and request.FILES.get("attachment"):
        files = [request.FILES.get("attachment")]
    return [f for f in files if f is not None]


def _validate_attachments(files):
    for file_obj in files:
        if getattr(file_obj, "size", 0) > MAX_ATTACHMENT_SIZE_BYTES:
            return f"Attachment {getattr(file_obj, 'name', 'file')} exceeds 10MB."
    return None


def _create_attachments(ticket, message, files, uploader):
    for file_obj in files:
        SupportTicketAttachment.objects.create(
            ticket=ticket,
            message=message,
            uploaded_by=uploader,
            file=file_obj,
            file_name=getattr(file_obj, "name", "attachment"),
            content_type=getattr(file_obj, "content_type", "") or "",
            file_size=int(getattr(file_obj, "size", 0) or 0),
        )


class AdminSupportTicketListView(APIView):
    permission_classes = [permissions.IsAdminUser]

    def get(self, request):
        page_raw = request.query_params.get("page", "1")
        page_size_raw = request.query_params.get("page_size", "20")
        status_filter = (request.query_params.get("status") or "").strip().lower()
        priority_filter = (request.query_params.get("priority") or "").strip().lower()
        category_filter = (request.query_params.get("category") or "").strip().lower()
        assigned_to_filter = (request.query_params.get("assigned_to") or "").strip()
        search_term = (request.query_params.get("q") or "").strip()

        try:
            page = max(1, int(page_raw))
        except Exception:
            page = 1

        try:
            page_size = max(1, min(100, int(page_size_raw)))
        except Exception:
            page_size = 20

        qs = SupportTicket.objects.filter(is_active=True).select_related("user", "assigned_to").order_by("-updated_at")

        if status_filter:
            qs = qs.filter(status=status_filter)
        if priority_filter:
            qs = qs.filter(priority=priority_filter)
        if category_filter:
            qs = qs.filter(category=category_filter)
        if assigned_to_filter:
            if assigned_to_filter == "unassigned":
                qs = qs.filter(assigned_to__isnull=True)
            else:
                qs = qs.filter(assigned_to_id=assigned_to_filter)
        if search_term:
            qs = qs.filter(
                Q(ticket_number__icontains=search_term)
                | Q(subject__icontains=search_term)
                | Q(user__mobile__icontains=search_term)
                | Q(user__email__icontains=search_term)
            )

        for ticket in qs[:150]:
            changed = apply_sla_and_escalation(ticket)
            if changed:
                ticket.save(update_fields=[*changed, "updated_at"])
                if ticket.escalation_level > 0:
                    notify_ticket_event(ticket=ticket, actor=request.user, event_type="escalated", include_admins=True)

        start = (page - 1) * page_size
        end = start + page_size
        page_qs = qs[start:end]

        data = SupportTicketListSerializer(page_qs, many=True).data
        return Response(
            {
                "results": data,
                "count": qs.count(),
                "page": page,
                "page_size": page_size,
            },
            status=status.HTTP_200_OK,
        )


class AdminSupportTicketDetailView(APIView):
    permission_classes = [permissions.IsAdminUser]

    def get(self, request, ticket_id):
        ticket = SupportTicket.objects.filter(id=ticket_id, is_active=True).select_related("user", "assigned_to").first()
        if not ticket:
            return Response({"detail": "Not found"}, status=status.HTTP_404_NOT_FOUND)

        changed = apply_sla_and_escalation(ticket)
        if changed:
            ticket.save(update_fields=[*changed, "updated_at"])
            if ticket.escalation_level > 0:
                notify_ticket_event(ticket=ticket, actor=request.user, event_type="escalated", include_admins=True)

        data = SupportTicketDetailSerializer(ticket, context={"request": request, "show_internal": True}).data
        return Response(data, status=status.HTTP_200_OK)


class AdminSupportTicketReplyView(APIView):
    permission_classes = [permissions.IsAdminUser]
    parser_classes = [JSONParser, MultiPartParser, FormParser]

    def post(self, request, ticket_id):
        ticket = SupportTicket.objects.filter(id=ticket_id, is_active=True).first()
        if not ticket:
            return Response({"detail": "Not found"}, status=status.HTTP_404_NOT_FOUND)

        message = (request.data.get("message") or "").strip()
        if not message:
            return Response({"detail": "message is required."}, status=status.HTTP_400_BAD_REQUEST)

        files = _collect_attachments(request)
        attachment_error = _validate_attachments(files)
        if attachment_error:
            return Response({"detail": attachment_error}, status=status.HTTP_400_BAD_REQUEST)

        is_internal = bool(request.data.get("is_internal", False))
        now = timezone.now()

        message_obj = SupportTicketMessage.objects.create(
            ticket=ticket,
            sender=request.user,
            message=message,
            is_internal=is_internal,
        )
        _create_attachments(ticket, message_obj, files, request.user)

        update_fields = ["updated_at"]

        if not is_internal:
            if ticket.status == "open":
                ticket.status = "in_progress"
                update_fields.append("status")
            if not ticket.first_response_at:
                ticket.first_response_at = now
                update_fields.append("first_response_at")
            ticket.last_admin_reply_at = now
            update_fields.append("last_admin_reply_at")

        changed = apply_sla_and_escalation(ticket, now=now)
        for field in changed:
            if field not in update_fields:
                update_fields.append(field)

        ticket.save(update_fields=update_fields)

        if not is_internal:
            notify_ticket_event(ticket=ticket, actor=request.user, event_type="admin_reply")
        if changed and ticket.escalation_level > 0:
            notify_ticket_event(ticket=ticket, actor=request.user, event_type="escalated", include_admins=True)

        data = SupportTicketDetailSerializer(ticket, context={"request": request, "show_internal": True}).data
        return Response({"success": True, "data": data}, status=status.HTTP_200_OK)


class AdminSupportTicketStatusUpdateView(APIView):
    permission_classes = [permissions.IsAdminUser]

    def post(self, request, ticket_id):
        ticket = SupportTicket.objects.filter(id=ticket_id, is_active=True).first()
        if not ticket:
            return Response({"detail": "Not found"}, status=status.HTTP_404_NOT_FOUND)

        next_status = (request.data.get("status") or "").strip().lower()
        valid_statuses = {s[0] for s in SupportTicket.STATUS_CHOICES}
        if next_status not in valid_statuses:
            return Response({"detail": "invalid status."}, status=status.HTTP_400_BAD_REQUEST)

        now = timezone.now()
        ticket.status = next_status

        if next_status == "resolved":
            ticket.resolved_at = now
            if not ticket.first_response_at:
                ticket.first_response_at = now
        elif next_status == "closed":
            ticket.closed_at = now
            if not ticket.first_response_at:
                ticket.first_response_at = now
        else:
            ticket.closed_at = None
            if next_status == "open":
                ticket.resolved_at = None

        ticket.save(update_fields=["status", "resolved_at", "closed_at", "first_response_at", "updated_at"])
        notify_ticket_event(ticket=ticket, actor=request.user, event_type="status_changed")

        data = SupportTicketDetailSerializer(ticket, context={"request": request, "show_internal": True}).data
        return Response({"success": True, "data": data}, status=status.HTTP_200_OK)


class AdminSupportTicketAssignView(APIView):
    permission_classes = [permissions.IsAdminUser]

    def post(self, request, ticket_id):
        ticket = SupportTicket.objects.filter(id=ticket_id, is_active=True).first()
        if not ticket:
            return Response({"detail": "Not found"}, status=status.HTTP_404_NOT_FOUND)

        assigned_to_id = request.data.get("assigned_to_id")
        if not assigned_to_id:
            ticket.assigned_to = None
            ticket.save(update_fields=["assigned_to", "updated_at"])
            notify_ticket_event(ticket=ticket, actor=request.user, event_type="assigned")
            data = SupportTicketDetailSerializer(ticket, context={"request": request, "show_internal": True}).data
            return Response({"success": True, "data": data}, status=status.HTTP_200_OK)

        assignee = User.objects.filter(id=assigned_to_id, is_active=True).first()
        if not assignee:
            return Response({"detail": "assignee not found."}, status=status.HTTP_400_BAD_REQUEST)

        ticket.assigned_to = assignee
        ticket.save(update_fields=["assigned_to", "updated_at"])
        notify_ticket_event(ticket=ticket, actor=request.user, event_type="assigned")

        data = SupportTicketDetailSerializer(ticket, context={"request": request, "show_internal": True}).data
        return Response({"success": True, "data": data}, status=status.HTTP_200_OK)


class AdminSupportTicketMetricsView(APIView):
    permission_classes = [permissions.IsAdminUser]

    def get(self, request):
        now = timezone.now()
        overdue_cutoff = now - timedelta(hours=24)
        qs = SupportTicket.objects.filter(is_active=True)

        first_response_samples = list(
            qs.filter(first_response_at__isnull=False)
            .values_list("created_at", "first_response_at")[:300]
        )
        avg_first_response_minutes = 0
        if first_response_samples:
            total_minutes = 0
            for created_at, first_response_at in first_response_samples:
                total_minutes += max(0, int((first_response_at - created_at).total_seconds() // 60))
            avg_first_response_minutes = round(total_minutes / len(first_response_samples), 2)

        return Response(
            {
                "open": qs.filter(status="open").count(),
                "in_progress": qs.filter(status="in_progress").count(),
                "resolved": qs.filter(status="resolved").count(),
                "closed": qs.filter(status="closed").count(),
                "unassigned_open": qs.filter(Q(status="open") | Q(status="in_progress"), assigned_to__isnull=True).count(),
                "overdue_open": qs.filter(Q(status="open") | Q(status="in_progress"), created_at__lte=overdue_cutoff, first_response_at__isnull=True).count(),
                "resolved_7d": qs.filter(status="resolved", resolved_at__gte=now - timedelta(days=7)).count(),
                "closed_30d": qs.filter(status="closed", closed_at__gte=now - timedelta(days=30)).count(),
                "avg_first_response_minutes": avg_first_response_minutes,
                "sla_breached": qs.filter(sla_breached_at__isnull=False, status__in=["open", "in_progress"]).count(),
                "escalated": qs.filter(escalation_level__gt=0, status__in=["open", "in_progress"]).count(),
                "near_due": qs.filter(
                    status__in=["open", "in_progress"],
                    resolution_due_at__isnull=False,
                    resolution_due_at__lte=now + timedelta(hours=4),
                    resolution_due_at__gte=now,
                ).count(),
            },
            status=status.HTTP_200_OK,
        )
