"""
Centralized core API views.
Migrated from apps.core.views.entity_search

Caching Strategy:
- QuickEntitySuggestionView: 5 minutes (300s) - autocomplete should be fast and relatively fresh
- EntityFuzzySearchView: No caching - POST requests with varying data
- EntityNotFoundView: No caching - POST requests with context-specific data
"""

import contextlib

from drf_spectacular.utils import extend_schema
from rest_framework import status
from rest_framework.permissions import AllowAny
from rest_framework.response import Response
from rest_framework.views import APIView

from apps.core.decorators.cache_decorators import cache_api_response
from apps.core.services.entity_fuzzy_matching import (
    EntityType,
    entity_fuzzy_matcher,
)

import logging

logger = logging.getLogger(__name__)


class TelemetryView(APIView):
    """
    Handle frontend telemetry and request metadata logging.
    
    This endpoint accepts telemetry data from the frontend for logging and
    analytics purposes. When error data is present, it persists the error
    to the database for monitoring.
    
    Note: This endpoint bypasses authentication entirely to ensure errors
    can be logged even when user tokens are expired or invalid.
    """

    authentication_classes = []  # Bypass JWT auth to allow error logging with expired tokens
    permission_classes = [AllowAny]


    @extend_schema(
        tags=["Core"],
        summary="Log request metadata",
        description="Log frontend telemetry and request metadata",
    )
    def post(self, request):
        """Accept telemetry data from frontend."""
        data = request.data
        
        # If this is an error report, persist it to the database
        if data.get('p_error_type') or data.get('p_error_message') or data.get('error_type') or data.get('error_message'):
            from apps.core.services import ErrorService
            
            # Handle both p_ prefixed params (from log_request_metadata RPC) and direct params
            error_message = data.get('p_error_message') or data.get('error_message') or 'Unknown error'
            error_type = data.get('p_error_type') or data.get('error_type') or 'Error'
            severity = data.get('p_severity') or data.get('severity') or 'medium'
            error_stack = data.get('p_error_stack') or data.get('error_stack') or ''
            error_code = data.get('p_error_code') or data.get('error_code') or ''
            
            # Build metadata from available fields
            metadata = {
                'action': data.get('p_action') or data.get('action'),
                'breadcrumbs': data.get('p_breadcrumbs'),
                'duration_ms': data.get('p_duration_ms'),
                'retry_attempts': data.get('p_retry_attempts'),
                'affected_route': data.get('p_affected_route'),
                'request_id': data.get('p_request_id') or data.get('request_id'),
            }
            # Remove None values
            metadata = {k: v for k, v in metadata.items() if v is not None}
            
            # Build environment from available fields
            environment = data.get('p_environment_context') or data.get('environment') or {}
            if isinstance(environment, str):
                import json
                try:
                    environment = json.loads(environment)
                except json.JSONDecodeError:
                    environment = {}
            
            try:
                error = ErrorService.capture_error(
                    error=error_message,
                    source='frontend',
                    request=request,
                    severity=severity,
                    metadata=metadata,
                    environment=environment,
                )
                # Update additional fields
                error.error_type = error_type
                error.error_stack = error_stack[:10000] if error_stack else ''
                error.error_code = error_code
                error.endpoint = data.get('p_affected_route') or ''
                error.http_status = data.get('p_http_status')
                error.save(update_fields=['error_type', 'error_stack', 'error_code', 'endpoint', 'http_status'])
                
                logger.info(f"Frontend error captured: {error.short_error_id}")
                return Response(
                    {"success": True, "error_id": str(error.error_id)},
                    status=status.HTTP_201_CREATED,
                )
            except Exception as e:
                logger.error(f"Failed to capture frontend error: {e}")
                # Fall through to regular telemetry logging
        
        # Non-error telemetry - just log and acknowledge
        logger.debug(
            "Telemetry received",
            extra={
                "data": data,
                "user_id": getattr(request.user, "id", None),
            },
        )
        return Response(
            {"success": True, "message": "Telemetry logged"},
            status=status.HTTP_200_OK,
        )



