import uuid

from fastapi import Request
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.responses import JSONResponse

from services.auth_service import AuthService
from services.database import async_session_maker
from models.sql.user import UserModel

# Paths that skip authentication (exact or prefix match)
PUBLIC_PATHS = [
    "/api/v1/auth/dev-status",
    "/api/v1/auth/dev-login",
    "/api/v1/auth/login",
    "/api/v1/auth/callback",
    "/docs",
    "/openapi.json",
    "/api/health",
]

auth_service = AuthService()


class AuthMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        path = request.url.path

        # Skip auth for public paths
        for public_path in PUBLIC_PATHS:
            if path == public_path or path.startswith(public_path + "/"):
                request.state.user = None
                return await call_next(request)

        # Skip auth for non-API paths (Next.js frontend routes)
        if not path.startswith("/api/"):
            request.state.user = None
            return await call_next(request)

        # Extract token from cookie or Authorization header
        token = request.cookies.get("session_token")
        if not token:
            auth_header = request.headers.get("Authorization")
            if auth_header and auth_header.startswith("Bearer "):
                token = auth_header[7:]

        if not token:
            return JSONResponse(
                status_code=401,
                content={"detail": "Authentication required"},
            )

        # Validate JWT
        claims = auth_service.validate_token(token)
        if not claims:
            return JSONResponse(
                status_code=401,
                content={"detail": "Invalid or expired token"},
            )

        # Load user from DB
        try:
            user_id = uuid.UUID(claims["sub"])
            async with async_session_maker() as session:
                user = await session.get(UserModel, user_id)
        except (ValueError, KeyError):
            return JSONResponse(
                status_code=401,
                content={"detail": "Invalid token payload"},
            )

        if not user:
            return JSONResponse(
                status_code=401,
                content={"detail": "User not found"},
            )

        if not user.is_active:
            return JSONResponse(
                status_code=401,
                content={"detail": "Account deactivated"},
            )

        request.state.user = user
        return await call_next(request)
