"""Middleware that auto-logs mutating API requests to audit log."""
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request
from starlette.responses import Response

from services import audit_service


AUDITABLE_METHODS = {"POST", "PUT", "PATCH", "DELETE"}


def _extract_resource_type(path: str) -> str:
    """Extract resource type from URL path.

    e.g. /api/v1/admin/users/123 -> users
         /api/v1/ppt/presentation/create -> presentation
    """
    parts = [p for p in path.split("/") if p]
    # Walk backwards to find first meaningful segment (skip IDs and actions)
    for part in reversed(parts):
        if part in ("v1", "api", "admin", "ppt"):
            continue
        # Skip UUID-looking segments
        if len(part) == 36 and part.count("-") == 4:
            continue
        # Skip common action words
        if part in ("create", "update", "delete", "export", "login", "logout", "callback"):
            continue
        return part
    return "unknown"


class AuditMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next) -> Response:
        response = await call_next(request)

        # Only log mutating requests to API endpoints
        if request.method not in AUDITABLE_METHODS:
            return response
        if not request.url.path.startswith("/api/"):
            return response
        # Skip auth endpoints (logged separately)
        if "/auth/" in request.url.path:
            return response

        # Only log successful mutations
        if response.status_code >= 400:
            return response

        user = getattr(request.state, "user", None)
        user_id = user.id if user else None
        ip_address = request.client.host if request.client else None
        resource_type = _extract_resource_type(request.url.path)

        audit_service.log(
            user_id=user_id,
            action=f"{request.method} {request.url.path}",
            resource_type=resource_type,
            ip_address=ip_address,
        )

        return response
