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.support.models import SupportTicket, SupportTicketAttachment, SupportTicketMessage
from apps.support.services import apply_sla_and_escalation, initialize_ticket_sla, 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 UserSupportTicketListCreateView(APIView):
    permission_classes = [permissions.IsAuthenticated]
    parser_classes = [JSONParser, MultiPartParser, FormParser]

    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()

        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(user=request.user, is_active=True)
            .select_related("user", "assigned_to")
            .order_by("-updated_at")
        )

        if status_filter:
            qs = qs.filter(status=status_filter)

        for ticket in qs[:100]:
            changed = apply_sla_and_escalation(ticket)
            if changed:
                ticket.save(update_fields=[*changed, "updated_at"])
            if changed and 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,
        )

    def post(self, request):
        subject = (request.data.get("subject") or "").strip()
        description = (request.data.get("description") or "").strip()
        category = (request.data.get("category") or "other").strip().lower()
        priority = (request.data.get("priority") or "medium").strip().lower()

        valid_categories = {c[0] for c in SupportTicket.CATEGORY_CHOICES}
        valid_priorities = {p[0] for p in SupportTicket.PRIORITY_CHOICES}

        if not subject:
            return Response({"detail": "subject is required."}, status=status.HTTP_400_BAD_REQUEST)
        if not description:
            return Response({"detail": "description is required."}, status=status.HTTP_400_BAD_REQUEST)
        if category not in valid_categories:
            return Response({"detail": "invalid category."}, status=status.HTTP_400_BAD_REQUEST)
        if priority not in valid_priorities:
            return Response({"detail": "invalid priority."}, 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)

        now = timezone.now()
        ticket = SupportTicket.objects.create(
            user=request.user,
            subject=subject,
            description=description,
            category=category,
            priority=priority,
            status="open",
            last_user_reply_at=now,
        )
        initialize_ticket_sla(ticket, now=now)
        ticket.save(update_fields=["first_response_due_at", "resolution_due_at", "updated_at"])

        message_obj = SupportTicketMessage.objects.create(
            ticket=ticket,
            sender=request.user,
            message=description,
            is_internal=False,
        )
        _create_attachments(ticket, message_obj, files, request.user)

        notify_ticket_event(ticket=ticket, actor=request.user, event_type="created", include_admins=True)

        data = SupportTicketDetailSerializer(ticket, context={"request": request, "show_internal": False}).data
        return Response({"success": True, "data": data}, status=status.HTTP_201_CREATED)


class UserSupportTicketDetailView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def get(self, request, ticket_id):
        ticket = (
            SupportTicket.objects.filter(id=ticket_id, user=request.user, 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": False}).data
        return Response(data, status=status.HTTP_200_OK)


class UserSupportTicketReplyView(APIView):
    permission_classes = [permissions.IsAuthenticated]
    parser_classes = [JSONParser, MultiPartParser, FormParser]

    def post(self, request, ticket_id):
        ticket = (
            SupportTicket.objects.filter(id=ticket_id, user=request.user, is_active=True)
            .select_related("user", "assigned_to")
            .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)

        now = timezone.now()
        reopened = False
        if ticket.status in {"resolved", "closed"}:
            ticket.status = "in_progress"
            ticket.closed_at = None
            ticket.resolved_at = None
            ticket.reopened_count = int(ticket.reopened_count or 0) + 1
            reopened = True

        message_obj = SupportTicketMessage.objects.create(
            ticket=ticket,
            sender=request.user,
            message=message,
            is_internal=False,
        )
        _create_attachments(ticket, message_obj, files, request.user)
        ticket.last_user_reply_at = now
        changed = apply_sla_and_escalation(ticket, now=now)
        ticket.save(
            update_fields=[
                "status",
                "closed_at",
                "resolved_at",
                "reopened_count",
                "last_user_reply_at",
                "sla_breached_at",
                "escalated_at",
                "escalation_level",
                "updated_at",
            ]
        )
        if changed and ticket.escalation_level > 0:
            notify_ticket_event(ticket=ticket, actor=request.user, event_type="escalated", include_admins=True)

        notify_ticket_event(ticket=ticket, actor=request.user, event_type="user_reply", include_admins=True)

        payload = SupportTicketDetailSerializer(ticket, context={"request": request, "show_internal": False}).data
        return Response({"success": True, "reopened": reopened, "data": payload}, status=status.HTTP_200_OK)


class UserSupportTicketCloseView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def post(self, request, ticket_id):
        ticket = SupportTicket.objects.filter(id=ticket_id, user=request.user, is_active=True).first()
        if not ticket:
            return Response({"detail": "Not found"}, status=status.HTTP_404_NOT_FOUND)

        if ticket.status != "closed":
            ticket.status = "closed"
            ticket.closed_at = timezone.now()
            ticket.save(update_fields=["status", "closed_at", "updated_at"])
            notify_ticket_event(ticket=ticket, actor=request.user, event_type="status_changed")

        return Response({"success": True}, status=status.HTTP_200_OK)


class UserSupportTicketMetricsView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def get(self, request):
        qs = SupportTicket.objects.filter(user=request.user, is_active=True)
        now = timezone.now()
        return Response(
            {
                "total": qs.count(),
                "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(),
                "high_priority_open": qs.filter(Q(status="open") | Q(status="in_progress"), priority__in=["high", "urgent"]).count(),
                "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,
        )
