"""
Views for ride functionality.

This module contains views for managing ride resources including
CRUD operations, search, filtering, and HTMX partial rendering.

View Types:
    CBVs:
        - RideDetailView: Display ride details
        - RideCreateView: Create new ride
        - RideUpdateView: Update existing ride
        - RideListView: List rides with filtering
        - RideSearchView: Search rides with HTMX support
        - RideRankingsView: Display ride rankings
        - RideRankingDetailView: Display ranking details
        - ManufacturerListView: List manufacturers
        - DesignerListView: List designers
        - SingleCategoryListView: List rides by category

    FBVs (HTMX Partials):
        - show_coaster_fields: Toggle coaster-specific fields
        - ride_status_actions: FSM status actions for moderators
        - ride_header_badge: Status badge partial
        - search_companies: Company search autocomplete
        - search_ride_models: Ride model search autocomplete
        - get_search_suggestions: Smart search suggestions
        - ranking_history_chart: Ranking history chart data
        - ranking_comparisons: Head-to-head comparisons

Dependencies:
    - Services: apps.rides.services
    - Models: apps.rides.models
    - Forms: apps.rides.forms

Code Quality:
    - PEP 8 compliant (verified with black, flake8, ruff)
    - Maximum line length: 88 characters
    - Maximum complexity: 10 (McCabe)
"""

import logging

from django.contrib.auth.mixins import LoginRequiredMixin
from django.db.models import Count, Q
from django.http import Http404, HttpRequest, HttpResponse
from django.shortcuts import get_object_or_404, render
from django.urls import reverse
from django.views.generic import CreateView, DetailView, ListView, UpdateView

from apps.core.logging import log_business_event, log_exception
from apps.moderation.mixins import EditSubmissionMixin, HistoryMixin
from apps.parks.models import Park

from .forms import RideForm, RideSearchForm
from .forms.search import MasterFilterForm
from .mixins import RideFormMixin
from .models.company import Company
from .models.rankings import RankingSnapshot, RideRanking
from .models.rides import Ride, RideModel
from .services.ranking_service import RideRankingService

logger = logging.getLogger(__name__)


class ParkContextRequired:
    """
    Mixin to require park context for views.

    Ensures that the view has access to a park_slug URL parameter.
    Raises Http404 if park context is not available.
    """

    def dispatch(self, request, *args, **kwargs):
        """Check for park context before dispatching to handler."""
        if "park_slug" not in self.kwargs:
            raise Http404("Park context is required")
        return super().dispatch(request, *args, **kwargs)


def show_coaster_fields(request: HttpRequest) -> HttpResponse:
    """
    Show roller coaster specific fields based on category selection.

    View Type: FBV (HTMX Partial)
    URL Pattern: /rides/coaster-fields/
    Returns: HTML partial with coaster-specific form fields

    Args:
        request: HTTP request with 'category' query parameter

    Returns:
        Empty response for non-coaster categories,
        or coaster fields partial for roller coasters
    """
    category = request.GET.get("category")
    if category != "RC":  # Only show for roller coasters
        return HttpResponse("")
    return render(request, "rides/partials/coaster_fields.html")


def ride_status_actions(request: HttpRequest, park_slug: str, ride_slug: str) -> HttpResponse:
    """
    Return FSM status actions for ride moderators.

    View Type: FBV (HTMX Partial)
    URL Pattern: /parks/<park_slug>/rides/<ride_slug>/status-actions/
    Returns: HTML partial with available status transition actions
    Permissions: rides.change_ride

    Args:
        request: HTTP request
        park_slug: Slug of the park
        ride_slug: Slug of the ride

    Returns:
        Empty response for non-moderators, or status actions partial
    """
    park = get_object_or_404(Park, slug=park_slug)
    ride = get_object_or_404(Ride, park=park, slug=ride_slug)

    # Only show to moderators
    if not request.user.has_perm("rides.change_ride"):
        return HttpResponse("")

    return render(
        request,
        "rides/partials/ride_status_actions.html",
        {"ride": ride, "park": park, "user": request.user},
    )