class EntityFuzzySearchView(APIView):
    """
    API endpoint for fuzzy entity search with authentication prompts.

    Handles entity lookup failures by providing intelligent suggestions and
    authentication prompts for entity creation.

    Migrated from apps.core.views.entity_search.EntityFuzzySearchView
    """

    permission_classes = [AllowAny]  # Allow both authenticated and anonymous users

    @extend_schema(
        tags=["Core"],
        summary="Fuzzy entity search",
        description="Perform fuzzy entity search with authentication prompts for entity creation",
    )
    def post(self, request):
        """
        Perform fuzzy entity search.

        Request body:
        {
            "query": "entity name to search",
            "entity_types": ["park", "ride", "company"],  // optional
            "include_suggestions": true  // optional, default true
        }

        Response:
        {
            "success": true,
            "query": "original query",
            "matches": [
                {
                    "entity_type": "park",
                    "name": "Cedar Point",
                    "slug": "cedar-point",
                    "score": 0.95,
                    "confidence": "high",
                    "match_reason": "Text similarity with 'Cedar Point'",
                    "url": "/parks/cedar-point/",
                    "entity_id": 123
                }
            ],
            "suggestion": {
                "suggested_name": "New Entity Name",
                "entity_type": "park",
                "requires_authentication": true,
                "login_prompt": "Log in to suggest adding...",
                "signup_prompt": "Sign up to contribute...",
                "creation_hint": "Help expand ThrillWiki..."
            },
            "user_authenticated": false
        }
        """
        try:
            # Parse request data
            query = request.data.get("query", "").strip()
            entity_types_raw = request.data.get("entity_types", ["park", "ride", "company"])
            include_suggestions = request.data.get("include_suggestions", True)

            # Validate query
            if not query or len(query) < 2:
                return Response(
                    {
                        "success": False,
                        "detail": "Query must be at least 2 characters long",
                        "code": "INVALID_QUERY",
                    },
                    status=status.HTTP_400_BAD_REQUEST,
                )

            # Parse and validate entity types
            entity_types = []
            valid_types = {"park", "ride", "company"}

            for entity_type in entity_types_raw:
                if entity_type in valid_types:
                    entity_types.append(EntityType(entity_type))

            if not entity_types:
                entity_types = [EntityType.PARK, EntityType.RIDE, EntityType.COMPANY]

            # Perform fuzzy matching
            matches, suggestion = entity_fuzzy_matcher.find_entity(
                query=query, entity_types=entity_types, user=request.user
            )

            # Format response
            response_data = {
                "success": True,
                "query": query,
                "matches": [match.to_dict() for match in matches],
                "user_authenticated": (
                    request.user.is_authenticated if hasattr(request.user, "is_authenticated") else False
                ),
            }

            # Include suggestion if requested and available
            if include_suggestions and suggestion:
                response_data["suggestion"] = {
                    "suggested_name": suggestion.suggested_name,
                    "entity_type": suggestion.entity_type.value,
                    "requires_authentication": suggestion.requires_authentication,
                    "login_prompt": suggestion.login_prompt,
                    "signup_prompt": suggestion.signup_prompt,
                    "creation_hint": suggestion.creation_hint,
                }

            return Response(response_data, status=status.HTTP_200_OK)

        except Exception as e:
            return Response(
                {
                    "success": False,
                    "detail": f"Internal server error: {str(e)}",
                    "code": "INTERNAL_ERROR",
                },
                status=status.HTTP_500_INTERNAL_SERVER_ERROR,
            )


class EntityNotFoundView(APIView):
    """
    Endpoint specifically for handling entity not found scenarios.

    This view is called when normal entity lookup fails and provides
    fuzzy matching suggestions along with authentication prompts.

    Migrated from apps.core.views.entity_search.EntityNotFoundView
    """

    permission_classes = [AllowAny]

    @extend_schema(
        tags=["Core"],
        summary="Handle entity not found",
        description="Handle entity not found scenarios with fuzzy matching suggestions and authentication prompts",
    )
    def post(self, request):
        """
        Handle entity not found with suggestions.

        Request body:
        {
            "original_query": "what user searched for",
            "attempted_slug": "slug-that-failed",  // optional
            "entity_type": "park",  // optional, inferred from context
            "context": {  // optional context information
                "park_slug": "park-slug-if-searching-for-ride",
                "source_page": "page where search originated"
            }
        }
        """
        try:
            original_query = request.data.get("original_query", "").strip()
            attempted_slug = request.data.get("attempted_slug", "")
            entity_type_hint = request.data.get("entity_type")
            context = request.data.get("context", {})

            if not original_query:
                return Response(
                    {
                        "success": False,
                        "detail": "original_query is required",
                        "code": "MISSING_QUERY",
                    },
                    status=status.HTTP_400_BAD_REQUEST,
                )

            # Determine entity types to search based on context
            entity_types = []
            if entity_type_hint:
                with contextlib.suppress(ValueError):
                    entity_types = [EntityType(entity_type_hint)]

            # If we have park context, prioritize ride searches
            if context.get("park_slug") and not entity_types:
                entity_types = [EntityType.RIDE, EntityType.PARK]

            # Default to all types if not specified
            if not entity_types:
                entity_types = [EntityType.PARK, EntityType.RIDE, EntityType.COMPANY]

            # Try fuzzy matching on the original query
            matches, suggestion = entity_fuzzy_matcher.find_entity(
                query=original_query, entity_types=entity_types, user=request.user
            )

            # If no matches on original query, try the attempted slug
            if not matches and attempted_slug:
                # Convert slug back to readable name for fuzzy matching
                slug_as_name = attempted_slug.replace("-", " ").title()
                matches, suggestion = entity_fuzzy_matcher.find_entity(
                    query=slug_as_name, entity_types=entity_types, user=request.user
                )

            # Prepare response with detailed context
            response_data = {
                "success": True,
                "original_query": original_query,
                "attempted_slug": attempted_slug,
                "context": context,
                "matches": [match.to_dict() for match in matches],
                "user_authenticated": (
                    request.user.is_authenticated if hasattr(request.user, "is_authenticated") else False
                ),
                "has_matches": len(matches) > 0,
            }

            # Always include suggestion for entity not found scenarios
            if suggestion:
                response_data["suggestion"] = {
                    "suggested_name": suggestion.suggested_name,
                    "entity_type": suggestion.entity_type.value,
                    "requires_authentication": suggestion.requires_authentication,
                    "login_prompt": suggestion.login_prompt,
                    "signup_prompt": suggestion.signup_prompt,
                    "creation_hint": suggestion.creation_hint,
                }

            return Response(response_data, status=status.HTTP_200_OK)

        except Exception as e:
            return Response(
                {
                    "success": False,
                    "detail": f"Internal server error: {str(e)}",
                    "code": "INTERNAL_ERROR",
                },
                status=status.HTTP_500_INTERNAL_SERVER_ERROR,
            )


