# app/exceptions/error_renderer.py
"""
Error Page Renderer

Renders context-aware error pages using Jinja2 templates.
Handles fallback logic and context-specific customization.
"""

import logging
from pathlib import Path
from typing import Any

from fastapi import Request
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates

from app.modules.enums import FrontendType
from middleware.frontend_type import get_frontend_type

logger = logging.getLogger(__name__)


class ErrorPageRenderer:
    """Renders context-aware error pages using Jinja2 templates."""

    # Map status codes to friendly names
    STATUS_CODE_NAMES = {
        400: "Bad Request",
        401: "Unauthorized",
        403: "Forbidden",
        404: "Not Found",
        422: "Validation Error",
        429: "Too Many Requests",
        500: "Internal Server Error",
        502: "Bad Gateway",
        503: "Service Unavailable",
    }

    # Map status codes to user-friendly messages
    STATUS_CODE_MESSAGES = {
        400: "The request could not be processed due to invalid data.",
        401: "You need to be authenticated to access this resource.",
        403: "You don't have permission to access this resource.",
        404: "The page you're looking for doesn't exist or has been moved.",
        422: "The submitted data contains validation errors.",
        429: "Too many requests. Please slow down and try again later.",
        500: "Something went wrong on our end. We're working to fix it.",
        502: "Unable to reach the service. Please try again later.",
        503: "The service is temporarily unavailable. Please try again later.",
    }

    @staticmethod
    def get_templates_dir() -> Path:
        """Get the templates directory path."""
        # Assuming templates are in app/templates/
        base_dir = Path(__file__).resolve().parent.parent
        return base_dir / "templates"

    @staticmethod
    def render_error_page(
        request: Request,
        status_code: int,
        error_code: str,
        message: str,
        details: dict[str, Any] | None = None,
        show_debug: bool = False,
    ) -> HTMLResponse:
        """
        Render appropriate error page based on request frontend type.

        Template Selection Priority:
        1. Frontend-specific error page: {frontend}/errors/{status_code}.html
        2. Frontend-specific generic: {frontend}/errors/generic.html
        3. Shared fallback error page: shared/{status_code}-fallback.html
        4. Shared fallback generic: shared/generic-fallback.html

        Args:
            request: FastAPI request object
            status_code: HTTP status code
            error_code: Application error code
            message: Error message
            details: Additional error details
            show_debug: Whether to show debug information

        Returns:
            HTMLResponse with rendered error page
        """
        # Get frontend type — default to PLATFORM in error rendering context
        # (errors can occur before FrontendTypeMiddleware runs)
        frontend_type = get_frontend_type(request) or FrontendType.PLATFORM

        # Prepare template data
        template_data = ErrorPageRenderer._prepare_template_data(
            request=request,
            frontend_type=frontend_type,
            status_code=status_code,
            error_code=error_code,
            message=message,
            details=details,
            show_debug=show_debug,
        )

        # Find and render template
        templates_dir = ErrorPageRenderer.get_templates_dir()
        templates = Jinja2Templates(directory=str(templates_dir))

        # Try to find appropriate template
        template_path = ErrorPageRenderer._find_template(
            frontend_type=frontend_type,
            status_code=status_code,
        )

        logger.info(
            f"Rendering error page: {template_path} for {status_code} in {frontend_type.value} frontend",
            extra={
                "status_code": status_code,
                "error_code": error_code,
                "frontend": frontend_type.value,
                "template": template_path,
            },
        )

        try:
            # Render template
            return templates.TemplateResponse(
                template_path,
                {
                    "request": request,
                    **template_data,
                },
                status_code=status_code,
            )
        except Exception as e:
            logger.error(
                f"Failed to render error template {template_path}: {e}", exc_info=True
            )
            # Return basic HTML as absolute fallback
            return ErrorPageRenderer._render_basic_html_fallback(
                status_code=status_code,
                error_code=error_code,
                message=message,
            )

    @staticmethod
    def _find_template(
        frontend_type: FrontendType,
        status_code: int,
    ) -> str:
        """
        Find appropriate error template based on frontend type and status code.

        Priority:
        1. {frontend}/errors/{status_code}.html
        2. {frontend}/errors/generic.html
        3. shared/{status_code}-fallback.html
        4. shared/generic-fallback.html
        """
        templates_dir = ErrorPageRenderer.get_templates_dir()

        # Map frontend type to folder name
        frontend_folders = {
            FrontendType.ADMIN: "admin",
            FrontendType.STORE: "store",
            FrontendType.STOREFRONT: "storefront",
            FrontendType.PLATFORM: "fallback",  # Platform uses fallback templates
        }

        frontend_folder = frontend_folders.get(frontend_type, "fallback")

        # Try frontend-specific status code template
        specific_template = f"{frontend_folder}/errors/{status_code}.html"
        if (templates_dir / specific_template).exists():
            return specific_template

        # Try frontend-specific generic template
        generic_template = f"{frontend_folder}/errors/generic.html"
        if (templates_dir / generic_template).exists():
            return generic_template

        # Try shared fallback status code template
        fallback_specific = f"shared/{status_code}-fallback.html"
        if (templates_dir / fallback_specific).exists():
            return fallback_specific

        # Use shared fallback generic template (must exist)
        return "shared/generic-fallback.html"

    @staticmethod
    def _prepare_template_data(
        request: Request,
        frontend_type: FrontendType,
        status_code: int,
        error_code: str,
        message: str,
        details: dict[str, Any] | None,
        show_debug: bool,
    ) -> dict[str, Any]:
        """Prepare data dictionary for error template."""
        # Get friendly status name
        status_name = ErrorPageRenderer.STATUS_CODE_NAMES.get(
            status_code, f"Error {status_code}"
        )

        # Get default user-friendly message
        default_message = ErrorPageRenderer.STATUS_CODE_MESSAGES.get(
            status_code, "An error occurred while processing your request."
        )

        # Use provided message or default
        user_message = message if message else default_message

        # Determine if we should show debug info
        # Only show to admins (we can check user role if available)
        display_debug = show_debug and ErrorPageRenderer._is_admin_user(request)

        # Get frontend-specific data
        frontend_data = ErrorPageRenderer._get_frontend_data(request, frontend_type)

        return {
            "status_code": status_code,
            "status_name": status_name,
            "error_code": error_code,
            "message": user_message,
            "details": details or {},
            "show_debug": display_debug,
            "frontend_type": frontend_type.value,
            "path": request.url.path,
            **frontend_data,
        }

    @staticmethod
    def _get_frontend_data(
        request: Request, frontend_type: FrontendType
    ) -> dict[str, Any]:
        """Get frontend-specific data for error templates."""
        data = {}

        # Add store information if available (for storefront frontend)
        if frontend_type == FrontendType.STOREFRONT:
            store = getattr(request.state, "store", None)
            if store:
                # Pass minimal store info for templates
                data["store"] = {
                    "id": store.id,
                    "name": store.name,
                    "subdomain": store.subdomain,
                    "logo": getattr(store, "logo", None),
                }

            # Add theme information if available
            theme = getattr(request.state, "theme", None)
            if theme:
                # Theme can be a dict or object, handle both
                if isinstance(theme, dict):
                    data["theme"] = theme
                else:
                    # If it's an object, convert to dict
                    data["theme"] = {
                        "colors": getattr(theme, "colors", {}),
                        "fonts": getattr(theme, "fonts", {}),
                        "branding": getattr(theme, "branding", {}),
                        "custom_css": getattr(theme, "custom_css", None),
                    }

            # Calculate base_url for storefront links
            store_context = getattr(request.state, "store_context", None)
            access_method = (
                store_context.get("detection_method", "unknown")
                if store_context
                else "unknown"
            )
            base_url = "/"
            if access_method == "path" and store:
                # Use the full_prefix from store_context to determine which pattern was used
                full_prefix = (
                    store_context.get("full_prefix", "/store/")
                    if store_context
                    else "/store/"
                )
                base_url = f"{full_prefix}{store.subdomain}/"
            data["base_url"] = base_url

        return data

    @staticmethod
    def _is_admin_user(request: Request) -> bool:
        """
        Check if current user is an admin (for debug info display).

        This is a placeholder - implement based on your auth system.
        """
        # TODO: Implement actual admin check based on JWT/session
        # For now, check if we're in admin frontend
        frontend_type = get_frontend_type(request)
        return frontend_type is not None and frontend_type == FrontendType.ADMIN

    @staticmethod
    def _render_basic_html_fallback(
        status_code: int,
        error_code: str,
        message: str,
    ) -> HTMLResponse:
        """
        Render a basic HTML error page as absolute fallback.

        Used when template rendering fails.
        """
        html_content = f"""
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{status_code} - Error</title>
    <style>
        * {{
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }}
        body {{
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            display: flex;
            align-items: center;
            justify-content: center;
            color: white;
            padding: 2rem;
        }}
        .container {{
            text-align: center;
            max-width: 600px;
        }}
        h1 {{
            font-size: 6rem;
            font-weight: 700;
            margin-bottom: 1rem;
            text-shadow: 2px 2px 4px rgba(0,0,0,0.2);
        }}
        h2 {{
            font-size: 1.5rem;
            font-weight: 400;
            margin-bottom: 1rem;
        }}
        p {{
            font-size: 1.1rem;
            margin-bottom: 2rem;
            opacity: 0.9;
        }}
        .error-code {{
            font-size: 0.9rem;
            opacity: 0.7;
            font-family: 'Courier New', monospace;
        }}
    </style>
</head>
<body>
    <div class="container">
        <h1>{status_code}</h1>
        <h2>Error</h2>
        <p>{message}</p>
        <div class="error-code">Error Code: {error_code}</div>
    </div>
</body>
</html>
        """
        return HTMLResponse(content=html_content, status_code=status_code)