def ride_header_badge(request: HttpRequest, park_slug: str, ride_slug: str) -> HttpResponse:
    """
    Return the header status badge partial for a ride.

    View Type: FBV (HTMX Partial)
    URL Pattern: /parks/<park_slug>/rides/<ride_slug>/header-badge/
    Returns: HTML partial with ride status badge

    Args:
        request: HTTP request
        park_slug: Slug of the park
        ride_slug: Slug of the ride

    Returns:
        Rendered status badge partial
    """
    park = get_object_or_404(Park, slug=park_slug)
    ride = get_object_or_404(Ride, park=park, slug=ride_slug)
    return render(
        request,
        "rides/partials/ride_header_badge.html",
        {"ride": ride, "park": park, "user": request.user},
    )


class RideDetailView(HistoryMixin, DetailView):
    """
    Display ride details with related data.

    View Type: CBV (DetailView)
    URL Pattern: /parks/<park_slug>/rides/<ride_slug>/
    Template: rides/ride_detail.html
    Permissions: Public

    Includes history tracking via HistoryMixin for audit trail display.
    """

    model = Ride
    template_name = "rides/ride_detail.html"
    slug_url_kwarg = "ride_slug"

    def get_queryset(self):
        """Get ride for the specific park with optimized query."""
        queryset = (
            Ride.objects.all()
            .select_related(
                "park",
                "park__location",
                "park_area",
                "manufacturer",
                "designer",
                "ride_model",
                "ride_model__manufacturer",
            )
            .prefetch_related("photos", "coaster_stats")
        )

        if "park_slug" in self.kwargs:
            queryset = queryset.filter(park__slug=self.kwargs["park_slug"])

        return queryset

    def get_context_data(self, **kwargs):
        """Add context data"""
        context = super().get_context_data(**kwargs)
        if "park_slug" in self.kwargs:
            context["park_slug"] = self.kwargs["park_slug"]
            context["park"] = self.object.park

        return context


class RideCreateView(LoginRequiredMixin, ParkContextRequired, RideFormMixin, CreateView):
    """
    View for creating a new ride.

    View Type: CBV (CreateView)
    URL Pattern: /parks/<park_slug>/rides/add/
    Template: rides/ride_form.html
    Permissions: LoginRequired
    """

    model = Ride
    form_class = RideForm
    template_name = "rides/ride_form.html"

    def get_success_url(self):
        """Get URL to redirect to after successful creation."""
        return reverse(
            "parks:rides:ride_detail",
            kwargs={
                "park_slug": self.park.slug,
                "ride_slug": self.object.slug,
            },
        )

    def get_form_kwargs(self):
        """Pass park to the form."""
        kwargs = super().get_form_kwargs()
        self.park = get_object_or_404(Park, slug=self.kwargs["park_slug"])
        kwargs["park"] = self.park
        return kwargs

    def get_context_data(self, **kwargs):
        """Add park and park_slug to context."""
        context = super().get_context_data(**kwargs)
        context["park"] = self.park
        context["park_slug"] = self.park.slug
        context["is_edit"] = False
        return context

    def form_valid(self, form):
        """Handle form submission using RideFormMixin for entity suggestions."""
        self.handle_entity_suggestions(form)
        response = super().form_valid(form)
        log_business_event(
            logger,
            event_type="ride_created",
            message=f"Ride created: {self.object.name}",
            context={
                "ride_id": self.object.id,
                "ride_name": self.object.name,
                "park_id": self.park.id,
                "park_name": self.park.name,
            },
            request=self.request,
        )
        return response


