"""
Smart Ride Loader for Hybrid Filtering Strategy

This service implements intelligent data loading for rides, automatically choosing
between client-side and server-side filtering based on data size and complexity.

Key Features:
- Automatic strategy selection (≤200 records = client-side, >200 = server-side)
- Progressive loading for large datasets
- Intelligent caching with automatic invalidation
- Comprehensive filter metadata generation
- Optimized database queries with strategic prefetching

Architecture:
- Client-side: Load all data once, filter in frontend
- Server-side: Apply filters in database, paginate results
- Hybrid: Combine both approaches based on data characteristics
"""

import logging
from typing import Any

from django.core.cache import cache
from django.db import models
from django.db.models import Max, Min, Q

logger = logging.getLogger(__name__)


class SmartRideLoader:
    """
    Intelligent ride data loader that chooses optimal filtering strategy.

    Strategy Selection:
    - ≤200 total records: Client-side filtering (load all data)
    - >200 total records: Server-side filtering (database filtering + pagination)

    Features:
    - Progressive loading for large datasets
    - 5-minute intelligent caching
    - Comprehensive filter metadata
    - Optimized queries with prefetch_related
    """

    # Configuration constants
    INITIAL_LOAD_SIZE = 50
    PROGRESSIVE_LOAD_SIZE = 25
    MAX_CLIENT_SIDE_RECORDS = 200
    CACHE_TIMEOUT = 300  # 5 minutes

    def __init__(self):
        self.cache_prefix = "rides_hybrid_"

    def get_initial_load(self, filters: dict[str, Any] | None = None) -> dict[str, Any]:
        """
        Get initial data load with automatic strategy selection.

        Args:
            filters: Optional filter parameters

        Returns:
            Dict containing:
            - strategy: 'client_side' or 'server_side'
            - data: List of ride records
            - total_count: Total number of records
            - has_more: Whether more data is available
            - filter_metadata: Available filter options
        """

        # Get total count for strategy decision
        total_count = self._get_total_count(filters)

        # Choose strategy based on total count
        if total_count <= self.MAX_CLIENT_SIDE_RECORDS:
            return self._get_client_side_data(filters, total_count)
        else:
            return self._get_server_side_data(filters, total_count)

    def get_progressive_load(self, offset: int, filters: dict[str, Any] | None = None) -> dict[str, Any]:
        """
        Get additional data for progressive loading (server-side strategy only).

        Args:
            offset: Number of records to skip
            filters: Filter parameters

        Returns:
            Dict containing additional ride records
        """

        # Build queryset with filters
        queryset = self._build_filtered_queryset(filters)

        # Get total count for this filtered set
        total_count = queryset.count()

        # Get progressive batch
        rides = list(queryset[offset : offset + self.PROGRESSIVE_LOAD_SIZE])

        return {
            "rides": self._serialize_rides(rides),
            "total_count": total_count,
            "has_more": len(rides) == self.PROGRESSIVE_LOAD_SIZE,
            "next_offset": offset + len(rides) if len(rides) == self.PROGRESSIVE_LOAD_SIZE else None,
        }

    def get_filter_metadata(self, filters: dict[str, Any] | None = None) -> dict[str, Any]:
        """
        Get comprehensive filter metadata for dynamic filter generation.

        Args:
            filters: Optional filters to scope the metadata

        Returns:
            Dict containing all available filter options and ranges
        """
        cache_key = f"{self.cache_prefix}filter_metadata_{hash(str(filters))}"
        metadata = cache.get(cache_key)

        if metadata is None:
            metadata = self._generate_filter_metadata(filters)
            cache.set(cache_key, metadata, self.CACHE_TIMEOUT)

        return metadata

    def invalidate_cache(self) -> None:
        """Invalidate all cached data for rides."""
        # Note: In production, you might want to use cache versioning
        # or more sophisticated cache invalidation
        cache_keys = [
            f"{self.cache_prefix}client_side_all",
            f"{self.cache_prefix}filter_metadata",
            f"{self.cache_prefix}total_count",
        ]

        for key in cache_keys:
            cache.delete(key)

    def _get_total_count(self, filters: dict[str, Any] | None = None) -> int:
        """Get total count of rides matching filters."""
        cache_key = f"{self.cache_prefix}total_count_{hash(str(filters))}"
        count = cache.get(cache_key)

        if count is None:
            queryset = self._build_filtered_queryset(filters)
            count = queryset.count()
            cache.set(cache_key, count, self.CACHE_TIMEOUT)

        return count

    def _get_client_side_data(self, filters: dict[str, Any] | None, total_count: int) -> dict[str, Any]:
        """Get all data for client-side filtering."""
        cache_key = f"{self.cache_prefix}client_side_all"
        cached_data = cache.get(cache_key)

        if cached_data is None:
            from apps.rides.models import Ride

            # Load all rides with optimized query
            queryset = (
                Ride.objects.select_related(
                    "park",
                    "park__location",
                    "park_area",
                    "manufacturer",
                    "designer",
                    "ride_model",
                    "ride_model__manufacturer",
                )
                .prefetch_related("coaster_stats")
                .order_by("name")
            )

            rides = list(queryset)
            cached_data = self._serialize_rides(rides)
            cache.set(cache_key, cached_data, self.CACHE_TIMEOUT)

        return {
            "strategy": "client_side",
            "rides": cached_data,
            "total_count": total_count,
            "has_more": False,
            "filter_metadata": self.get_filter_metadata(filters),
        }

    def _get_server_side_data(self, filters: dict[str, Any] | None, total_count: int) -> dict[str, Any]:
        """Get initial batch for server-side filtering."""
        # Build filtered queryset
        queryset = self._build_filtered_queryset(filters)

        # Get initial batch
        rides = list(queryset[: self.INITIAL_LOAD_SIZE])

        return {
            "strategy": "server_side",
            "rides": self._serialize_rides(rides),
            "total_count": total_count,
            "has_more": len(rides) == self.INITIAL_LOAD_SIZE,
            "next_offset": len(rides) if len(rides) == self.INITIAL_LOAD_SIZE else None,
        }

    def _build_filtered_queryset(self, filters: dict[str, Any] | None):
        """Build Django queryset with applied filters."""
        from apps.rides.models import Ride

        # Start with optimized base queryset
        queryset = Ride.objects.select_related(
            "park", "park__location", "park_area", "manufacturer", "designer", "ride_model", "ride_model__manufacturer"
        ).prefetch_related("coaster_stats")

        if not filters:
            return queryset.order_by("name")

        # Apply filters
        q_objects = Q()

        # Text search using computed search_text field
        if "search" in filters and filters["search"]:
            search_term = filters["search"].lower()
            q_objects &= Q(search_text__icontains=search_term)

        # Park filters
        if "park_slug" in filters and filters["park_slug"]:
            q_objects &= Q(park__slug=filters["park_slug"])

        if "park_id" in filters and filters["park_id"]:
            q_objects &= Q(park_id=filters["park_id"])

        # Category filters
        if "category" in filters and filters["category"]:
            q_objects &= Q(category__in=filters["category"])

        # Status filters
        if "status" in filters and filters["status"]:
            q_objects &= Q(status__in=filters["status"])

        # Company filters
        if "manufacturer_ids" in filters and filters["manufacturer_ids"]:
            q_objects &= Q(manufacturer_id__in=filters["manufacturer_ids"])

        if "designer_ids" in filters and filters["designer_ids"]:
            q_objects &= Q(designer_id__in=filters["designer_ids"])

        # Ride model filters
        if "ride_model_ids" in filters and filters["ride_model_ids"]:
            q_objects &= Q(ride_model_id__in=filters["ride_model_ids"])

        # Opening year filters using computed opening_year field
        if "opening_year" in filters and filters["opening_year"]:
            q_objects &= Q(opening_year=filters["opening_year"])

        if "min_opening_year" in filters and filters["min_opening_year"]:
            q_objects &= Q(opening_year__gte=filters["min_opening_year"])

        if "max_opening_year" in filters and filters["max_opening_year"]:
            q_objects &= Q(opening_year__lte=filters["max_opening_year"])

        # Rating filters
        if "min_rating" in filters and filters["min_rating"]:
            q_objects &= Q(average_rating__gte=filters["min_rating"])

        if "max_rating" in filters and filters["max_rating"]:
            q_objects &= Q(average_rating__lte=filters["max_rating"])

        # Height requirement filters
        if "min_height_requirement" in filters and filters["min_height_requirement"]:
            q_objects &= Q(min_height_in__gte=filters["min_height_requirement"])

        if "max_height_requirement" in filters and filters["max_height_requirement"]:
            q_objects &= Q(max_height_in__lte=filters["max_height_requirement"])

        # Capacity filters
        if "min_capacity" in filters and filters["min_capacity"]:
            q_objects &= Q(capacity_per_hour__gte=filters["min_capacity"])

        if "max_capacity" in filters and filters["max_capacity"]:
            q_objects &= Q(capacity_per_hour__lte=filters["max_capacity"])

        # Roller coaster specific filters
        if "roller_coaster_type" in filters and filters["roller_coaster_type"]:
            q_objects &= Q(coaster_stats__roller_coaster_type__in=filters["roller_coaster_type"])

        if "track_material" in filters and filters["track_material"]:
            q_objects &= Q(coaster_stats__track_material__in=filters["track_material"])

        if "propulsion_system" in filters and filters["propulsion_system"]:
            q_objects &= Q(coaster_stats__propulsion_system__in=filters["propulsion_system"])

        # Roller coaster height filters
        if "min_height_ft" in filters and filters["min_height_ft"]:
            q_objects &= Q(coaster_stats__height_ft__gte=filters["min_height_ft"])

        if "max_height_ft" in filters and filters["max_height_ft"]:
            q_objects &= Q(coaster_stats__height_ft__lte=filters["max_height_ft"])

        # Roller coaster speed filters
        if "min_speed_mph" in filters and filters["min_speed_mph"]:
            q_objects &= Q(coaster_stats__speed_mph__gte=filters["min_speed_mph"])

        if "max_speed_mph" in filters and filters["max_speed_mph"]:
            q_objects &= Q(coaster_stats__speed_mph__lte=filters["max_speed_mph"])

        # Inversion filters
        if "min_inversions" in filters and filters["min_inversions"]:
            q_objects &= Q(coaster_stats__inversions__gte=filters["min_inversions"])

        if "max_inversions" in filters and filters["max_inversions"]:
            q_objects &= Q(coaster_stats__inversions__lte=filters["max_inversions"])

        if "has_inversions" in filters and filters["has_inversions"] is not None:
            if filters["has_inversions"]:
                q_objects &= Q(coaster_stats__inversions__gt=0)
            else:
                q_objects &= Q(coaster_stats__inversions=0)

        # Apply filters and ordering
        queryset = queryset.filter(q_objects)

        # Apply ordering
        ordering = filters.get("ordering", "name")
        if ordering in ["height_ft", "-height_ft", "speed_mph", "-speed_mph"]:
            # For coaster stats ordering, we need to join and order by the stats
            ordering_field = ordering.replace("height_ft", "coaster_stats__height_ft").replace(
                "speed_mph", "coaster_stats__speed_mph"
            )
            queryset = queryset.order_by(ordering_field)
        else:
            queryset = queryset.order_by(ordering)

        return queryset

    def _serialize_rides(self, rides: list) -> list[dict[str, Any]]:
        """Serialize ride objects to dictionaries."""
        serialized = []

        for ride in rides:
            # Basic ride data
            ride_data = {
                "id": ride.id,
                "name": ride.name,
                "slug": ride.slug,
                "description": ride.description,
                "category": ride.category,
                "status": ride.status,
                "opening_date": ride.opening_date.isoformat() if ride.opening_date else None,
                "closing_date": ride.closing_date.isoformat() if ride.closing_date else None,
                "opening_year": ride.opening_year,
                "min_height_in": ride.min_height_in,
                "max_height_in": ride.max_height_in,
                "capacity_per_hour": ride.capacity_per_hour,
                "ride_duration_seconds": ride.ride_duration_seconds,
                "average_rating": float(ride.average_rating) if ride.average_rating else None,
                "url": ride.url,
                "park_url": ride.park_url,
                "created_at": ride.created_at.isoformat(),
                "updated_at": ride.updated_at.isoformat(),
            }

            # Park data
            if ride.park:
                ride_data["park"] = {
                    "id": ride.park.id,
                    "name": ride.park.name,
                    "slug": ride.park.slug,
                }

                # Park location data
                if hasattr(ride.park, "location") and ride.park.location:
                    ride_data["park"]["location"] = {
                        "city": ride.park.location.city,
                        "state": ride.park.location.state,
                        "country": ride.park.location.country,
                    }

            # Park area data
            if ride.park_area:
                ride_data["park_area"] = {
                    "id": ride.park_area.id,
                    "name": ride.park_area.name,
                    "slug": ride.park_area.slug,
                }

            # Company data
            if ride.manufacturer:
                ride_data["manufacturer"] = {
                    "id": ride.manufacturer.id,
                    "name": ride.manufacturer.name,
                    "slug": ride.manufacturer.slug,
                }

            if ride.designer:
                ride_data["designer"] = {
                    "id": ride.designer.id,
                    "name": ride.designer.name,
                    "slug": ride.designer.slug,
                }

            # Ride model data
            if ride.ride_model:
                ride_data["ride_model"] = {
                    "id": ride.ride_model.id,
                    "name": ride.ride_model.name,
                    "slug": ride.ride_model.slug,
                    "category": ride.ride_model.category,
                }

                if ride.ride_model.manufacturer:
                    ride_data["ride_model"]["manufacturer"] = {
                        "id": ride.ride_model.manufacturer.id,
                        "name": ride.ride_model.manufacturer.name,
                        "slug": ride.ride_model.manufacturer.slug,
                    }

            # Roller coaster stats
            if hasattr(ride, "coaster_stats") and ride.coaster_stats:
                stats = ride.coaster_stats
                ride_data["coaster_stats"] = {
                    "height_ft": float(stats.height_ft) if stats.height_ft else None,
                    "length_ft": float(stats.length_ft) if stats.length_ft else None,
                    "speed_mph": float(stats.speed_mph) if stats.speed_mph else None,
                    "inversions": stats.inversions,
                    "ride_time_seconds": stats.ride_time_seconds,
                    "track_type": stats.track_type,
                    "track_material": stats.track_material,
                    "roller_coaster_type": stats.roller_coaster_type,
                    "max_drop_height_ft": float(stats.max_drop_height_ft) if stats.max_drop_height_ft else None,
                    "propulsion_system": stats.propulsion_system,
                    "train_style": stats.train_style,
                    "trains_count": stats.trains_count,
                    "cars_per_train": stats.cars_per_train,
                    "seats_per_car": stats.seats_per_car,
                }

            serialized.append(ride_data)

        return serialized

    def _generate_filter_metadata(self, filters: dict[str, Any] | None = None) -> dict[str, Any]:
        """Generate comprehensive filter metadata."""
        from apps.rides.models import Ride, RideModel
        from apps.rides.models.company import Company
        from apps.rides.models.rides import RollerCoasterStats

        # Get unique values from database with counts
        parks_data = list(
            Ride.objects.exclude(park__isnull=True)
            .select_related("park")
            .values("park__id", "park__name", "park__slug")
            .annotate(count=models.Count("id"))
            .distinct()
            .order_by("park__name")
        )

        park_areas_data = list(
            Ride.objects.exclude(park_area__isnull=True)
            .select_related("park_area")
            .values("park_area__id", "park_area__name", "park_area__slug")
            .annotate(count=models.Count("id"))
            .distinct()
            .order_by("park_area__name")
        )

        manufacturers_data = list(
            Company.objects.filter(roles__contains=["MANUFACTURER"])
            .values("id", "name", "slug")
            .annotate(count=models.Count("manufactured_rides"))
            .order_by("name")
        )

        designers_data = list(
            Company.objects.filter(roles__contains=["DESIGNER"])
            .values("id", "name", "slug")
            .annotate(count=models.Count("designed_rides"))
            .order_by("name")
        )

        ride_models_data = list(
            RideModel.objects.select_related("manufacturer")
            .values("id", "name", "slug", "manufacturer__name", "manufacturer__slug", "category")
            .annotate(count=models.Count("rides"))
            .order_by("manufacturer__name", "name")
        )

        # Get categories and statuses with counts
        categories_data = list(Ride.objects.values("category").annotate(count=models.Count("id")).order_by("category"))

        statuses_data = list(Ride.objects.values("status").annotate(count=models.Count("id")).order_by("status"))

        # Get roller coaster specific data with counts
        rc_types_data = list(
            RollerCoasterStats.objects.values("roller_coaster_type")
            .annotate(count=models.Count("ride"))
            .exclude(roller_coaster_type__isnull=True)
            .order_by("roller_coaster_type")
        )

        track_materials_data = list(
            RollerCoasterStats.objects.values("track_material")
            .annotate(count=models.Count("ride"))
            .exclude(track_material__isnull=True)
            .order_by("track_material")
        )

        propulsion_systems_data = list(
            RollerCoasterStats.objects.values("propulsion_system")
            .annotate(count=models.Count("ride"))
            .exclude(propulsion_system__isnull=True)
            .order_by("propulsion_system")
        )

        # Convert to frontend-expected format with value/label/count
        categories = [
            {"value": item["category"], "label": self._get_category_label(item["category"]), "count": item["count"]}
            for item in categories_data
        ]

        statuses = [
            {"value": item["status"], "label": self._get_status_label(item["status"]), "count": item["count"]}
            for item in statuses_data
        ]

        roller_coaster_types = [
            {
                "value": item["roller_coaster_type"],
                "label": self._get_rc_type_label(item["roller_coaster_type"]),
                "count": item["count"],
            }
            for item in rc_types_data
        ]

        track_materials = [
            {
                "value": item["track_material"],
                "label": self._get_track_material_label(item["track_material"]),
                "count": item["count"],
            }
            for item in track_materials_data
        ]

        propulsion_systems = [
            {
                "value": item["propulsion_system"],
                "label": self._get_propulsion_system_label(item["propulsion_system"]),
                "count": item["count"],
            }
            for item in propulsion_systems_data
        ]

        # Convert other data to expected format
        parks = [
            {"value": str(item["park__id"]), "label": item["park__name"], "count": item["count"]} for item in parks_data
        ]

        park_areas = [
            {"value": str(item["park_area__id"]), "label": item["park_area__name"], "count": item["count"]}
            for item in park_areas_data
        ]

        manufacturers = [
            {"value": str(item["id"]), "label": item["name"], "count": item["count"]} for item in manufacturers_data
        ]

        designers = [
            {"value": str(item["id"]), "label": item["name"], "count": item["count"]} for item in designers_data
        ]

        ride_models = [
            {"value": str(item["id"]), "label": f"{item['manufacturer__name']} {item['name']}", "count": item["count"]}
            for item in ride_models_data
        ]

        # Calculate ranges from actual data
        ride_stats = Ride.objects.aggregate(
            min_rating=Min("average_rating"),
            max_rating=Max("average_rating"),
            min_height_req=Min("min_height_in"),
            max_height_req=Max("max_height_in"),
            min_capacity=Min("capacity_per_hour"),
            max_capacity=Max("capacity_per_hour"),
            min_duration=Min("ride_duration_seconds"),
            max_duration=Max("ride_duration_seconds"),
            min_year=Min("opening_year"),
            max_year=Max("opening_year"),
        )

        # Calculate roller coaster specific ranges
        coaster_stats = RollerCoasterStats.objects.aggregate(
            min_height_ft=Min("height_ft"),
            max_height_ft=Max("height_ft"),
            min_length_ft=Min("length_ft"),
            max_length_ft=Max("length_ft"),
            min_speed_mph=Min("speed_mph"),
            max_speed_mph=Max("speed_mph"),
            min_inversions=Min("inversions"),
            max_inversions=Max("inversions"),
            min_ride_time=Min("ride_time_seconds"),
            max_ride_time=Max("ride_time_seconds"),
            min_drop_height=Min("max_drop_height_ft"),
            max_drop_height=Max("max_drop_height_ft"),
            min_trains=Min("trains_count"),
            max_trains=Max("trains_count"),
            min_cars=Min("cars_per_train"),
            max_cars=Max("cars_per_train"),
            min_seats=Min("seats_per_car"),
            max_seats=Max("seats_per_car"),
        )

        return {
            "categorical": {
                "categories": categories,
                "statuses": statuses,
                "roller_coaster_types": roller_coaster_types,
                "track_materials": track_materials,
                "propulsion_systems": propulsion_systems,
                "parks": parks,
                "park_areas": park_areas,
                "manufacturers": manufacturers,
                "designers": designers,
                "ride_models": ride_models,
            },
            "ranges": {
                "rating": {
                    "min": float(ride_stats["min_rating"] or 1),
                    "max": float(ride_stats["max_rating"] or 10),
                    "step": 0.1,
                    "unit": "stars",
                },
                "height_requirement": {
                    "min": ride_stats["min_height_req"] or 30,
                    "max": ride_stats["max_height_req"] or 90,
                    "step": 1,
                    "unit": "inches",
                },
                "capacity": {
                    "min": ride_stats["min_capacity"] or 0,
                    "max": ride_stats["max_capacity"] or 5000,
                    "step": 50,
                    "unit": "riders/hour",
                },
                "ride_duration": {
                    "min": ride_stats["min_duration"] or 0,
                    "max": ride_stats["max_duration"] or 600,
                    "step": 10,
                    "unit": "seconds",
                },
                "opening_year": {
                    "min": ride_stats["min_year"] or 1800,
                    "max": ride_stats["max_year"] or 2030,
                    "step": 1,
                    "unit": "year",
                },
                "height_ft": {
                    "min": float(coaster_stats["min_height_ft"] or 0),
                    "max": float(coaster_stats["max_height_ft"] or 500),
                    "step": 5,
                    "unit": "feet",
                },
                "length_ft": {
                    "min": float(coaster_stats["min_length_ft"] or 0),
                    "max": float(coaster_stats["max_length_ft"] or 10000),
                    "step": 100,
                    "unit": "feet",
                },
                "speed_mph": {
                    "min": float(coaster_stats["min_speed_mph"] or 0),
                    "max": float(coaster_stats["max_speed_mph"] or 150),
                    "step": 5,
                    "unit": "mph",
                },
                "inversions": {
                    "min": coaster_stats["min_inversions"] or 0,
                    "max": coaster_stats["max_inversions"] or 20,
                    "step": 1,
                    "unit": "inversions",
                },
            },
            "total_count": Ride.objects.count(),
        }

    def _get_category_label(self, category: str) -> str:
        """Convert category code to human-readable label."""
        category_labels = {
            "RC": "Roller Coaster",
            "DR": "Dark Ride",
            "FR": "Flat Ride",
            "WR": "Water Ride",
            "TR": "Transport Ride",
            "OT": "Other",
        }
        if category in category_labels:
            return category_labels[category]
        else:
            raise ValueError(f"Unknown ride category: {category}")

    def _get_status_label(self, status: str) -> str:
        """Convert status code to human-readable label."""
        status_labels = {
            "OPERATING": "Operating",
            "CLOSED_TEMP": "Temporarily Closed",
            "SBNO": "Standing But Not Operating",
            "CLOSING": "Closing Soon",
            "CLOSED_PERM": "Permanently Closed",
            "UNDER_CONSTRUCTION": "Under Construction",
            "DEMOLISHED": "Demolished",
            "RELOCATED": "Relocated",
        }
        if status in status_labels:
            return status_labels[status]
        else:
            raise ValueError(f"Unknown ride status: {status}")

    def _get_rc_type_label(self, rc_type: str) -> str:
        """Convert roller coaster type to human-readable label."""
        rc_type_labels = {
            "SITDOWN": "Sit Down",
            "INVERTED": "Inverted",
            "SUSPENDED": "Suspended",
            "FLOORLESS": "Floorless",
            "FLYING": "Flying",
            "WING": "Wing",
            "DIVE": "Dive",
            "SPINNING": "Spinning",
            "WILD_MOUSE": "Wild Mouse",
            "BOBSLED": "Bobsled",
            "PIPELINE": "Pipeline",
            "FOURTH_DIMENSION": "4th Dimension",
            "FAMILY": "Family",
        }
        if rc_type in rc_type_labels:
            return rc_type_labels[rc_type]
        else:
            raise ValueError(f"Unknown roller coaster type: {rc_type}")

    def _get_track_material_label(self, material: str) -> str:
        """Convert track material to human-readable label."""
        material_labels = {
            "STEEL": "Steel",
            "WOOD": "Wood",
            "HYBRID": "Hybrid (Steel/Wood)",
        }
        if material in material_labels:
            return material_labels[material]
        else:
            raise ValueError(f"Unknown track material: {material}")

    def _get_propulsion_system_label(self, propulsion_system: str) -> str:
        """Convert propulsion system to human-readable label."""
        propulsion_labels = {
            "CHAIN": "Chain Lift",
            "LSM": "Linear Synchronous Motor",
            "LIM": "Linear Induction Motor",
            "HYDRAULIC": "Hydraulic Launch",
            "PNEUMATIC": "Pneumatic Launch",
            "CABLE": "Cable Lift",
            "FLYWHEEL": "Flywheel Launch",
            "GRAVITY": "Gravity",
            "NONE": "No Propulsion System",
        }
        if propulsion_system in propulsion_labels:
            return propulsion_labels[propulsion_system]
        else:
            raise ValueError(f"Unknown propulsion system: {propulsion_system}")
