"""
Services for park-related business logic.
Following Django styleguide pattern for business logic encapsulation.
"""

from typing import Any

from django.contrib.auth import get_user_model
from django.contrib.auth.models import AbstractBaseUser
from django.db import transaction
from django.db.models import Q

from .models import Park, ParkArea
from .services.location_service import ParkLocationService

# Use AbstractBaseUser for type hinting
UserType = AbstractBaseUser
User = get_user_model()


class ParkService:
    """Service for managing park operations."""

    @staticmethod
    def create_park(
        *,
        name: str,
        description: str = "",
        status: str = "OPERATING",
        operator_id: int | None = None,
        property_owner_id: int | None = None,
        opening_date: str | None = None,
        closing_date: str | None = None,
        operating_season: str = "",
        size_acres: float | None = None,
        website: str = "",
        location_data: dict[str, Any] | None = None,
        created_by: UserType | None = None,
    ) -> Park:
        """
        Create a new park with validation and location handling.

        Args:
            name: Park name
            description: Park description
            status: Operating status
            operator_id: ID of operating company
            property_owner_id: ID of property owner company
            opening_date: Opening date
            closing_date: Closing date
            operating_season: Operating season description
            size_acres: Park size in acres
            website: Park website URL
            location_data: Dictionary containing location information
            created_by: User creating the park

        Returns:
            Created Park instance

        Raises:
            ValidationError: If park data is invalid
        """
        with transaction.atomic():
            # Create park instance
            park = Park(
                name=name,
                description=description,
                status=status,
                opening_date=opening_date,
                closing_date=closing_date,
                operating_season=operating_season,
                size_acres=size_acres,
                website=website,
            )

            # Set foreign key relationships if provided
            if operator_id:
                from .models import Company

                park.operator = Company.objects.get(id=operator_id)

            if property_owner_id:
                from .models import Company

                park.property_owner = Company.objects.get(id=property_owner_id)

            # CRITICAL STYLEGUIDE FIX: Call full_clean before save
            park.full_clean()
            park.save()

            # Handle location if provided
            if location_data:
                ParkLocationService.create_park_location(park=park, **location_data)

            return park

    @staticmethod
    def update_park(
        *,
        park_id: int,
        updates: dict[str, Any],
        updated_by: UserType | None = None,
    ) -> Park:
        """
        Update an existing park with validation.

        Args:
            park_id: ID of park to update
            updates: Dictionary of field updates
            updated_by: User performing the update

        Returns:
            Updated Park instance

        Raises:
            Park.DoesNotExist: If park doesn't exist
            ValidationError: If update data is invalid
        """
        with transaction.atomic():
            park = Park.objects.select_for_update().get(id=park_id)

            # Apply updates
            for field, value in updates.items():
                if hasattr(park, field):
                    setattr(park, field, value)

            # CRITICAL STYLEGUIDE FIX: Call full_clean before save
            park.full_clean()
            park.save()

            return park

    @staticmethod
    def delete_park(*, park_id: int, deleted_by: UserType | None = None) -> bool:
        """
        Soft delete a park by setting status to DEMOLISHED.

        Args:
            park_id: ID of park to delete
            deleted_by: User performing the deletion

        Returns:
            True if successfully deleted

        Raises:
            Park.DoesNotExist: If park doesn't exist
        """
        with transaction.atomic():
            park = Park.objects.select_for_update().get(id=park_id)
            park.demolish(user=deleted_by)

            return True

    @staticmethod
    def create_park_area(
        *,
        park_id: int,
        name: str,
        description: str = "",
        created_by: UserType | None = None,
    ) -> ParkArea:
        """
        Create a new area within a park.

        Args:
            park_id: ID of the parent park
            name: Area name
            description: Area description
            created_by: User creating the area

        Returns:
            Created ParkArea instance

        Raises:
            Park.DoesNotExist: If park doesn't exist
            ValidationError: If area data is invalid
        """
        park = Park.objects.get(id=park_id)

        area = ParkArea(park=park, name=name, description=description)

        # CRITICAL STYLEGUIDE FIX: Call full_clean before save
        area.full_clean()
        area.save()

        return area

    @staticmethod
    def update_park_statistics(*, park_id: int) -> Park:
        """
        Recalculate and update park statistics (ride counts, ratings).

        Args:
            park_id: ID of park to update statistics for

        Returns:
            Updated Park instance with fresh statistics
        """
        from django.db.models import Avg, Count

        from apps.rides.models import Ride

        from .models import ParkReview

        with transaction.atomic():
            park = Park.objects.select_for_update().get(id=park_id)

            # Calculate ride counts
            ride_stats = Ride.objects.filter(park=park).aggregate(
                total_rides=Count("id"),
                coaster_count=Count("id", filter=Q(category__in=["RC", "WC"])),
            )

            # Calculate average rating
            avg_rating = ParkReview.objects.filter(park=park, is_published=True).aggregate(avg_rating=Avg("rating"))[
                "avg_rating"
            ]

            # Update park fields
            park.ride_count = ride_stats["total_rides"] or 0
            park.coaster_count = ride_stats["coaster_count"] or 0
            park.average_rating = avg_rating

            # CRITICAL STYLEGUIDE FIX: Call full_clean before save
            park.full_clean()
            park.save()

            return park
