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",
    "/stream",
    "/save/conversation",
    "/rate/conversation"
]

DOC_ROUTES = [
    "/",
    "/health-check",
    "/redoc",
    "/openapi.json"
]

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, "")

            HOTEL_ID = request.headers.get("HOTEL_ID")
            SUB_HOTEL_ID = request.headers.get("SUB_HOTEL_ID")

            if path not in DOC_ROUTES:
                # if not HOTEL_ID or not SUB_HOTEL_ID:
                #     raise HTTPException(status_code=401, detail="Hotel ID or Sub Hotel ID is required")
                
                if HOTEL_ID != settings.ID:
                    raise HTTPException(status_code=401, detail="Hotel ID is invalid")
                
                # if SUB_HOTEL_ID not in await get_hotel_details():
                #     raise HTTPException(status_code=401, detail="Sub Hotel ID is invalid")

            request.state.HOTEL_ID = HOTEL_ID
            request.state.SUB_HOTEL_ID = SUB_HOTEL_ID

            # routes skip
            if path in PUBLIC_ROUTES or path in DOC_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,
            )