"""
ViewSets and Views for observability API endpoints.

Provides CRUD operations for PipelineError, read-only access for
Anomaly, AlertCorrelationRule, CleanupJobLog, and aggregated views
for DataRetentionStats.
"""

from django.db import connection
from django.db.models import Count, Max, Min
from django.db.models.functions import Coalesce
from django.utils import timezone
from django_filters.rest_framework import DjangoFilterBackend
from drf_spectacular.utils import extend_schema, extend_schema_view
from rest_framework import status, viewsets
from rest_framework.decorators import action
from rest_framework.filters import OrderingFilter, SearchFilter
from rest_framework.permissions import IsAdminUser
from rest_framework.response import Response
from rest_framework.views import APIView

from apps.core.models import (
    AlertCorrelationRule,
    Anomaly,
    CleanupJobLog,
    PipelineError,
)

from .observability_serializers import (
    AlertCorrelationRuleSerializer,
    AnomalySerializer,
    CleanupJobLogSerializer,
    DataRetentionStatsSerializer,
    PipelineErrorResolveSerializer,
    PipelineErrorSerializer,
)


@extend_schema_view(
    list=extend_schema(
        summary="List pipeline errors",
        description="Get all pipeline errors, optionally filtered by severity or resolved status.",
        tags=["Admin - Observability"],
    ),
    retrieve=extend_schema(
        summary="Get pipeline error",
        description="Get details of a specific pipeline error.",
        tags=["Admin - Observability"],
    ),
    create=extend_schema(
        summary="Create pipeline error",
        description="Create a new pipeline error.",
        tags=["Admin - Observability"],
    ),
    update=extend_schema(
        summary="Update pipeline error",
        description="Update an existing pipeline error.",
        tags=["Admin - Observability"],
    ),
    partial_update=extend_schema(
        summary="Partial update pipeline error",
        description="Partially update an existing pipeline error.",
        tags=["Admin - Observability"],
    ),
    destroy=extend_schema(
        summary="Delete pipeline error",
        description="Delete a pipeline error.",
        tags=["Admin - Observability"],
    ),
)
class PipelineErrorViewSet(viewsets.ModelViewSet):
    """
    ViewSet for managing pipeline errors.

    Provides CRUD operations plus a resolve action for marking errors as resolved.
    """

    queryset = PipelineError.objects.select_related("resolved_by").all()
    serializer_class = PipelineErrorSerializer
    permission_classes = [IsAdminUser]
    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    filterset_fields = ["severity", "function_name", "resolved", "error_code"]
    search_fields = ["error_message", "function_name", "error_code"]
    ordering_fields = ["occurred_at", "severity"]
    ordering = ["-occurred_at"]

    def get_queryset(self):
        queryset = super().get_queryset()

        # Date range filtering
        start_date = self.request.query_params.get("start_date")
        end_date = self.request.query_params.get("end_date")

        if start_date:
            queryset = queryset.filter(occurred_at__gte=start_date)
        if end_date:
            queryset = queryset.filter(occurred_at__lte=end_date)

        return queryset

    @extend_schema(
        summary="Resolve pipeline error",
        description="Mark a pipeline error as resolved.",
        request=PipelineErrorResolveSerializer,
        responses={200: PipelineErrorSerializer},
        tags=["Admin - Observability"],
    )
    @action(detail=True, methods=["post"])
    def resolve(self, request, pk=None):
        """Mark a pipeline error as resolved."""
        error = self.get_object()

        if error.resolved:
            return Response(
                {"detail": "Error is already resolved"},
                status=status.HTTP_400_BAD_REQUEST,
            )

        serializer = PipelineErrorResolveSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        error.resolved = True
        error.resolved_at = timezone.now()
        error.resolved_by = request.user
        error.resolution_notes = serializer.validated_data.get("resolution_notes", "")
        error.save()

        return Response(PipelineErrorSerializer(error).data)


@extend_schema_view(
    list=extend_schema(
        summary="List recent anomalies",
        description="Get recent anomalies with optional filtering by severity or type.",
        tags=["Admin - Observability"],
    ),
    retrieve=extend_schema(
        summary="Get anomaly details",
        description="Get details of a specific anomaly.",
        tags=["Admin - Observability"],
    ),
)
class AnomalyViewSet(viewsets.ReadOnlyModelViewSet):
    """
    ViewSet for viewing detected anomalies.

    Provides read-only access to anomaly data with filtering options.
    This serves as the recent_anomalies_view endpoint.
    """

    queryset = Anomaly.objects.select_related("alert").all()
    serializer_class = AnomalySerializer
    permission_classes = [IsAdminUser]
    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    filterset_fields = ["severity", "anomaly_type", "metric_category", "alert_created"]
    search_fields = ["metric_name", "metric_category"]
    ordering_fields = ["detected_at", "severity", "deviation_score"]
    ordering = ["-detected_at"]

    def get_queryset(self):
        queryset = super().get_queryset()

        # Date range filtering
        start_date = self.request.query_params.get("start_date")
        end_date = self.request.query_params.get("end_date")

        if start_date:
            queryset = queryset.filter(detected_at__gte=start_date)
        if end_date:
            queryset = queryset.filter(detected_at__lte=end_date)

        return queryset


