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

import logging
from typing import TYPE_CHECKING, Any, Optional

from django.core.files.uploadedfile import UploadedFile
from django.db import transaction
from django.db.models import Q

if TYPE_CHECKING:
    from django.contrib.auth.models import AbstractUser

from ..models import Park, ParkArea, ParkPhoto
from ..models.location import ParkLocation
from .location_service import ParkLocationService

logger = logging.getLogger(__name__)


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: Optional["AbstractUser"] = 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 apps.parks.models import Company

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

            if property_owner_id:
                from apps.parks.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: Optional["AbstractUser"] = 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: Optional["AbstractUser"] = 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.status = "DEMOLISHED"

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

            return True

    @staticmethod
    def create_park_area(
        *,
        park_id: int,
        name: str,
        description: str = "",
        created_by: Optional["AbstractUser"] = 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.parks.models import ParkReview
        from apps.rides.models import Ride

        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

    @staticmethod
    def create_park_with_moderation(
        *,
        changes: dict[str, Any],
        submitter: "AbstractUser",
        reason: str = "",
        source: str = "",
    ) -> dict[str, Any]:
        """
        Create a park through the moderation system.

        Args:
            changes: Dictionary of park data
            submitter: User submitting the park
            reason: Reason for submission
            source: Source of information

        Returns:
            Dictionary with status and created object (if auto-approved)
        """
        from apps.moderation.services import ModerationService

        return ModerationService.create_edit_submission_with_queue(
            content_object=None,
            changes=changes,
            submitter=submitter,
            submission_type="CREATE",
            reason=reason,
            source=source,
        )

    @staticmethod
    def update_park_with_moderation(
        *,
        park: Park,
        changes: dict[str, Any],
        submitter: "AbstractUser",
        reason: str = "",
        source: str = "",
    ) -> dict[str, Any]:
        """
        Update a park through the moderation system.

        Args:
            park: Park instance to update
            changes: Dictionary of changes
            submitter: User submitting the update
            reason: Reason for submission
            source: Source of information

        Returns:
            Dictionary with status and updated object (if auto-approved)
        """
        from apps.moderation.services import ModerationService

        return ModerationService.create_edit_submission_with_queue(
            content_object=park,
            changes=changes,
            submitter=submitter,
            submission_type="EDIT",
            reason=reason,
            source=source,
        )

    @staticmethod
    def create_or_update_location(
        *,
        park: Park,
        latitude: float | None,
        longitude: float | None,
        street_address: str = "",
        city: str = "",
        state: str = "",
        country: str = "USA",
        postal_code: str = "",
    ) -> ParkLocation | None:
        """
        Create or update a park's location.

        Args:
            park: Park instance
            latitude: Latitude coordinate
            longitude: Longitude coordinate
            street_address: Street address
            city: City name
            state: State/region
            country: Country (default: USA)
            postal_code: Postal/ZIP code

        Returns:
            ParkLocation instance or None if no coordinates provided
        """
        if not latitude or not longitude:
            return None

        try:
            park_location = park.location
            # Update existing location
            park_location.street_address = street_address
            park_location.city = city
            park_location.state = state
            park_location.country = country or "USA"
            park_location.postal_code = postal_code
            park_location.set_coordinates(float(latitude), float(longitude))
            park_location.save()
            return park_location
        except ParkLocation.DoesNotExist:
            # Create new location
            park_location = ParkLocation.objects.create(
                park=park,
                street_address=street_address,
                city=city,
                state=state,
                country=country or "USA",
                postal_code=postal_code,
            )
            park_location.set_coordinates(float(latitude), float(longitude))
            park_location.save()
            return park_location

    @staticmethod
    def upload_photos(
        *,
        park: Park,
        photos: list[UploadedFile],
        uploaded_by: "AbstractUser",
    ) -> dict[str, Any]:
        """
        Upload multiple photos for a park.

        Args:
            park: Park instance
            photos: List of uploaded photo files
            uploaded_by: User uploading the photos

        Returns:
            Dictionary with uploaded_count and errors list
        """

        uploaded_count = 0
        errors: list[str] = []

        for photo_file in photos:
            try:
                ParkPhoto.objects.create(
                    image=photo_file,
                    uploaded_by=uploaded_by,
                    park=park,
                )
                uploaded_count += 1
            except Exception as e:
                error_msg = f"Error uploading photo {photo_file.name}: {str(e)}"
                errors.append(error_msg)
                logger.warning(error_msg)

        return {
            "uploaded_count": uploaded_count,
            "errors": errors,
        }

    @staticmethod
    def handle_park_creation_result(
        *,
        result: dict[str, Any],
        form_data: dict[str, Any],
        photos: list[UploadedFile],
        user: "AbstractUser",
    ) -> dict[str, Any]:
        """
        Handle the result of park creation through moderation.

        Args:
            result: Result from create_park_with_moderation
            form_data: Cleaned form data containing location info
            photos: List of uploaded photo files
            user: User who submitted

        Returns:
            Dictionary with status, park (if created), uploaded_count, and errors
        """
        response: dict[str, Any] = {
            "status": result["status"],
            "park": None,
            "uploaded_count": 0,
            "errors": [],
        }

        if result["status"] == "auto_approved":
            park = result["created_object"]
            response["park"] = park

            # Create location
            ParkService.create_or_update_location(
                park=park,
                latitude=form_data.get("latitude"),
                longitude=form_data.get("longitude"),
                street_address=form_data.get("street_address", ""),
                city=form_data.get("city", ""),
                state=form_data.get("state", ""),
                country=form_data.get("country", "USA"),
                postal_code=form_data.get("postal_code", ""),
            )

            # Upload photos
            if photos:
                photo_result = ParkService.upload_photos(
                    park=park,
                    photos=photos,
                    uploaded_by=user,
                )
                response["uploaded_count"] = photo_result["uploaded_count"]
                response["errors"] = photo_result["errors"]

        elif result["status"] == "failed":
            response["message"] = result.get("message", "Creation failed")

        return response

    @staticmethod
    def handle_park_update_result(
        *,
        result: dict[str, Any],
        park: Park,
        form_data: dict[str, Any],
        photos: list[UploadedFile],
        user: "AbstractUser",
    ) -> dict[str, Any]:
        """
        Handle the result of park update through moderation.

        Args:
            result: Result from update_park_with_moderation
            park: Original park instance (for queued submissions)
            form_data: Cleaned form data containing location info
            photos: List of uploaded photo files
            user: User who submitted

        Returns:
            Dictionary with status, park, uploaded_count, and errors
        """
        response: dict[str, Any] = {
            "status": result["status"],
            "park": park,
            "uploaded_count": 0,
            "errors": [],
        }

        if result["status"] == "auto_approved":
            updated_park = result["created_object"]
            response["park"] = updated_park

            # Update location
            ParkService.create_or_update_location(
                park=updated_park,
                latitude=form_data.get("latitude"),
                longitude=form_data.get("longitude"),
                street_address=form_data.get("street_address", ""),
                city=form_data.get("city", ""),
                state=form_data.get("state", ""),
                country=form_data.get("country", ""),
                postal_code=form_data.get("postal_code", ""),
            )

            # Upload photos
            if photos:
                photo_result = ParkService.upload_photos(
                    park=updated_park,
                    photos=photos,
                    uploaded_by=user,
                )
                response["uploaded_count"] = photo_result["uploaded_count"]
                response["errors"] = photo_result["errors"]

        elif result["status"] == "failed":
            response["message"] = result.get("message", "Update failed")

        return response
