{% include "templates/base/session.py.j2" %}

import time

from fastapi import APIRouter, Depends, HTTPException, Request, Response, status

_runtime: Optional[SessionRuntime] = None


def get_runtime() -> SessionRuntime:
    global _runtime
    if _runtime is None:
        _runtime = SessionRuntime(load_session_settings())
    return _runtime


def create_router(*, router: APIRouter | None = None) -> APIRouter:
    """Provide session issuance, verification, metadata, and revocation endpoints."""

    router = router or APIRouter(prefix="/sessions", tags=["session"])

    @router.get("/metadata", response_model=Dict[str, Any])
    def get_metadata() -> Dict[str, Any]:
        return describe_session()

    @router.get("/features", response_model=Dict[str, Any])
    def list_features() -> Dict[str, Any]:
        return {"features": list_session_features()}

    @router.post("/", status_code=status.HTTP_201_CREATED)
    async def create_session_endpoint(  # type: ignore[no-untyped-def]
        request: Request,
        response: Response,
        runtime: SessionRuntime = Depends(get_runtime),
    ) -> Dict[str, Any]:
        payload = await request.json()
        if "user_id" not in payload:
            raise HTTPException(status.HTTP_400_BAD_REQUEST, detail="Missing 'user_id' in payload")

        claims = payload.get("claims", {})
        if not isinstance(claims, dict):
            claims = {}

        envelope = runtime.issue_session(
            user_id=str(payload["user_id"]),
            payload=claims,
        )
        _apply_cookie(response, envelope.cookie)
        return {
            "session_id": envelope.session.session_id,
            "expires_at": envelope.session.expires_at,
            "refresh_token": envelope.refresh_token,
        }

    @router.get("/current")
    async def get_current_session(  # type: ignore[no-untyped-def]
        request: Request,
        runtime: SessionRuntime = Depends(get_runtime),
    ) -> Dict[str, Any]:
        token = _extract_token(request, runtime.settings.cookie.name)
        try:
            session = runtime.verify_session_token(token)
        except ValueError as exc:
            raise HTTPException(status.HTTP_401_UNAUTHORIZED, detail=str(exc)) from exc
        return {
            "session_id": session.session_id,
            "user_id": session.user_id,
            "payload": session.payload,
            "expires_at": session.expires_at,
        }

    @router.post("/refresh")
    async def refresh_session_endpoint(  # type: ignore[no-untyped-def]
        request: Request,
        response: Response,
        runtime: SessionRuntime = Depends(get_runtime),
    ) -> Dict[str, Any]:
        payload = await request.json()
        refresh_token = payload.get("refresh_token")
        if not isinstance(refresh_token, str):
            raise HTTPException(status.HTTP_400_BAD_REQUEST, detail="Refresh token required")

        try:
            envelope = runtime.rotate_session(refresh_token)
        except ValueError as exc:
            raise HTTPException(status.HTTP_400_BAD_REQUEST, detail=str(exc)) from exc
        _apply_cookie(response, envelope.cookie)
        return {
            "session_id": envelope.session.session_id,
            "expires_at": envelope.session.expires_at,
            "refresh_token": envelope.refresh_token,
        }

    @router.delete("/{session_id}", status_code=status.HTTP_204_NO_CONTENT)
    async def revoke_session(  # type: ignore[no-untyped-def]
        session_id: str,
        runtime: SessionRuntime = Depends(get_runtime),
    ) -> Response:
        runtime.revoke_session(session_id)
        return Response(status_code=status.HTTP_204_NO_CONTENT)

    return router


def _extract_token(request: Request, cookie_name: str) -> str:
	token = request.cookies.get(cookie_name)
	if token:
		return token
	header = request.headers.get("authorization")
	if header and header.lower().startswith("bearer "):
		return header.split(" ", 1)[1]
	raise HTTPException(status.HTTP_401_UNAUTHORIZED, detail="Session token missing")


def _apply_cookie(response: Response, cookie: Dict[str, Any]) -> None:
	response.set_cookie(
		key=cookie["name"],
		value=cookie["value"],
		domain=cookie.get("domain"),
		secure=bool(cookie.get("secure", True)),
		httponly=bool(cookie.get("httponly", True)),
		samesite=str(cookie.get("same_site", "lax")).lower(),
		expires=int(cookie.get("expires", time.time() + 60)),
	)


__all__.extend(["get_runtime", "create_router"])