class RideUpdateView(
    LoginRequiredMixin,
    ParkContextRequired,
    RideFormMixin,
    EditSubmissionMixin,
    UpdateView,
):
    """
    View for updating an existing ride.

    View Type: CBV (UpdateView)
    URL Pattern: /parks/<park_slug>/rides/<ride_slug>/edit/
    Template: rides/ride_form.html
    Permissions: LoginRequired
    """

    model = Ride
    form_class = RideForm
    template_name = "rides/ride_form.html"
    slug_url_kwarg = "ride_slug"

    def get_success_url(self):
        """Get URL to redirect to after successful update."""
        return reverse(
            "parks:rides:ride_detail",
            kwargs={
                "park_slug": self.park.slug,
                "ride_slug": self.object.slug,
            },
        )

    def get_queryset(self):
        """Get ride for the specific park."""
        return Ride.objects.filter(park__slug=self.kwargs["park_slug"])

    def get_form_kwargs(self):
        """Pass park to the form."""
        kwargs = super().get_form_kwargs()
        self.park = get_object_or_404(Park, slug=self.kwargs["park_slug"])
        kwargs["park"] = self.park
        return kwargs

    def get_context_data(self, **kwargs):
        """Add park and park_slug to context."""
        context = super().get_context_data(**kwargs)
        context["park"] = self.park
        context["park_slug"] = self.park.slug
        context["is_edit"] = True
        return context

    def form_valid(self, form):
        """Handle form submission using RideFormMixin for entity suggestions."""
        self.handle_entity_suggestions(form)
        response = super().form_valid(form)
        log_business_event(
            logger,
            event_type="ride_updated",
            message=f"Ride updated: {self.object.name}",
            context={
                "ride_id": self.object.id,
                "ride_name": self.object.name,
                "park_id": self.park.id,
                "park_name": self.park.name,
            },
            request=self.request,
        )
        return response


class RideListView(ListView):
    """Enhanced view for displaying a list of rides with advanced filtering"""

    model = Ride
    template_name = "rides/ride_list.html"
    context_object_name = "rides"
    paginate_by = 24

    def get_queryset(self):
        """Get filtered rides using the advanced search service."""
        # Apply park context if available
        park = None
        if "park_slug" in self.kwargs:
            self.park = get_object_or_404(Park, slug=self.kwargs["park_slug"])
            park = self.park

        # For now, use a simpler approach until we can properly integrate the search service
        queryset = (
            Ride.objects.all()
            .select_related("park", "ride_model", "ride_model__manufacturer")
            .prefetch_related("photos")
        )

        if park:
            queryset = queryset.filter(park=park)

        # Apply basic search if provided
        search_query = self.request.GET.get("search", "").strip()
        if search_query:
            queryset = queryset.filter(name__icontains=search_query)

        return queryset

    def get_template_names(self):
        """Return appropriate template based on request type"""
        if hasattr(self.request, "htmx") and self.request.htmx:
            return ["rides/partials/ride_list_results.html"]
        return [self.template_name]

    def get_context_data(self, **kwargs):
        """Add filter form and context data"""
        context = super().get_context_data(**kwargs)

        # Add park context
        if hasattr(self, "park"):
            context["park"] = self.park
            context["park_slug"] = self.kwargs["park_slug"]

        # Add filter form
        filter_form = MasterFilterForm(self.request.GET)
        context["filter_form"] = filter_form
        # Use Rich Choice registry directly
        from apps.core.choices.registry import get_choices

        choices = get_choices("categories", "rides")
        context["category_choices"] = [(choice.value, choice.label) for choice in choices]

        # Add filter summary for display
        if filter_form.is_valid():
            context["active_filters"] = filter_form.get_filter_summary()
            context["has_filters"] = filter_form.has_active_filters()
        else:
            context["active_filters"] = {}
            context["has_filters"] = False

        # Add total count before filtering (for display purposes)
        if hasattr(self, "park"):
            context["total_rides"] = Ride.objects.filter(park=self.park).count()
        else:
            context["total_rides"] = Ride.objects.count()

        # Add filtered count
        context["filtered_count"] = self.get_queryset().count()

        return context


