"""
Django management command to calculate trending content.

This replaces the Celery task for calculating trending content.
Run with: uv run manage.py calculate_trending
"""

import logging
from typing import Any

from django.contrib.contenttypes.models import ContentType
from django.core.cache import cache
from django.core.management.base import BaseCommand, CommandError
from django.utils import timezone

from apps.core.analytics import PageView
from apps.parks.models import Park
from apps.rides.models import Ride
from apps.core.utils import capture_and_log

logger = logging.getLogger(__name__)


class Command(BaseCommand):
    help = "Calculate trending content and cache results"

    def add_arguments(self, parser):
        parser.add_argument(
            "--content-type",
            type=str,
            default="all",
            choices=["all", "parks", "rides"],
            help="Type of content to calculate (default: all)",
        )
        parser.add_argument(
            "--limit",
            type=int,
            default=50,
            help="Maximum number of results to calculate (default: 50)",
        )
        parser.add_argument("--verbose", action="store_true", help="Enable verbose output")

    def handle(self, *args, **options):
        content_type = options["content_type"]
        limit = options["limit"]
        verbose = options["verbose"]

        if verbose:
            self.stdout.write(f"Starting trending calculation for {content_type}")

        try:
            # Time windows for calculations
            current_period_hours = 168  # 7 days
            # 14 days (for previous 7-day window comparison)
            previous_period_hours = 336

            trending_items = []

            if content_type in ["all", "parks"]:
                park_items = self._calculate_trending_parks(
                    current_period_hours,
                    previous_period_hours,
                    limit if content_type == "parks" else limit * 2,
                )
                trending_items.extend(park_items)
                if verbose:
                    self.stdout.write(f"Calculated {len(park_items)} trending parks")

            if content_type in ["all", "rides"]:
                ride_items = self._calculate_trending_rides(
                    current_period_hours,
                    previous_period_hours,
                    limit if content_type == "rides" else limit * 2,
                )
                trending_items.extend(ride_items)
                if verbose:
                    self.stdout.write(f"Calculated {len(ride_items)} trending rides")

            # Sort by trending score and apply limit
            trending_items.sort(key=lambda x: x.get("trending_score", 0), reverse=True)
            trending_items = trending_items[:limit]

            # Format results for API consumption
            formatted_results = self._format_trending_results(
                trending_items, current_period_hours, previous_period_hours
            )

            # Cache results
            cache_key = f"trending:calculated:{content_type}:{limit}"
            cache.set(cache_key, formatted_results, 3600)  # Cache for 1 hour

            self.stdout.write(
                self.style.SUCCESS(
                    f"Successfully calculated {len(formatted_results)} trending items for {content_type}"
                )
            )

            if verbose:
                for item in formatted_results[:5]:  # Show first 5 items
                    self.stdout.write(f"  {item['name']} (score: {item.get('views_change', 'N/A')})")

        except Exception as e:
            capture_and_log(e, 'Calculate trending content', source='management', severity='high')
            raise CommandError(f"Failed to calculate trending content: {e}") from None

    def _calculate_trending_parks(
        self, current_period_hours: int, previous_period_hours: int, limit: int
    ) -> list[dict[str, Any]]:
        """Calculate trending scores for parks using real data."""
        parks = Park.objects.filter(status="OPERATING").select_related("location", "operator")

        trending_parks = []

        for park in parks:
            try:
                score = self._calculate_content_score(park, "park", current_period_hours, previous_period_hours)
                if score > 0:  # Only include items with positive trending scores
                    trending_parks.append(
                        {
                            "content_object": park,
                            "content_type": "park",
                            "trending_score": score,
                            "id": park.id,
                            "name": park.name,
                            "slug": park.slug,
                            "park": park.name,  # For parks, park field is the park name itself
                            "category": "park",
                            "rating": (float(park.average_rating) if park.average_rating else 0.0),
                            "date_opened": (park.opening_date.isoformat() if park.opening_date else ""),
                            "url": park.url,
                        }
                    )
            except Exception as e:
                logger.warning(f"Error calculating score for park {park.id}: {e}")

        return trending_parks

    def _calculate_trending_rides(
        self, current_period_hours: int, previous_period_hours: int, limit: int
    ) -> list[dict[str, Any]]:
        """Calculate trending scores for rides using real data."""
        rides = Ride.objects.filter(status="OPERATING").select_related("park", "park__location")

        trending_rides = []

        for ride in rides:
            try:
                score = self._calculate_content_score(ride, "ride", current_period_hours, previous_period_hours)
                if score > 0:  # Only include items with positive trending scores
                    trending_rides.append(
                        {
                            "content_object": ride,
                            "content_type": "ride",
                            "trending_score": score,
                            "id": ride.pk,
                            "name": ride.name,
                            "slug": ride.slug,
                            "park": ride.park.name if ride.park else "",
                            "category": "ride",
                            "rating": (float(ride.average_rating) if ride.average_rating else 0.0),
                            "date_opened": (ride.opening_date.isoformat() if ride.opening_date else ""),
                            "url": ride.url,
                            "park_url": ride.park.url if ride.park else "",
                        }
                    )
            except Exception as e:
                logger.warning(f"Error calculating score for ride {ride.pk}: {e}")

        return trending_rides

    def _calculate_content_score(
        self,
        content_obj: Any,
        content_type: str,
        current_period_hours: int,
        previous_period_hours: int,
    ) -> float:
        """Calculate weighted trending score for content object using real analytics data."""
        try:
            # Get content type for PageView queries
            ct = ContentType.objects.get_for_model(content_obj)

            # 1. View Growth Score (40% weight)
            view_growth_score = self._calculate_view_growth_score(
                ct, content_obj.id, current_period_hours, previous_period_hours
            )

            # 2. Rating Score (30% weight)
            rating_score = self._calculate_rating_score(content_obj)

            # 3. Recency Score (20% weight)
            recency_score = self._calculate_recency_score(content_obj)

            # 4. Popularity Score (10% weight)
            popularity_score = self._calculate_popularity_score(ct, content_obj.id, current_period_hours)

            # Calculate weighted final score
            final_score = view_growth_score * 0.4 + rating_score * 0.3 + recency_score * 0.2 + popularity_score * 0.1

            return final_score

        except Exception as e:
            capture_and_log(e, f'Calculate score for {content_type} {content_obj.id}', source='management', severity='medium')
            return 0.0

    def _calculate_view_growth_score(
        self,
        content_type: ContentType,
        object_id: int,
        current_period_hours: int,
        previous_period_hours: int,
    ) -> float:
        """Calculate normalized view growth score using real PageView data."""
        try:
            current_views, previous_views, growth_percentage = PageView.get_views_growth(
                content_type,
                object_id,
                current_period_hours,
                previous_period_hours,
            )

            if previous_views == 0:
                # New content with views gets boost
                return min(current_views / 100.0, 1.0) if current_views > 0 else 0.0

            # Normalize growth percentage to 0-1 scale
            normalized_growth = min(growth_percentage / 500.0, 1.0) if growth_percentage > 0 else 0.0
            return max(normalized_growth, 0.0)

        except Exception as e:
            logger.warning(f"Error calculating view growth: {e}")
            return 0.0

    def _calculate_rating_score(self, content_obj: Any) -> float:
        """Calculate normalized rating score."""
        try:
            rating = getattr(content_obj, "average_rating", None)
            if rating is None or rating == 0:
                return 0.3  # Neutral score for unrated content

            # Normalize rating from 1-10 scale to 0-1 scale
            return min(max((float(rating) - 1) / 9.0, 0.0), 1.0)

        except Exception as e:
            logger.warning(f"Error calculating rating score: {e}")
            return 0.3

    def _calculate_recency_score(self, content_obj: Any) -> float:
        """Calculate recency score based on when content was added/updated."""
        try:
            # Use opening_date for parks/rides, or created_at as fallback
            date_added = getattr(content_obj, "opening_date", None)
            if not date_added:
                date_added = getattr(content_obj, "created_at", None)
            if not date_added:
                return 0.5  # Neutral score for unknown dates

            # Handle both date and datetime objects
            if hasattr(date_added, "date"):
                date_added = date_added.date()

            # Calculate days since added
            today = timezone.now().date()
            days_since_added = (today - date_added).days

            # Recency score: newer content gets higher scores
            if days_since_added <= 0:
                return 1.0
            elif days_since_added <= 30:
                return 1.0 - (days_since_added / 30.0) * 0.2  # 1.0 to 0.8
            elif days_since_added <= 365:
                return 0.8 - ((days_since_added - 30) / (365 - 30)) * 0.7  # 0.8 to 0.1
            else:
                return 0.0

        except Exception as e:
            logger.warning(f"Error calculating recency score: {e}")
            return 0.5

    def _calculate_popularity_score(self, content_type: ContentType, object_id: int, hours: int) -> float:
        """Calculate popularity score based on total view count."""
        try:
            total_views = PageView.get_total_views_count(content_type, object_id, hours=hours)

            # Normalize views to 0-1 scale
            if total_views == 0:
                return 0.0
            elif total_views <= 100:
                return total_views / 200.0  # 0.0 to 0.5
            else:
                return min(0.5 + (total_views - 100) / 1800.0, 1.0)  # 0.5 to 1.0

        except Exception as e:
            logger.warning(f"Error calculating popularity score: {e}")
            return 0.0

    def _format_trending_results(
        self,
        trending_items: list[dict[str, Any]],
        current_period_hours: int,
        previous_period_hours: int,
    ) -> list[dict[str, Any]]:
        """Format trending results for frontend consumption."""
        formatted_results = []

        for rank, item in enumerate(trending_items, 1):
            try:
                # Get view change for display
                content_obj = item["content_object"]
                ct = ContentType.objects.get_for_model(content_obj)
                current_views, previous_views, growth_percentage = PageView.get_views_growth(
                    ct,
                    content_obj.id,
                    current_period_hours,
                    previous_period_hours,
                )

                # Format exactly as frontend expects
                formatted_item = {
                    "id": item["id"],
                    "name": item["name"],
                    "park": item["park"],
                    "category": item["category"],
                    "rating": item["rating"],
                    "rank": rank,
                    "views": current_views,
                    "views_change": (
                        f"+{growth_percentage:.1f}%" if growth_percentage > 0 else f"{growth_percentage:.1f}%"
                    ),
                    "slug": item["slug"],
                    "date_opened": item["date_opened"],
                    "url": item["url"],
                }

                # Add park_url for rides
                if item.get("park_url"):
                    formatted_item["park_url"] = item["park_url"]

                formatted_results.append(formatted_item)

            except Exception as e:
                logger.warning(f"Error formatting trending item: {e}")

        return formatted_results
