"""
Core views for the application.
"""

import json
import logging
from typing import Any

from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist
from django.db.models import Model
from django.http import HttpRequest, HttpResponse, JsonResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse
from django.utils.decorators import method_decorator
from django.views import View
from django.views.decorators.csrf import csrf_protect
from django.views.generic import DetailView, TemplateView
from django_fsm import TransitionNotAllowed, can_proceed

from apps.core.state_machine.exceptions import (
    TransitionNotAvailable,
    TransitionPermissionDenied,
    TransitionValidationError,
    format_transition_error,
)

logger = logging.getLogger(__name__)


class SlugRedirectMixin(View):
    """
    Mixin that handles redirects for old slugs.
    Requires the model to inherit from SluggedModel and view to inherit from DetailView.
    """

    model: type[Model] | None = None
    slug_url_kwarg: str = "slug"
    object: Model | None = None

    def dispatch(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse:
        # Only apply slug redirect logic to DetailViews
        if not isinstance(self, DetailView):
            return super().dispatch(request, *args, **kwargs)

        # Get the object using current or historical slug
        try:
            self.object = self.get_object()  # type: ignore
            # Check if we used an old slug
            current_slug = kwargs.get(self.slug_url_kwarg)
            if current_slug and current_slug != getattr(self.object, "slug", None):
                # Get the URL pattern name from the view
                url_pattern = self.get_redirect_url_pattern()
                # Build kwargs for reverse()
                reverse_kwargs = self.get_redirect_url_kwargs()
                # Redirect to the current slug URL
                return redirect(reverse(url_pattern, kwargs=reverse_kwargs), permanent=True)
            return super().dispatch(request, *args, **kwargs)
        except Exception:  # pylint: disable=broad-exception-caught
            # Fallback to default dispatch on any error (e.g. object not found)
            return super().dispatch(request, *args, **kwargs)

    def get_redirect_url_pattern(self) -> str:
        """
        Get the URL pattern name for redirects.
        Should be overridden by subclasses.
        """
        raise NotImplementedError("Subclasses must implement get_redirect_url_pattern()")

    def get_redirect_url_kwargs(self) -> dict[str, Any]:
        """
        Get the kwargs for reverse() when redirecting.
        Should be overridden by subclasses if they need custom kwargs.
        """
        if not self.object:
            return {}
        return {self.slug_url_kwarg: getattr(self.object, "slug", "")}


class GlobalSearchView(TemplateView):
    """Unified search view with HTMX support for debounced results and suggestions."""

    template_name = "core/search/search.html"

    def get(self, request, *args, **kwargs):
        q = request.GET.get("q", "")
        results = []
        suggestions = []
        # Lightweight placeholder search.
        # Real implementation should query multiple models.
        if q:
            # Return a small payload of mocked results to keep this scaffold safe
            results = [{"title": f"Result for {q}", "url": "#", "subtitle": "Park"}]
            suggestions = [{"text": q, "url": "#"}]

        context = {"results": results, "suggestions": suggestions}

        # If HTMX request, render dropdown partial
        if request.headers.get("HX-Request") == "true":
            return render(request, "core/search/partials/search_dropdown.html", context)

        return render(request, self.template_name, context)


# =============================================================================
# FSM Transition View Infrastructure
# =============================================================================


# Default transition metadata for styling
TRANSITION_METADATA = {
    # Approval transitions
    "approve": {
        "style": "green",
        "icon": "check",
        "requires_confirm": True,
        "confirm_message": "Are you sure you want to approve this?",
    },
    "transition_to_approved": {
        "style": "green",
        "icon": "check",
        "requires_confirm": True,
        "confirm_message": "Are you sure you want to approve this?",
    },
    # Rejection transitions
    "reject": {
        "style": "red",
        "icon": "times",
        "requires_confirm": True,
        "confirm_message": "Are you sure you want to reject this?",
    },
    "transition_to_rejected": {
        "style": "red",
        "icon": "times",
        "requires_confirm": True,
        "confirm_message": "Are you sure you want to reject this?",
    },
    # Escalation transitions
    "escalate": {
        "style": "yellow",
        "icon": "arrow-up",
        "requires_confirm": True,
        "confirm_message": "Are you sure you want to escalate this?",
    },
    "transition_to_escalated": {
        "style": "yellow",
        "icon": "arrow-up",
        "requires_confirm": True,
        "confirm_message": "Are you sure you want to escalate this?",
    },
    # Assignment transitions
    "assign": {"style": "blue", "icon": "user-plus", "requires_confirm": False},
    "unassign": {"style": "gray", "icon": "user-minus", "requires_confirm": False},
    # Status transitions
    "start": {"style": "blue", "icon": "play", "requires_confirm": False},
    "complete": {
        "style": "green",
        "icon": "check-circle",
        "requires_confirm": True,
        "confirm_message": "Are you sure you want to complete this?",
    },
    "cancel": {
        "style": "red",
        "icon": "ban",
        "requires_confirm": True,
        "confirm_message": "Are you sure you want to cancel this?",
    },
    "reopen": {"style": "blue", "icon": "redo", "requires_confirm": False},
    # Resolution transitions
    "resolve": {
        "style": "green",
        "icon": "check-double",
        "requires_confirm": True,
        "confirm_message": "Are you sure you want to resolve this?",
    },
    "dismiss": {
        "style": "gray",
        "icon": "times-circle",
        "requires_confirm": True,
        "confirm_message": "Are you sure you want to dismiss this?",
    },
    # Default
    "default": {"style": "gray", "icon": "arrow-right", "requires_confirm": False},
}


def get_transition_metadata(transition_name: str) -> dict[str, Any]:
    """Get metadata for a transition by name."""
    # Check for exact match first
    if transition_name in TRANSITION_METADATA:
        return TRANSITION_METADATA[transition_name].copy()

    # Check for partial match (e.g., "transition_to_approved" contains "approve")
    for key, metadata in TRANSITION_METADATA.items():
        if key in transition_name.lower() or transition_name.lower() in key:
            return metadata.copy()

    return TRANSITION_METADATA["default"].copy()


def add_toast_trigger(response: HttpResponse, message: str, toast_type: str = "success") -> HttpResponse:
    """
    Add HX-Trigger header to trigger Alpine.js toast.

    Args:
        response: The HTTP response to modify
        message: Toast message to display
        toast_type: Type of toast ('success', 'error', 'warning', 'info')

    Returns:
        Modified response with HX-Trigger header
    """
    trigger_data = {"showToast": {"message": message, "type": toast_type}}
    response["HX-Trigger"] = json.dumps(trigger_data)
    return response


@method_decorator(csrf_protect, name="dispatch")
class FSMTransitionView(View):
    """
    Generic view for handling FSM state transitions via HTMX.

    This view handles POST requests to execute FSM transitions on any model
    that uses django-fsm. It validates permissions, executes the transition,
    and returns either an updated HTML partial (for HTMX) or JSON response.

    URL pattern should provide:
        - app_label: The app containing the model
        - model_name: The model name (lowercase)
        - pk: The primary key of the object
        - transition_name: The name of the transition method to execute

    Example URL patterns:
        path('fsm/<str:app_label>/<str:model_name>/<int:pk>/transition/<str:transition_name>/',
             FSMTransitionView.as_view(), name='fsm_transition')
    """

    # Override these in subclasses or pass via URL kwargs
    partial_template = None  # Template to render after successful transition

    def get_model_class(self, app_label: str, model_name: str) -> type[Model] | None:
        """
        Get the model class from app_label and model_name.

        Args:
            app_label: The Django app label (e.g., 'moderation')
            model_name: The model name in lowercase (e.g., 'editsubmission')

        Returns:
            The model class or None if not found
        """
        try:
            content_type = ContentType.objects.get(app_label=app_label, model=model_name)
            return content_type.model_class()
        except ContentType.DoesNotExist:
            return None

    def get_object(self, model_class: type[Model], pk: Any, slug: str | None = None) -> Model:
        """
        Get the model instance.

        Args:
            model_class: The model class
            pk: Primary key of the object (can be int or slug)
            slug: Optional slug if using slug-based lookup

        Returns:
            The model instance

        Raises:
            Http404: If object not found
        """
        if slug:
            return get_object_or_404(model_class, slug=slug)
        return get_object_or_404(model_class, pk=pk)

    def get_transition_method(self, obj: Model, transition_name: str):
        """
        Get the transition method from the object.

        Args:
            obj: The model instance
            transition_name: The name of the transition method

        Returns:
            The transition method or None
        """
        return getattr(obj, transition_name, None)

    def validate_transition(self, obj: Model, transition_name: str, user) -> tuple[bool, str | None]:
        """
        Validate that the transition can proceed.

        Args:
            obj: The model instance
            transition_name: The name of the transition method
            user: The user attempting the transition

        Returns:
            Tuple of (can_proceed, error_message)
        """
        method = self.get_transition_method(obj, transition_name)

        if method is None:
            return (
                False,
                f"Transition '{transition_name}' not found on {obj.__class__.__name__}",
            )

        if not callable(method):
            return False, f"'{transition_name}' is not a callable method"

        # Check if the transition can proceed
        if not can_proceed(method, user):
            return (
                False,
                f"Transition '{transition_name}' is not allowed from current state",
            )

        return True, None

    def execute_transition(self, obj: Model, transition_name: str, user, **kwargs) -> None:
        """
        Execute the transition on the object.

        Args:
            obj: The model instance
            transition_name: The name of the transition method
            user: The user performing the transition
            **kwargs: Additional arguments to pass to the transition method

        Raises:
            TransitionNotAllowed: If transition fails
        """
        method = self.get_transition_method(obj, transition_name)

        # Execute the transition with user parameter
        method(user=user, **kwargs)
        obj.save()

    def get_success_message(self, obj: Model, transition_name: str) -> str:
        """Generate a success message for the transition."""
        # Clean up transition name for display
        display_name = transition_name.replace("transition_to_", "").replace("_", " ").title()
        model_name = obj._meta.verbose_name.title()
        return f"{model_name} has been {display_name.lower()}d successfully."

    def get_error_message(self, error: Exception) -> str:
        """Generate an error message from an exception."""
        if hasattr(error, "user_message"):
            return error.user_message
        return str(error) or "An error occurred during the transition."

    def get_partial_template(self, obj: Model, request: HttpRequest) -> str | None:
        """
        Get the template to render after a successful transition.

        Override this method to return a custom template based on the object.
        Uses Django's template loader to find model-specific templates.
        """
        if self.partial_template:
            return self.partial_template

        app_label = obj._meta.app_label
        model_name = obj._meta.model_name

        # Special handling for parks and rides - return status section
        if app_label == "parks" and model_name == "park":
            return "parks/partials/park_status_actions.html"
        elif app_label == "rides" and model_name == "ride":
            return "rides/partials/ride_status_actions.html"

        # Check for model-specific templates in order of preference
        possible_templates = [
            f"{app_label}/partials/{model_name}_row.html",
            f"{app_label}/partials/{model_name}_item.html",
            f"{app_label}/partials/{model_name}.html",
            "htmx/updated_row.html",
        ]

        # Use template loader to check if template exists
        from django.template import TemplateDoesNotExist
        from django.template.loader import select_template

        try:
            template = select_template(possible_templates)
            return template.template.name
        except TemplateDoesNotExist:
            return "htmx/updated_row.html"

    def format_success_response(self, request: HttpRequest, obj: Model, transition_name: str) -> HttpResponse:
        """
        Format a successful transition response.

        For HTMX requests: renders the partial template with toast trigger
        For regular requests: returns JSON response
        """
        message = self.get_success_message(obj, transition_name)

        if request.headers.get("HX-Request"):
            # HTMX request - render partial and add toast trigger
            template = self.get_partial_template(obj, request)

            if template:
                # Build context with object and model-specific variable names
                context = {
                    "object": obj,
                    "user": request.user,
                    "transition_success": True,
                    "success_message": message,
                }
                # Add model-specific variable (e.g., 'park' or 'ride') for template compatibility
                model_name = obj._meta.model_name
                context[model_name] = obj

                response = render(request, template, context)
            else:
                # No template - return empty response with OOB swap for status
                response = HttpResponse("")

            return add_toast_trigger(response, message, "success")

        # Regular request - return JSON
        return JsonResponse(
            {
                "success": True,
                "message": message,
                "new_state": (getattr(obj, obj.state_field_name, None) if hasattr(obj, "state_field_name") else None),
            }
        )

    def format_error_response(self, request: HttpRequest, error: Exception, status_code: int = 400) -> HttpResponse:
        """
        Format an error response.

        For HTMX requests: returns error with toast trigger
        For regular requests: returns JSON response
        """
        message = self.get_error_message(error)
        error_data = format_transition_error(error)

        if request.headers.get("HX-Request"):
            # HTMX request - return error response with toast trigger
            response = HttpResponse(status=status_code)
            return add_toast_trigger(response, message, "error")

        # Regular request - return JSON
        return JsonResponse(
            {
                "success": False,
                "error": error_data,
            },
            status=status_code,
        )

    def post(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
        """Handle POST request to execute a transition."""
        app_label = kwargs.get("app_label")
        model_name = kwargs.get("model_name")
        pk = kwargs.get("pk")
        slug = kwargs.get("slug")
        transition_name = kwargs.get("transition_name")

        # Validate required parameters
        if not all([app_label, model_name, transition_name]):
            return self.format_error_response(
                request,
                ValueError("Missing required parameters: app_label, model_name, and transition_name"),
                400,
            )

        if not pk and not slug:
            return self.format_error_response(request, ValueError("Missing required parameter: pk or slug"), 400)

        # Get the model class
        model_class = self.get_model_class(app_label, model_name)
        if model_class is None:
            return self.format_error_response(request, ValueError(f"Model '{app_label}.{model_name}' not found"), 404)

        # Get the object
        try:
            obj = self.get_object(model_class, pk, slug)
        except ObjectDoesNotExist:
            return self.format_error_response(request, ValueError(f"Object not found: {model_name} with pk={pk}"), 404)

        # Validate the transition
        can_execute, error_msg = self.validate_transition(obj, transition_name, request.user)
        if not can_execute:
            return self.format_error_response(
                request,
                TransitionNotAvailable(
                    message=error_msg,
                    user_message=error_msg,
                    current_state=getattr(obj, "status", None),
                    requested_transition=transition_name,
                ),
                400,
            )

        # Execute the transition
        try:
            # Get any additional kwargs from POST data
            extra_kwargs = {}
            if request.POST.get("notes"):
                extra_kwargs["notes"] = request.POST.get("notes")
            if request.POST.get("reason"):
                extra_kwargs["reason"] = request.POST.get("reason")

            self.execute_transition(obj, transition_name, request.user, **extra_kwargs)

            logger.info(
                f"Transition '{transition_name}' executed on {model_class.__name__}(pk={obj.pk}) by user {request.user}"
            )

            return self.format_success_response(request, obj, transition_name)

        except TransitionPermissionDenied as e:
            logger.warning(
                f"Permission denied for transition '{transition_name}' on {model_class.__name__}(pk={obj.pk}) by user {request.user}: {e}"
            )
            return self.format_error_response(request, e, 403)

        except TransitionValidationError as e:
            logger.warning(
                f"Validation error for transition '{transition_name}' on {model_class.__name__}(pk={obj.pk}): {e}"
            )
            return self.format_error_response(request, e, 400)

        except TransitionNotAllowed as e:
            logger.warning(f"Transition not allowed: '{transition_name}' on {model_class.__name__}(pk={obj.pk}): {e}")
            return self.format_error_response(request, e, 400)

        except Exception as e:
            logger.exception(
                f"Unexpected error during transition '{transition_name}' on {model_class.__name__}(pk={obj.pk})"
            )
            return self.format_error_response(request, ValueError(f"An unexpected error occurred: {str(e)}"), 500)