class SingleCategoryListView(ListView):
    """View for displaying rides of a specific category"""

    model = Ride
    template_name = "rides/park_category_list.html"
    context_object_name = "rides"

    def get_queryset(self):
        """Get rides filtered by category and optionally by park"""
        category = self.kwargs.get("category")
        queryset = Ride.objects.filter(category=category).select_related(
            "park", "ride_model", "ride_model__manufacturer"
        )

        if "park_slug" in self.kwargs:
            self.park = get_object_or_404(Park, slug=self.kwargs["park_slug"])
            queryset = queryset.filter(park=self.park)

        return queryset

    def get_context_data(self, **kwargs):
        """Add park and category information to context"""
        context = super().get_context_data(**kwargs)
        if hasattr(self, "park"):
            context["park"] = self.park
            context["park_slug"] = self.kwargs["park_slug"]
        # Find the category choice by value using Rich Choice registry
        from apps.core.choices.registry import get_choices

        choices = get_choices("categories", "rides")
        category_choice = next(
            (choice for choice in choices if choice.value == self.kwargs["category"]),
            None,
        )
        context["category"] = category_choice.label if category_choice else "Unknown"
        return context


# Alias for parks app to maintain backward compatibility
ParkSingleCategoryListView = SingleCategoryListView


def search_companies(request: HttpRequest) -> HttpResponse:
    """Search companies and return results for HTMX"""
    query = request.GET.get("q", "").strip()
    role = request.GET.get("role", "").upper()

    companies = Company.objects.only("id", "name", "slug", "roles").order_by("name")
    if role:
        companies = companies.filter(roles__contains=[role])
    if query:
        companies = companies.filter(name__icontains=query)
    companies = companies[:10]

    return render(
        request,
        "rides/partials/company_search_results.html",
        {"companies": companies, "search_term": query},
    )


def search_ride_models(request: HttpRequest) -> HttpResponse:
    """Search ride models and return results for HTMX"""
    query = request.GET.get("q", "").strip()
    manufacturer_id = request.GET.get("manufacturer")

    # Show all ride models on click, filter on input
    ride_models = RideModel.objects.select_related("manufacturer").order_by("name")
    if query:
        ride_models = ride_models.filter(name__icontains=query)
    if manufacturer_id:
        ride_models = ride_models.filter(manufacturer_id=manufacturer_id)
    ride_models = ride_models[:10]

    return render(
        request,
        "rides/partials/ride_model_search_results.html",
        {
            "ride_models": ride_models,
            "search_term": query,
            "manufacturer_id": manufacturer_id,
        },
    )


def get_search_suggestions(request: HttpRequest) -> HttpResponse:
    """Get smart search suggestions for rides

    Returns suggestions including:
    - Common matching ride names
    - Matching parks
    - Matching categories
    """
    query = request.GET.get("q", "").strip().lower()
    suggestions = []

    if query:
        # Get common ride names
        matching_names = (
            Ride.objects.filter(name__icontains=query).values("name").annotate(count=Count("id")).order_by("-count")[:3]
        )

        for match in matching_names:
            suggestions.append(
                {
                    "type": "ride",
                    "text": match["name"],
                    "count": match["count"],
                }
            )

        # Get matching parks with optimized query
        matching_parks = Park.objects.select_related("location").filter(
            Q(name__icontains=query) | Q(location__city__icontains=query)
        )[:3]

        for park in matching_parks:
            suggestions.append(
                {
                    "type": "park",
                    "text": park.name,
                    "location": park.location.city if park.location else None,
                }
            )

        # Add category matches
        from apps.core.choices.registry import get_choices

        choices = get_choices("categories", "rides")
        for choice in choices:
            if query in choice.label.lower():
                ride_count = Ride.objects.filter(category=choice.value).count()
                suggestions.append(
                    {
                        "type": "category",
                        "code": choice.value,
                        "text": choice.label,
                        "count": ride_count,
                    }
                )

    return render(
        request,
        "rides/partials/search_suggestions.html",
        {"suggestions": suggestions, "query": query},
    )