class QuickEntitySuggestionView(APIView):
    """
    Lightweight endpoint for quick entity suggestions (e.g., autocomplete).

    Migrated from apps.core.views.entity_search.QuickEntitySuggestionView

    Security Note: This endpoint only accepts GET requests, which are inherently
    safe from CSRF attacks. No CSRF exemption is needed.
    """

    permission_classes = [AllowAny]

    @extend_schema(
        tags=["Core"],
        summary="Quick entity suggestions",
        description="Lightweight endpoint for quick entity suggestions (e.g., autocomplete)",
    )
    @cache_api_response(timeout=300, key_prefix="entity_suggestions")
    def get(self, request):
        """
        Get quick entity suggestions.

        Query parameters:
        - q: query string
        - types: comma-separated entity types (park,ride,company)
        - limit: max results (default 5)
        """
        try:
            query = request.GET.get("q", "").strip()
            types_param = request.GET.get("types", "park,ride,company")
            limit = min(int(request.GET.get("limit", 5)), 10)  # Cap at 10

            if not query or len(query) < 2:
                return Response({"suggestions": [], "query": query}, status=status.HTTP_200_OK)

            # Parse entity types
            entity_types = []
            for type_str in types_param.split(","):
                type_str = type_str.strip()
                if type_str in ["park", "ride", "company"]:
                    entity_types.append(EntityType(type_str))

            if not entity_types:
                entity_types = [EntityType.PARK, EntityType.RIDE, EntityType.COMPANY]

            # Get fuzzy matches
            matches, _ = entity_fuzzy_matcher.find_entity(query=query, entity_types=entity_types, user=request.user)

            # Format as simple suggestions
            suggestions = []
            for match in matches[:limit]:
                suggestions.append(
                    {
                        "name": match.name,
                        "type": match.entity_type.value,
                        "slug": match.slug,
                        "url": match.url,
                        "score": match.score,
                        "confidence": match.confidence,
                    }
                )

            return Response(
                {"suggestions": suggestions, "query": query, "count": len(suggestions)},
                status=status.HTTP_200_OK,
            )

        except Exception as e:
            return Response(
                {"suggestions": [], "query": request.GET.get("q", ""), "detail": str(e)},
                status=status.HTTP_200_OK,
            )  # Return 200 even on errors for autocomplete


# Utility function for other views to use
def get_entity_suggestions(query: str, entity_types: list[str] | None = None, user=None):
    """
    Utility function for other Django views to get entity suggestions.

    Args:
        query: Search query
        entity_types: List of entity type strings
        user: Django user object

    Returns:
        Tuple of (matches, suggestion)
    """
    try:
        # Convert string types to EntityType enums
        parsed_types = []
        if entity_types:
            for entity_type in entity_types:
                try:
                    parsed_types.append(EntityType(entity_type))
                except ValueError:
                    continue

        if not parsed_types:
            parsed_types = [EntityType.PARK, EntityType.RIDE, EntityType.COMPANY]

        return entity_fuzzy_matcher.find_entity(query=query, entity_types=parsed_types, user=user)
    except Exception:
        return [], None
