from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.pagination import PageNumberPagination
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated, IsAdminUser
from drf_spectacular.utils import extend_schema, OpenApiParameter
from drf_spectacular.types import OpenApiTypes

from .models import Notification, NotificationTemplate, NotificationPreference, FCMDevice, EmailLog
from .serializers import (
    NotificationSerializer,
    NotificationCreateSerializer,
    NotificationTemplateSerializer,
    NotificationPreferenceSerializer,
    FCMDeviceSerializer,
    EmailLogSerializer
)
from .handler import NotificationHandler


class NotificationPagination(PageNumberPagination):
    page_size = 50
    page_size_query_param = "page_size"
    max_page_size = 200


class NotificationViewSet(viewsets.ModelViewSet):
    """
    ViewSet for managing user notifications.

    Supports:
    - Listing notifications (only the authenticated user's)
    - Marking single/multiple as read
    - Deleting notifications
    - Real-time WebSocket delivery
    """
    permission_classes = [IsAuthenticated]
    pagination_class = NotificationPagination

    def get_serializer_class(self):
        if self.action == 'create':
            return NotificationCreateSerializer
        return NotificationSerializer

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

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

    @extend_schema(
        description="Mark all notifications as read"
    )
    @action(detail=False, methods=['post'])
    def mark_all_read(self, request):
        count = NotificationHandler.mark_all_as_read(request.user)
        return Response({'status': 'marked all as read', 'count': count})

    @extend_schema(
        description="Get unread count"
    )
    @action(detail=False, methods=['get'])
    def unread_count(self, request):
        count = NotificationHandler.get_unread_count(request.user)
        return Response({'unread_count': count})

    @extend_schema(
        description="Get recent unread notifications"
    )
    @action(detail=False, methods=['get'])
    def unread(self, request):
        limit = int(request.query_params.get('limit', 10))
        notifications = NotificationHandler.get_unread(request.user, limit=limit)
        serializer = NotificationSerializer(notifications, many=True)
        return Response(serializer.data)

    @extend_schema(
        description="Delete (soft) a specific notification"
    )
    @action(detail=True, methods=['post'])
    def delete(self, request, pk=None):
        notification = self.get_object()
        notification.mark_as_deleted()
        return Response({'status': 'deleted'})

    @extend_schema(
        description="Delete all read notifications"
    )
    @action(detail=False, methods=['post'])
    def delete_read(self, request):
        deleted, _ = Notification.objects.filter(
            user=request.user,
            is_read=True,
            is_deleted=False
        ).update(is_deleted=True)
        return Response({'status': 'deleted', 'count': deleted})


class NotificationTemplateViewSet(viewsets.ModelViewSet):
    """
    ViewSet for managing notification templates.
    Admin only endpoint.
    """
    queryset = NotificationTemplate.objects.all()
    serializer_class = NotificationTemplateSerializer

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


class FCMDeviceViewSet(viewsets.ModelViewSet):
    """Register/list/remove the current user's FCM push tokens.

    Browsers POST the token they get from the Firebase JS SDK. Re-registering an
    existing token (e.g. after a token refresh on another account) re-points it
    at the current user and reactivates it.
    """
    permission_classes = [IsAuthenticated]
    serializer_class = FCMDeviceSerializer

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

    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        token = serializer.validated_data['token']
        platform = serializer.validated_data.get('platform', 'web')

        device, _ = FCMDevice.objects.update_or_create(
            token=token,
            defaults={
                'user': request.user,
                'platform': platform,
                'is_active': True,
            },
        )
        out = self.get_serializer(device)
        return Response(out.data, status=status.HTTP_201_CREATED)

    @extend_schema(description="Unregister a device token (used on logout)")
    @action(detail=False, methods=['post'])
    def unregister(self, request):
        token = request.data.get('token')
        if not token:
            return Response(
                {'error': 'token is required'},
                status=status.HTTP_400_BAD_REQUEST
            )
        deleted, _ = FCMDevice.objects.filter(
            user=request.user, token=token
        ).delete()
        return Response({'status': 'unregistered', 'deleted': deleted})


class EmailLogPagination(PageNumberPagination):
    page_size = 50
    page_size_query_param = "page_size"
    max_page_size = 200


class EmailLogViewSet(viewsets.ReadOnlyModelViewSet):
    """Read-only audit log of notification emails sent. Admin only — exposes
    who/when/status, never the rendered email body/content.

    Scoped to recipients inside the caller's active organization. `is_staff` is
    a platform flag, not a tenant one, so an unscoped queryset let a staff
    account attached to one tenant read every other tenant's recipient
    addresses, subjects and delivery errors (SEC-13).
    """
    queryset = EmailLog.objects.select_related('user')
    serializer_class = EmailLogSerializer
    permission_classes = [IsAdminUser]
    pagination_class = EmailLogPagination

    @extend_schema(
        parameters=[
            OpenApiParameter(name='status', type=OpenApiTypes.STR, description='Filter by status (sent/failed)'),
            OpenApiParameter(name='notification_type', type=OpenApiTypes.STR, description='Filter by notification type'),
        ]
    )
    def get_queryset(self):
        from apps.core.tenancy import get_active_organization

        qs = super().get_queryset()
        if not self.request.user.is_superuser:
            # Rows with no recipient user have no derivable tenant, so they are
            # withheld from anyone but a platform superuser.
            qs = qs.filter(user__organizations=get_active_organization(self.request))
        status_filter = self.request.query_params.get('status')
        type_filter = self.request.query_params.get('notification_type')
        if status_filter:
            qs = qs.filter(status=status_filter)
        if type_filter:
            qs = qs.filter(notification_type=type_filter)
        return qs.distinct()


class NotificationPreferenceViewSet(viewsets.ModelViewSet):
    """
    ViewSet for managing user notification preferences.
    Each user can only manage their own preferences.
    """
    permission_classes = [IsAuthenticated]
    serializer_class = NotificationPreferenceSerializer

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

    def perform_create(self, serializer):
        serializer.save(user=self.request.user)

    @extend_schema(
        description="Get, or update, notification preferences for the current user"
    )
    @action(detail=False, methods=['get', 'post'])
    def me(self, request):
        try:
            prefs = NotificationPreference.objects.get(user=request.user)
        except NotificationPreference.DoesNotExist:
            prefs = NotificationPreference.objects.create(user=request.user)

        if request.method == 'POST':
            serializer = NotificationPreferenceSerializer(
                prefs, data=request.data, partial=True
            )
            serializer.is_valid(raise_exception=True)
            serializer.save()
            return Response(serializer.data)

        serializer = NotificationPreferenceSerializer(prefs)
        return Response(serializer.data)

    @extend_schema(
        description="Update notification type preference"
    )
    @action(detail=False, methods=['post'])
    def update_type(self, request):
        notification_type = request.data.get('type')
        enabled = request.data.get('enabled', True)

        if not notification_type:
            return Response(
                {'error': 'type is required'},
                status=status.HTTP_400_BAD_REQUEST
            )

        try:
            prefs = NotificationPreference.objects.get(user=request.user)
        except NotificationPreference.DoesNotExist:
            prefs = NotificationPreference.objects.create(user=request.user)

        prefs.set_type_enabled(notification_type, enabled)
        serializer = NotificationPreferenceSerializer(prefs)
        return Response(serializer.data)