class RideSearchView(ListView):
    """View for ride search functionality with HTMX support."""

    model = Ride
    template_name = "search/partials/ride_search_results.html"
    context_object_name = "rides"
    paginate_by = 20

    def get_queryset(self):
        """Get filtered rides based on search form."""
        queryset = Ride.objects.select_related("park").order_by("name")

        # Process search form
        form = RideSearchForm(self.request.GET)
        search_term = self.request.GET.get("ride", "").strip()
        if form.is_valid():
            ride = form.cleaned_data.get("ride")
            if ride:
                # If specific ride selected, return just that ride
                queryset = queryset.filter(id=ride.id)
            else:
                # If no specific ride, filter by search term
                if search_term:
                    queryset = queryset.filter(name__icontains=search_term)

        result_count = queryset.count()
        logger.info(
            "Ride search executed",
            extra={
                "query": search_term,
                "result_count": result_count,
            },
        )
        return queryset

    def get_template_names(self):
        """Return appropriate template based on request type."""
        if self.request.htmx:
            return ["search/partials/ride_search_results.html"]
        return ["search/ride_search.html"]

    def get_context_data(self, **kwargs):
        """Add search form to context."""
        context = super().get_context_data(**kwargs)
        context["search_form"] = RideSearchForm(self.request.GET)
        return context


class RideRankingsView(ListView):
    """View for displaying ride rankings using the Internet Roller Coaster Poll."""

    model = RideRanking
    template_name = "rides/rankings.html"
    context_object_name = "rankings"
    paginate_by = 50

    def get_queryset(self):
        """Get rankings with optimized queries."""
        queryset = RideRanking.objects.select_related(
            "ride", "ride__park", "ride__manufacturer", "ride__ride_model"
        ).order_by("rank")

        # Filter by category if specified
        category = self.request.GET.get("category")
        if category and category != "all":
            queryset = queryset.filter(ride__category=category)

        # Filter by minimum mutual riders
        min_riders = self.request.GET.get("min_riders")
        if min_riders:
            try:
                min_riders_int = int(min_riders)
                queryset = queryset.filter(mutual_riders_count__gte=min_riders_int)
            except (ValueError, TypeError) as e:
                log_exception(
                    logger,
                    e,
                    context={
                        "operation": "ride_rankings_min_riders",
                        "min_riders": min_riders,
                    },
                    request=self.request,
                )

        return queryset

    def get_template_names(self):
        """Return appropriate template based on request type."""
        if self.request.htmx:
            return ["rides/partials/rankings_table.html"]
        return [self.template_name]

    def get_context_data(self, **kwargs):
        """Add context for rankings view."""
        context = super().get_context_data(**kwargs)
        # Use Rich Choice registry directly
        from apps.core.choices.registry import get_choices

        choices = get_choices("categories", "rides")
        context["category_choices"] = [(choice.value, choice.label) for choice in choices]
        context["selected_category"] = self.request.GET.get("category", "all")
        context["min_riders"] = self.request.GET.get("min_riders", "")

        # Add statistics
        if self.object_list:
            context["total_ranked"] = RideRanking.objects.count()
            context["last_updated"] = self.object_list[0].last_calculated if self.object_list else None

        return context


class RideRankingDetailView(DetailView):
    """View for displaying detailed ranking information for a specific ride."""

    model = Ride
    template_name = "rides/ranking_detail.html"
    slug_url_kwarg = "ride_slug"

    def get_queryset(self):
        """Get ride with ranking data."""
        return Ride.objects.select_related("park", "manufacturer", "ranking").prefetch_related(
            "comparisons_as_a", "comparisons_as_b", "ranking_history"
        )

    def get_context_data(self, **kwargs):
        """Add ranking details to context."""
        context = super().get_context_data(**kwargs)

        # Get ranking details from service
        service = RideRankingService()
        ranking_details = service.get_ride_ranking_details(self.object)

        if ranking_details:
            context.update(ranking_details)

            # Get recent movement
            recent_snapshots = RankingSnapshot.objects.filter(ride=self.object).order_by("-snapshot_date")[:7]

            if len(recent_snapshots) >= 2:
                context["rank_change"] = recent_snapshots[0].rank - recent_snapshots[1].rank
                context["previous_rank"] = recent_snapshots[1].rank
        else:
            context["not_ranked"] = True

        return context


