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

from app.schemas import ResponseWrapper
from app.exceptions import HTTPException
from app.configs.settings import settings

import jwt
from jwt import ExpiredSignatureError, InvalidTokenError

PUBLIC_ROUTES = [
    "/health-check",
]

async def verify_jwt_token(token: str) -> dict:
    try:
        # Decode & validate token
        payload = jwt.decode(token, settings.JWT_SECRET_KEY, algorithms=[settings.JWT_ALGORITHM])

        return payload  # token is valid

    except ExpiredSignatureError:
        raise HTTPException(status_code=401, detail="Token has expired")

    except InvalidTokenError:
        raise HTTPException(status_code=401, detail="Invalid token")

class AuthenticationMiddleware(BaseHTTPMiddleware):

    async def dispatch(self, request: Request, call_next):

        try:
            # ✅ Check if route exists
            route_exists = False
            for route in request.app.router.routes:
                match, _ = route.matches(request.scope)
                if match != Match.NONE:
                    route_exists = True
                    break

            # If route does not exist → let FastAPI handle 404
            if not route_exists or request.method == "OPTIONS":
                return await call_next(request)

            # Normalize path
            path = request.url.path.replace(settings.API_PREFIX, "")

            # routes skip
            if path in PUBLIC_ROUTES:
                return await call_next(request)

            # 🔐 Auth check
            if not request.headers.get("Authorization"):
                raise HTTPException(status_code=401, detail="Unauthorized")

            user = await verify_jwt_token(request.headers.get("Authorization"))

            request.state.user = user

            return await call_next(request)

        except HTTPException as exception:
            return JSONResponse(
                status_code=exception.status_code,
                content=ResponseWrapper(
                    status=False,
                    message=exception.detail,
                    data=None
                ).__dict__,
                headers=exception.headers,
            )