from typing import List

from fastapi import Request
from fastapi.responses import PlainTextResponse
from fastapi.templating import Jinja2Templates
from jinja2 import PackageLoader, Environment


class PluginLoadFailedException(Exception):
    """Raised by the load guard dependency when a request hits a load-failed plugin."""

    def __init__(self, plugin_name: str, errors: List[str], tracebacks: List[str]):
        self.plugin_name = plugin_name
        self.errors = errors
        self.tracebacks = tracebacks
        super().__init__(f"Plugin '{plugin_name}' failed to load")


def plugin_load_failed_handler(request: Request, exc: PluginLoadFailedException):
    """Render a user-friendly error page for load-failed plugins."""
    templates = _get_templates(request)
    try:
        return templates.TemplateResponse(
            "plugin_load_error.html.j2",
            {
                "request": request,
                "plugin_name": exc.plugin_name,
                "errors": exc.errors,
                "tracebacks": exc.tracebacks,
                "admin_url": f"/fp-admin/base_module/{exc.plugin_name}/overview",
            },
            status_code=503,
        )
    except Exception:
        body = f"503 Plugin Load Error\n\nPlugin '{exc.plugin_name}' failed to load:\n"
        body += "\n".join(exc.errors)
        return PlainTextResponse(body, status_code=503)


def _get_templates(request: Request) -> Jinja2Templates:
    templates = getattr(request.app.state, "jinja_templates", None)
    if templates is None:
        templates = Jinja2Templates(env=Environment(loader=PackageLoader("fastpluggy", "templates")))  # nosec B701
    return templates


def not_found_handler(request: Request, exc):
    return _get_templates(request).TemplateResponse("404.html.j2", {
        "request": request,
        "title": "Page Not Found",
    }, status_code=404)


async def unauthorized_handler(request: Request, exc):
    """Redirect to login if an auth manager is configured, otherwise show 401 page."""
    fp = getattr(request.app.state, "fastpluggy", None)
    auth_manager = getattr(fp, "auth_manager", None) if fp else None
    if auth_manager and hasattr(auth_manager, "on_authenticate_error"):
        result = await auth_manager.on_authenticate_error(request)
        if result is not None:
            return result
    return _get_templates(request).TemplateResponse("404.html.j2", {
        "request": request,
        "title": "Unauthorized",
    }, status_code=401)


def forbidden_handler(request: Request, exc):
    return _get_templates(request).TemplateResponse("403.html.j2", {
        "request": request,
        "title": "Access Denied",
    }, status_code=403)