def ranking_history_chart(request: HttpRequest, ride_slug: str) -> HttpResponse:
    """HTMX endpoint for ranking history chart data."""
    ride = get_object_or_404(Ride, slug=ride_slug)

    # Get last 30 days of ranking history
    history = RankingSnapshot.objects.filter(ride=ride).order_by("-snapshot_date")[:30]

    # Prepare data for chart
    chart_data = [
        {
            "date": snapshot.snapshot_date.isoformat(),
            "rank": snapshot.rank,
            "win_pct": float(snapshot.winning_percentage) * 100,
        }
        for snapshot in reversed(history)
    ]

    return render(
        request,
        "rides/partials/ranking_chart.html",
        {"chart_data": chart_data, "ride": ride},
    )


def ranking_comparisons(request: HttpRequest, ride_slug: str) -> HttpResponse:
    """HTMX endpoint for ride head-to-head comparisons."""
    ride = get_object_or_404(Ride, slug=ride_slug)

    # Get head-to-head comparisons
    from django.db.models import Q

    from .models.rankings import RidePairComparison

    comparisons = (
        RidePairComparison.objects.filter(Q(ride_a=ride) | Q(ride_b=ride))
        .select_related("ride_a", "ride_b", "ride_a__park", "ride_b__park")
        .order_by("-mutual_riders_count")[:20]
    )

    # Format comparisons for display
    comparison_data = []
    for comp in comparisons:
        if comp.ride_a == ride:
            opponent = comp.ride_b
            wins = comp.ride_a_wins
            losses = comp.ride_b_wins
        else:
            opponent = comp.ride_a
            wins = comp.ride_b_wins
            losses = comp.ride_a_wins

        result = "win" if wins > losses else "loss" if losses > wins else "tie"

        comparison_data.append(
            {
                "opponent": opponent,
                "wins": wins,
                "losses": losses,
                "ties": comp.ties,
                "result": result,
                "mutual_riders": comp.mutual_riders_count,
            }
        )

    return render(
        request,
        "rides/partials/ranking_comparisons.html",
        {"comparisons": comparison_data, "ride": ride},
    )


class ManufacturerListView(ListView):
    """View for displaying a list of ride manufacturers"""

    model = Company
    template_name = "manufacturers/manufacturer_list.html"
    context_object_name = "manufacturers"
    paginate_by = 24

    def get_queryset(self):
        """Get companies that are manufacturers with optimized query"""
        return (
            Company.objects.filter(roles__contains=["MANUFACTURER"])
            .annotate(ride_count=Count("manufactured_rides"))
            .only("id", "name", "slug", "roles", "description")
            .order_by("name")
        )

    def get_context_data(self, **kwargs):
        """Add context data"""
        context = super().get_context_data(**kwargs)
        context["total_manufacturers"] = self.get_queryset().count()
        return context


class DesignerListView(ListView):
    """View for displaying a list of ride designers"""

    model = Company
    template_name = "designers/designer_list.html"
    context_object_name = "designers"
    paginate_by = 24

    def get_queryset(self):
        """Get companies that are designers with optimized query"""
        return (
            Company.objects.filter(roles__contains=["DESIGNER"])
            .annotate(ride_count=Count("designed_rides"))
            .only("id", "name", "slug", "roles", "description")
            .order_by("name")
        )

    def get_context_data(self, **kwargs):
        """Add context data"""
        context = super().get_context_data(**kwargs)
        context["total_designers"] = self.get_queryset().count()
        return context
