"""
Statistics API views for ThrillWiki.

Provides aggregate statistics about the platform's content including
counts of parks, rides, manufacturers, and other entities.
"""

from datetime import datetime

from django.core.cache import cache
from django.db.models import Count
from django.utils import timezone
from drf_spectacular.utils import OpenApiExample, extend_schema
from rest_framework import status
from rest_framework.permissions import AllowAny, IsAdminUser
from rest_framework.response import Response
from rest_framework.views import APIView

from apps.parks.models import Company as ParkCompany
from apps.parks.models import Park, ParkPhoto, ParkReview
from apps.rides.models import (
    Company as RideCompany,
)
from apps.rides.models import (
    Ride,
    RidePhoto,
    RideReview,
    RollerCoasterStats,
)

from ..serializers.stats import StatsSerializer


class StatsAPIView(APIView):
    """
    API endpoint that returns aggregate statistics about the platform.

    Returns counts of various entities like parks, rides, manufacturers, etc.
    Results are cached for performance.
    """

    permission_classes = [AllowAny]

    def _get_relative_time(self, timestamp_str):
        """
        Convert an ISO timestamp to a human-readable relative time.

        Args:
            timestamp_str: ISO format timestamp string

        Returns:
            str: Human-readable relative time (e.g., "2 days, 3 hours, 15 minutes ago", "just now")
        """
        if not timestamp_str or timestamp_str == "just_now":
            return "just now"

        try:
            # Parse the ISO timestamp
            if isinstance(timestamp_str, str):
                timestamp = datetime.fromisoformat(timestamp_str.replace("Z", "+00:00"))
            else:
                timestamp = timestamp_str

            # Make timezone-aware if needed
            if timestamp.tzinfo is None:
                timestamp = timezone.make_aware(timestamp)

            now = timezone.now()
            diff = now - timestamp
            total_seconds = int(diff.total_seconds())

            # If less than a minute, return "just now"
            if total_seconds < 60:
                return "just now"

            # Calculate time components
            days = diff.days
            hours = (total_seconds % 86400) // 3600
            minutes = (total_seconds % 3600) // 60

            # Build the relative time string
            parts = []

            if days > 0:
                parts.append(f'{days} day{"s" if days != 1 else ""}')

            if hours > 0:
                parts.append(f'{hours} hour{"s" if hours != 1 else ""}')

            if minutes > 0:
                parts.append(f'{minutes} minute{"s" if minutes != 1 else ""}')

            # Join parts with commas and add "ago"
            if len(parts) == 0:
                return "just now"
            elif len(parts) == 1:
                return f"{parts[0]} ago"
            elif len(parts) == 2:
                return f"{parts[0]} and {parts[1]} ago"
            else:
                return f'{", ".join(parts[:-1])}, and {parts[-1]} ago'

        except (ValueError, TypeError):
            return "unknown"

    @extend_schema(
        operation_id="get_platform_stats",
        summary="Get platform statistics",
        description="""
        Returns comprehensive aggregate statistics about the ThrillWiki platform.

        This endpoint provides detailed counts and breakdowns of all major entities including:
        - Parks, rides, and roller coasters
        - Companies (manufacturers, operators, designers, property owners)
        - Photos and reviews
        - Ride categories (roller coasters, dark rides, flat rides, etc.)
        - Status breakdowns (operating, closed, under construction, etc.)

        Results are cached for 5 minutes for optimal performance and automatically
        invalidated when relevant data changes.

        **No authentication required** - this is a public endpoint.
        """.strip(),
        responses={
            200: StatsSerializer,
            500: {
                "type": "object",
                "properties": {
                    "error": {
                        "type": "string",
                        "description": "Error message if statistics calculation fails",
                    }
                },
            },
        },
        tags=["Statistics"],
        examples=[
            OpenApiExample(
                name="Sample Response",
                description="Example of platform statistics response",
                value={
                    "total_parks": 7,
                    "total_rides": 10,
                    "total_manufacturers": 6,
                    "total_operators": 7,
                    "total_designers": 4,
                    "total_property_owners": 0,
                    "total_roller_coasters": 8,
                    "total_photos": 0,
                    "total_park_photos": 0,
                    "total_ride_photos": 0,
                    "total_reviews": 8,
                    "total_park_reviews": 4,
                    "total_ride_reviews": 4,
                    "roller_coasters": 10,
                    "operating_parks": 7,
                    "operating_rides": 10,
                    "last_updated": "2025-08-28T17:34:59.677143+00:00",
                    "relative_last_updated": "just now",
                },
            )
        ],
    )
    def get(self, request):
        """Get platform statistics."""
        # Try to get cached stats first
        cache_key = "platform_stats"
        cached_stats = cache.get(cache_key)

        if cached_stats:
            return Response(cached_stats, status=status.HTTP_200_OK)

        # Calculate fresh stats
        stats = self._calculate_stats()

        # Cache for 5 minutes
        cache.set(cache_key, stats, 300)

        return Response(stats, status=status.HTTP_200_OK)

    def _calculate_stats(self):
        """Calculate all platform statistics."""

        # Basic entity counts
        total_parks = Park.objects.count()
        total_rides = Ride.objects.count()

        # Company counts by role
        total_manufacturers = RideCompany.objects.filter(roles__contains=["MANUFACTURER"]).count()

        total_operators = ParkCompany.objects.filter(roles__contains=["OPERATOR"]).count()

        total_designers = RideCompany.objects.filter(roles__contains=["DESIGNER"]).count()

        total_property_owners = ParkCompany.objects.filter(roles__contains=["PROPERTY_OWNER"]).count()

        # Photo counts (combined)
        total_park_photos = ParkPhoto.objects.count()
        total_ride_photos = RidePhoto.objects.count()
        total_photos = total_park_photos + total_ride_photos

        # Ride type counts
        total_roller_coasters = RollerCoasterStats.objects.count()

        # Ride category counts
        ride_categories = Ride.objects.values("category").annotate(count=Count("id")).exclude(category="")

        category_stats = {}
        for category in ride_categories:
            category_code = category["category"]
            category_count = category["count"]

            # Convert category codes to readable names
            category_names = {
                "RC": "roller_coasters",
                "DR": "dark_rides",
                "FR": "flat_rides",
                "WR": "water_rides",
                "TR": "transport_rides",
                "OT": "other_rides",
            }

            category_name = category_names.get(category_code, f"category_{category_code.lower()}")
            category_stats[category_name] = category_count

        # Park status counts
        park_statuses = Park.objects.values("status").annotate(count=Count("id"))

        park_status_stats = {}
        for status_item in park_statuses:
            status_code = status_item["status"]
            status_count = status_item["count"]

            # Convert status codes to readable names
            status_names = {
                "OPERATING": "operating_parks",
                "CLOSED_TEMP": "temporarily_closed_parks",
                "CLOSED_PERM": "permanently_closed_parks",
                "UNDER_CONSTRUCTION": "under_construction_parks",
                "DEMOLISHED": "demolished_parks",
                "RELOCATED": "relocated_parks",
            }

            if status_code in status_names:
                status_name = status_names[status_code]
            else:
                raise ValueError(f"Unknown park status: {status_code}")
            park_status_stats[status_name] = status_count

        # Ride status counts
        ride_statuses = Ride.objects.values("status").annotate(count=Count("id"))

        ride_status_stats = {}
        for status_item in ride_statuses:
            status_code = status_item["status"]
            status_count = status_item["count"]

            # Convert status codes to readable names
            status_names = {
                "OPERATING": "operating_rides",
                "CLOSED_TEMP": "temporarily_closed_rides",
                "SBNO": "sbno_rides",
                "CLOSING": "closing_rides",
                "CLOSED_PERM": "permanently_closed_rides",
                "UNDER_CONSTRUCTION": "under_construction_rides",
                "DEMOLISHED": "demolished_rides",
                "RELOCATED": "relocated_rides",
            }

            status_name = status_names.get(status_code, f"ride_status_{status_code.lower()}")
            ride_status_stats[status_name] = status_count

        # Review counts
        total_park_reviews = ParkReview.objects.count()
        total_ride_reviews = RideReview.objects.count()
        total_reviews = total_park_reviews + total_ride_reviews

        # Timestamp handling
        now = timezone.now()
        last_updated_iso = now.isoformat()

        # Get cached timestamp or use current time
        cached_timestamp = cache.get("platform_stats_timestamp")
        if cached_timestamp and cached_timestamp != "just_now":
            # Use cached timestamp for consistency
            last_updated_iso = cached_timestamp
        else:
            # Set new timestamp in cache
            cache.set("platform_stats_timestamp", last_updated_iso, 300)

        # Calculate relative time
        relative_last_updated = self._get_relative_time(last_updated_iso)

        # Combine all stats
        stats = {
            # Core entity counts
            "total_parks": total_parks,
            "total_rides": total_rides,
            "total_manufacturers": total_manufacturers,
            "total_operators": total_operators,
            "total_designers": total_designers,
            "total_property_owners": total_property_owners,
            "total_roller_coasters": total_roller_coasters,
            # Photo counts
            "total_photos": total_photos,
            "total_park_photos": total_park_photos,
            "total_ride_photos": total_ride_photos,
            # Review counts
            "total_reviews": total_reviews,
            "total_park_reviews": total_park_reviews,
            "total_ride_reviews": total_ride_reviews,
            # Category breakdowns
            **category_stats,
            # Status breakdowns
            **park_status_stats,
            **ride_status_stats,
            # Metadata
            "last_updated": last_updated_iso,
            "relative_last_updated": relative_last_updated,
        }

        return stats


class StatsRecalculateAPIView(APIView):
    """
    Admin-only API endpoint to force recalculation of platform statistics.

    This endpoint clears the cache and forces a fresh calculation of all statistics.
    Only accessible to admin users.
    """

    permission_classes = [IsAdminUser]

    @extend_schema(exclude=True)
    def post(self, request):
        """Force recalculation of platform statistics."""
        # Clear the cache
        cache.delete("platform_stats")
        cache.delete("platform_stats_timestamp")

        # Create a new StatsAPIView instance to reuse the calculation logic
        stats_view = StatsAPIView()
        fresh_stats = stats_view._calculate_stats()

        # Cache the fresh stats
        cache.set("platform_stats", fresh_stats, 300)

        # Return success response with the fresh stats
        return Response(
            {
                "detail": "Platform statistics have been successfully recalculated",
                "stats": fresh_stats,
                "recalculated_at": timezone.now().isoformat(),
            },
            status=status.HTTP_200_OK,
        )