@extend_schema_view(
    list=extend_schema(
        summary="List alert correlations",
        description="Get all alert correlation rules with optional filtering.",
        tags=["Admin - Observability"],
    ),
    retrieve=extend_schema(
        summary="Get alert correlation rule",
        description="Get details of a specific alert correlation rule.",
        tags=["Admin - Observability"],
    ),
    create=extend_schema(
        summary="Create alert correlation rule",
        description="Create a new alert correlation rule.",
        tags=["Admin - Observability"],
    ),
    update=extend_schema(
        summary="Update alert correlation rule",
        description="Update an existing alert correlation rule.",
        tags=["Admin - Observability"],
    ),
    partial_update=extend_schema(
        summary="Partial update alert correlation rule",
        description="Partially update an existing alert correlation rule.",
        tags=["Admin - Observability"],
    ),
    destroy=extend_schema(
        summary="Delete alert correlation rule",
        description="Delete an alert correlation rule.",
        tags=["Admin - Observability"],
    ),
)
class AlertCorrelationViewSet(viewsets.ModelViewSet):
    """
    ViewSet for managing alert correlation rules.

    Provides CRUD operations for configuring how alerts are correlated.
    This serves as the alert_correlations_view endpoint.
    """

    queryset = AlertCorrelationRule.objects.all()
    serializer_class = AlertCorrelationRuleSerializer
    permission_classes = [IsAdminUser]
    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    filterset_fields = ["is_active", "incident_severity"]
    search_fields = ["rule_name", "rule_description"]
    ordering_fields = ["rule_name", "created_at"]
    ordering = ["rule_name"]


@extend_schema_view(
    list=extend_schema(
        summary="List cleanup job logs",
        description="Get all cleanup job logs with optional filtering by status.",
        tags=["Admin - Observability"],
    ),
    retrieve=extend_schema(
        summary="Get cleanup job log",
        description="Get details of a specific cleanup job log entry.",
        tags=["Admin - Observability"],
    ),
)
class CleanupJobLogViewSet(viewsets.ReadOnlyModelViewSet):
    """
    ViewSet for viewing cleanup job logs.

    Provides read-only access to cleanup job execution history.
    """

    queryset = CleanupJobLog.objects.all()
    serializer_class = CleanupJobLogSerializer
    permission_classes = [IsAdminUser]
    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    filterset_fields = ["status", "job_name"]
    search_fields = ["job_name", "error_message"]
    ordering_fields = ["executed_at", "duration_ms"]
    ordering = ["-executed_at"]

    def get_queryset(self):
        queryset = super().get_queryset()

        # Date range filtering
        start_date = self.request.query_params.get("start_date")
        end_date = self.request.query_params.get("end_date")

        if start_date:
            queryset = queryset.filter(executed_at__gte=start_date)
        if end_date:
            queryset = queryset.filter(executed_at__lte=end_date)

        return queryset


@extend_schema(
    summary="Get data retention stats",
    description="Get aggregated data retention statistics for monitoring database growth.",
    tags=["Admin - Observability"],
    responses={200: DataRetentionStatsSerializer(many=True)},
)
class DataRetentionStatsView(APIView):
    """
    API view for data retention statistics.

    Returns aggregated statistics about table sizes, record counts,
    and data age for monitoring data retention and growth.
    """

    permission_classes = [IsAdminUser]

    def get(self, request):
        """Get data retention statistics for key tables."""
        from datetime import timedelta

        from django.apps import apps

        now = timezone.now()
        seven_days_ago = now - timedelta(days=7)
        thirty_days_ago = now - timedelta(days=30)

        # Tables to report on
        tables_to_check = [
            ("core", "pipelineerror", "occurred_at"),
            ("core", "applicationerror", "created_at"),
            ("core", "systemalert", "created_at"),
            ("core", "requestmetadata", "created_at"),
            ("core", "anomaly", "detected_at"),
            ("core", "cleanupjoblog", "executed_at"),
            ("moderation", "editsubmission", "created_at"),
            ("moderation", "moderationauditlog", "created_at"),
            ("notifications", "notificationlog", "created_at"),
        ]

        stats = []
        for app_label, model_name, date_field in tables_to_check:
            try:
                model = apps.get_model(app_label, model_name)
                filter_kwargs_7d = {f"{date_field}__gte": seven_days_ago}
                filter_kwargs_30d = {f"{date_field}__gte": thirty_days_ago}

                # Get record counts and date ranges
                qs = model.objects.aggregate(
                    total=Coalesce(Count("id"), 0),
                    last_7_days=Coalesce(Count("id", filter=model.objects.filter(**filter_kwargs_7d).query.where), 0),
                    last_30_days=Coalesce(Count("id", filter=model.objects.filter(**filter_kwargs_30d).query.where), 0),
                    oldest_record=Min(date_field),
                    newest_record=Max(date_field),
                )

                # Get table size from database
                table_name = model._meta.db_table
                with connection.cursor() as cursor:
                    cursor.execute(
                        """
                        SELECT pg_size_pretty(pg_total_relation_size(%s))
                        """,
                        [table_name],
                    )
                    result = cursor.fetchone()
                    table_size = result[0] if result else "Unknown"

                stats.append(
                    {
                        "table_name": table_name,
                        "total_records": model.objects.count(),
                        "last_7_days": model.objects.filter(**filter_kwargs_7d).count(),
                        "last_30_days": model.objects.filter(**filter_kwargs_30d).count(),
                        "oldest_record": qs.get("oldest_record"),
                        "newest_record": qs.get("newest_record"),
                        "table_size": table_size,
                    }
                )
            except Exception:
                # Skip tables that don't exist or have errors
                continue

        serializer = DataRetentionStatsSerializer(stats, many=True)
        return Response(serializer.data)
