from fastapi import Request, HTTPException
from fastapi.responses import JSONResponse

from starlette.routing import Match
from starlette.middleware.base import BaseHTTPMiddleware

import jwt
from jwt import ExpiredSignatureError, InvalidTokenError

from app.core.config import settings

PUBLIC_ROUTES = [
    "/health",
    "/hotel/chat"
]

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

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

    except InvalidTokenError:
        print("Invalid token")
        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={
                    "status": False,
                    "message": exception.detail,
                    "data": None
                },
                headers=exception.headers,
            )