"""
Management command to warm cache with frequently accessed data.

This command pre-populates the cache with commonly requested data to improve
initial response times after deployment or cache flush.

Usage:
    python manage.py warm_cache
    python manage.py warm_cache --parks-only
    python manage.py warm_cache --rides-only
    python manage.py warm_cache --metadata-only
    python manage.py warm_cache --dry-run
"""

import logging
import time

from django.core.management.base import BaseCommand
from django.db.models import Count

from apps.core.services.enhanced_cache_service import EnhancedCacheService

logger = logging.getLogger(__name__)


class Command(BaseCommand):
    help = "Warm cache with frequently accessed data for improved performance"

    def add_arguments(self, parser):
        parser.add_argument(
            "--dry-run",
            action="store_true",
            help="Show what would be cached without actually caching",
        )
        parser.add_argument(
            "--parks-only",
            action="store_true",
            help="Only warm park-related caches",
        )
        parser.add_argument(
            "--rides-only",
            action="store_true",
            help="Only warm ride-related caches",
        )
        parser.add_argument(
            "--metadata-only",
            action="store_true",
            help="Only warm filter metadata caches",
        )
        parser.add_argument(
            "--verbose",
            action="store_true",
            help="Show detailed output",
        )

    def handle(self, *args, **options):
        dry_run = options["dry_run"]
        parks_only = options["parks_only"]
        rides_only = options["rides_only"]
        metadata_only = options["metadata_only"]
        verbose = options["verbose"]

        # Default to warming all if no specific option is selected
        warm_all = not (parks_only or rides_only or metadata_only)

        start_time = time.time()
        cache_service = EnhancedCacheService()
        warmed_count = 0
        failed_count = 0

        self.stdout.write("Starting cache warming...")

        if dry_run:
            self.stdout.write(self.style.WARNING("DRY RUN - No caches will be modified"))

        # Import models (do this lazily to avoid circular imports)
        try:
            from apps.parks.models import Park
            from apps.rides.models import Ride

            parks_available = True
            rides_available = True
        except ImportError as e:
            self.stdout.write(self.style.WARNING(f"Some models not available: {e}"))
            parks_available = False
            rides_available = False

        # Warm park caches
        if (warm_all or parks_only) and parks_available:
            self.stdout.write("\nWarming park caches...")

            # Park list
            if not dry_run:
                try:
                    parks_list = list(
                        Park.objects.select_related("location", "operator")
                        .only(
                            "id",
                            "name",
                            "slug",
                            "status",
                            "location__city",
                            "location__state_province",
                            "location__country",
                        )
                        .order_by("name")[:500]
                    )
                    cache_service.default_cache.set(
                        "warm:park_list",
                        [{"id": p.id, "name": p.name, "slug": p.slug} for p in parks_list],
                        timeout=3600,
                    )
                    warmed_count += 1
                    if verbose:
                        self.stdout.write(f"  Cached {len(parks_list)} parks")
                except Exception as e:
                    failed_count += 1
                    self.stdout.write(self.style.ERROR(f"  Failed to cache park list: {e}"))
            else:
                self.stdout.write("  Would cache: park_list")
                warmed_count += 1

            # Park counts by status
            if not dry_run:
                try:
                    status_counts = Park.objects.values("status").annotate(count=Count("id"))
                    cache_service.default_cache.set("warm:park_status_counts", list(status_counts), timeout=3600)
                    warmed_count += 1
                    if verbose:
                        self.stdout.write("  Cached park status counts")
                except Exception as e:
                    failed_count += 1
                    self.stdout.write(self.style.ERROR(f"  Failed to cache park status counts: {e}"))
            else:
                self.stdout.write("  Would cache: park_status_counts")
                warmed_count += 1

            # Popular parks (top 20 by ride count)
            if not dry_run:
                try:
                    popular_parks = list(
                        Park.objects.annotate(ride_count=Count("rides"))
                        .select_related("location")
                        .order_by("-ride_count")[:20]
                    )
                    cache_service.default_cache.set(
                        "warm:popular_parks",
                        [
                            {"id": p.id, "name": p.name, "slug": p.slug, "ride_count": p.ride_count}
                            for p in popular_parks
                        ],
                        timeout=3600,
                    )
                    warmed_count += 1
                    if verbose:
                        self.stdout.write(f"  Cached {len(popular_parks)} popular parks")
                except Exception as e:
                    failed_count += 1
                    self.stdout.write(self.style.ERROR(f"  Failed to cache popular parks: {e}"))
            else:
                self.stdout.write("  Would cache: popular_parks")
                warmed_count += 1

        # Warm ride caches
        if (warm_all or rides_only) and rides_available:
            self.stdout.write("\nWarming ride caches...")

            # Ride list
            if not dry_run:
                try:
                    rides_list = list(
                        Ride.objects.select_related("park")
                        .only("id", "name", "slug", "status", "category", "park__name", "park__slug")
                        .order_by("name")[:1000]
                    )
                    cache_service.default_cache.set(
                        "warm:ride_list",
                        [
                            {"id": r.id, "name": r.name, "slug": r.slug, "park": r.park.name if r.park else None}
                            for r in rides_list
                        ],
                        timeout=3600,
                    )
                    warmed_count += 1
                    if verbose:
                        self.stdout.write(f"  Cached {len(rides_list)} rides")
                except Exception as e:
                    failed_count += 1
                    self.stdout.write(self.style.ERROR(f"  Failed to cache ride list: {e}"))
            else:
                self.stdout.write("  Would cache: ride_list")
                warmed_count += 1

            # Ride counts by category
            if not dry_run:
                try:
                    category_counts = Ride.objects.values("category").annotate(count=Count("id"))
                    cache_service.default_cache.set("warm:ride_category_counts", list(category_counts), timeout=3600)
                    warmed_count += 1
                    if verbose:
                        self.stdout.write("  Cached ride category counts")
                except Exception as e:
                    failed_count += 1
                    self.stdout.write(self.style.ERROR(f"  Failed to cache ride category counts: {e}"))
            else:
                self.stdout.write("  Would cache: ride_category_counts")
                warmed_count += 1

            # Top-rated rides
            if not dry_run:
                try:
                    top_rides = list(
                        Ride.objects.filter(average_rating__isnull=False)
                        .select_related("park")
                        .order_by("-average_rating")[:20]
                    )
                    cache_service.default_cache.set(
                        "warm:top_rated_rides",
                        [
                            {
                                "id": r.id,
                                "name": r.name,
                                "slug": r.slug,
                                "rating": float(r.average_rating) if r.average_rating else None,
                            }
                            for r in top_rides
                        ],
                        timeout=3600,
                    )
                    warmed_count += 1
                    if verbose:
                        self.stdout.write(f"  Cached {len(top_rides)} top-rated rides")
                except Exception as e:
                    failed_count += 1
                    self.stdout.write(self.style.ERROR(f"  Failed to cache top-rated rides: {e}"))
            else:
                self.stdout.write("  Would cache: top_rated_rides")
                warmed_count += 1

        # Warm filter metadata caches
        if warm_all or metadata_only:
            self.stdout.write("\nWarming filter metadata caches...")

            if parks_available and not dry_run:
                try:
                    # Park filter metadata
                    from apps.parks.services.hybrid_loader import smart_park_loader

                    metadata = smart_park_loader.get_filter_metadata()
                    cache_service.default_cache.set("warm:park_filter_metadata", metadata, timeout=1800)
                    warmed_count += 1
                    if verbose:
                        self.stdout.write("  Cached park filter metadata")
                except Exception as e:
                    failed_count += 1
                    self.stdout.write(self.style.ERROR(f"  Failed to cache park filter metadata: {e}"))
            elif parks_available:
                self.stdout.write("  Would cache: park_filter_metadata")
                warmed_count += 1

            if rides_available and not dry_run:
                try:
                    # Ride filter metadata
                    from apps.rides.services.hybrid_loader import SmartRideLoader

                    ride_loader = SmartRideLoader()
                    metadata = ride_loader.get_filter_metadata()
                    cache_service.default_cache.set("warm:ride_filter_metadata", metadata, timeout=1800)
                    warmed_count += 1
                    if verbose:
                        self.stdout.write("  Cached ride filter metadata")
                except Exception as e:
                    failed_count += 1
                    self.stdout.write(self.style.ERROR(f"  Failed to cache ride filter metadata: {e}"))
            elif rides_available:
                self.stdout.write("  Would cache: ride_filter_metadata")
                warmed_count += 1

        # Summary
        elapsed_time = time.time() - start_time
        self.stdout.write("\n" + "=" * 60)
        self.stdout.write(self.style.SUCCESS(f"Cache warming completed in {elapsed_time:.2f} seconds"))
        self.stdout.write(f"Successfully warmed: {warmed_count} cache entries")
        if failed_count > 0:
            self.stdout.write(self.style.ERROR(f"Failed: {failed_count} cache entries"))

        if dry_run:
            self.stdout.write(self.style.WARNING("\nDry run - no caches were actually modified"))
