from rest_framework import viewsets
from rest_framework.decorators import action
from rest_framework.response import Response
from drf_spectacular.utils import extend_schema, OpenApiParameter
from drf_spectacular.types import OpenApiTypes

from .models import User, Tag, Attachment, Comment, Notification, AuditLog, AppConfig
from .serializers import (
    UserSerializer, TagSerializer, AttachmentSerializer,
    CommentSerializer, NotificationSerializer, AuditLogSerializer, AppConfigSerializer
)


class UserViewSet(viewsets.ModelViewSet):
    """ViewSet for User management. Staff: full access. Non-staff: read-only directory + edit self."""
    queryset = User.objects.prefetch_related("user_roles__role__permissions")
    serializer_class = UserSerializer

    def get_permissions(self):
        from rest_framework.permissions import IsAuthenticated, IsAdminUser
        if self.action in ("create", "destroy"):
            return [IsAdminUser()]
        return [IsAuthenticated()]

    def update(self, request, *args, **kwargs):
        instance = self.get_object()
        if instance.pk != request.user.pk and not (request.user.is_staff or request.user.is_superuser):
            return Response({"error": "may only update own profile"}, status=403)
        return super().update(request, *args, **kwargs)

    def partial_update(self, request, *args, **kwargs):
        instance = self.get_object()
        if instance.pk != request.user.pk and not (request.user.is_staff or request.user.is_superuser):
            return Response({"error": "may only update own profile"}, status=403)
        return super().partial_update(request, *args, **kwargs)

    @extend_schema(
        summary="Get current user",
        description="Returns the currently authenticated user's details",
        responses={200: UserSerializer}
    )
    @action(detail=False, methods=["get"])
    def me(self, request):
        """Get current authenticated user."""
        serializer = self.get_serializer(request.user)
        return Response(serializer.data)


class TagViewSet(viewsets.ModelViewSet):
    """
    ViewSet for Tag management.

    retrieve:
    Get a specific tag by ID.

    list:
    List all tags with optional filtering by name.

    create:
    Create a new tag.

    update:
    Update an existing tag.

    destroy:
    Delete a tag.
    """
    queryset = Tag.objects.all()
    serializer_class = TagSerializer

    @extend_schema(
        parameters=[
            OpenApiParameter(
                name="name",
                type=OpenApiTypes.STR,
                description="Filter tags by name (partial match)"
            ),
        ]
    )
    def list(self, request, *args, **kwargs):
        return super().list(request, *args, **kwargs)


class AttachmentViewSet(viewsets.ModelViewSet):
    """
    ViewSet for Attachment management.

    retrieve:
    Get a specific attachment by ID.

    list:
    List all attachments.

    create:
    Upload a new attachment.

    destroy:
    Delete an attachment.
    """
    queryset = Attachment.objects.all()
    serializer_class = AttachmentSerializer


class CommentViewSet(viewsets.ModelViewSet):
    """
    ViewSet for Comment management.

    retrieve:
    Get a specific comment by ID.

    list:
    List all comments.

    create:
    Create a new comment.

    update:
    Update an existing comment.

    destroy:
    Delete a comment.
    """
    queryset = Comment.objects.select_related("author")
    serializer_class = CommentSerializer


class NotificationViewSet(viewsets.ModelViewSet):
    """
    ViewSet for Notification management.

    retrieve:
    Get a specific notification by ID.

    list:
    List all notifications for the current user.

    create:
    Create a new notification.

    update:
    Update a notification (mark as read).

    destroy:
    Delete a notification.
    """
    serializer_class = NotificationSerializer

    def get_queryset(self):
        return Notification.objects.filter(user=self.request.user)

    @extend_schema(
        summary="Mark notification as read",
        description="Mark a specific notification as read"
    )
    @action(detail=True, methods=["post"])
    def mark_read(self, request, pk=None):
        """Mark notification as read."""
        notification = self.get_object()
        notification.is_read = True
        notification.save()
        return Response({"status": "marked as read"})


class AuditLogViewSet(viewsets.ReadOnlyModelViewSet):
    """ViewSet for AuditLog (read-only). Staff/superuser only — audit data is sensitive."""
    queryset = AuditLog.objects.select_related("user")
    serializer_class = AuditLogSerializer

    def get_permissions(self):
        from rest_framework.permissions import IsAdminUser
        return [IsAdminUser()]

    @extend_schema(
        parameters=[
            OpenApiParameter(
                name="user",
                type=OpenApiTypes.UUID,
                description="Filter by user ID"
            ),
            OpenApiParameter(
                name="action",
                type=OpenApiTypes.STR,
                description="Filter by action type (e.g., 'create', 'update', 'delete')"
            ),
            OpenApiParameter(
                name="model_name",
                type=OpenApiTypes.STR,
                description="Filter by model name"
            ),
        ]
    )
    def list(self, request, *args, **kwargs):
        return super().list(request, *args, **kwargs)


class AppConfigViewSet(viewsets.ViewSet):
    """Singleton system config (AI + email). Superuser/staff only — holds secrets."""

    def get_permissions(self):
        from rest_framework.permissions import IsAdminUser
        return [IsAdminUser()]

    def _get_singleton(self):
        obj = AppConfig.objects.first()
        if obj is None:
            obj = AppConfig.objects.create()
        return obj

    @action(detail=False, methods=["get", "patch", "put"], url_path="current")
    def current(self, request):
        obj = self._get_singleton()
        if request.method in ("PATCH", "PUT"):
            serializer = AppConfigSerializer(obj, data=request.data, partial=request.method == "PATCH")
            serializer.is_valid(raise_exception=True)
            serializer.save()
            return Response(serializer.data)
        return Response(AppConfigSerializer(obj).data)