# #region AuthApi [C:5] [TYPE Module] [SEMANTICS fastapi, auth, api]
# @BRIEF Authentication API endpoints.
# @LAYER API
# @PRE Python environment and dependencies installed; database available.
# @POST FastAPI app instance with auth routes registered.
# @SIDE_EFFECT Registers API routes; configures OAuth and CORS middleware.
# @DATA_CONTRACT Input -> OAuth2PasswordRequestForm -> Token, User
# @INVARIANT All auth endpoints must return consistent error codes.
# @RELATION DEPENDS_ON -> [is_adfs_configured]

from fastapi import APIRouter, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordRequestForm
from sqlalchemy.orm import Session
import starlette.requests

from ..core.auth.jwt import blacklist_token
from ..core.auth.logger import log_security_event
from ..core.auth.oauth import is_adfs_configured, oauth
from ..core.database import get_auth_db
from ..core.logger import belief_scope, logger
from ..core.rate_limiter import rate_limiter
from ..dependencies import get_current_user
from ..schemas.auth import Token, User as UserSchema
from ..services.auth_service import AuthService

# #region router [C:1] [TYPE Variable]
# @RELATION DEPENDS_ON -> [EXT:Library:fastapi.APIRouter]
# @BRIEF APIRouter instance for authentication routes.
router = APIRouter(prefix="/api/auth", tags=["auth"])
# #endregion router


# #region login_for_access_token [C:4] [TYPE Function]
# @BRIEF Authenticates a user and returns a JWT access token.
# @PRE form_data contains username and password.
# @POST Returns a Token object on success.
# @SIDE_EFFECT DB read/write for auth session; writes security event log.
# @RELATION CALLS -> [AuthService]

@router.post("/login", response_model=Token)
async def login_for_access_token(
    form_data: OAuth2PasswordRequestForm = Depends(),
    db: Session = Depends(get_auth_db),
    request: starlette.requests.Request = None,
):
    with belief_scope("api.auth.login"):
        # Rate limiting — protect against brute force
        client_ip = request.client.host if request.client else "unknown"
        if rate_limiter.is_banned(client_ip):
            log_security_event(
                "LOGIN_BLOCKED", form_data.username, {"reason": "Rate limited", "ip": client_ip}
            )
            raise HTTPException(
                status_code=status.HTTP_429_TOO_MANY_REQUESTS,
                detail="Too many login attempts. Try again later.",
            )

        auth_service = AuthService(db)
        user = auth_service.authenticate_user(form_data.username, form_data.password)
        if not user:
            rate_limiter.record_attempt(client_ip)
            log_security_event(
                "LOGIN_FAILED", form_data.username, {"reason": "Invalid credentials"}
            )
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Incorrect username or password",
                headers={"WWW-Authenticate": "Bearer"},
            )
        rate_limiter.record_success(client_ip)
        log_security_event("LOGIN_SUCCESS", user.username, {"source": "LOCAL"})
        return auth_service.create_session(user)


# #endregion login_for_access_token

# #region read_users_me [C:4] [TYPE Function]
# @BRIEF Retrieves the profile of the currently authenticated user.
# @PRE Valid JWT token provided.
# @POST Returns the current user's data.
# @RELATION DEPENDS_ON -> [get_current_user]
# @SIDE_EFFECT Reads current user from DB via auth middleware; writes security event log.

@router.get("/me", response_model=UserSchema)
async def read_users_me(current_user: UserSchema = Depends(get_current_user)):
    with belief_scope("api.auth.me"):
        return current_user


# #endregion read_users_me


# #region logout [C:4] [TYPE Function]
# @BRIEF Logs out the current user — blacklists the JWT token server-side.
# @PRE Valid JWT token provided in Authorization header.
# @POST Token is added to blacklist; subsequent requests with same token are rejected.
# @SIDE_EFFECT Writes security event LOGOUT event; writes to token_blacklist table.
# @RELATION DEPENDS_ON -> [get_current_user]
# @RELATION CALLS -> [blacklist_token]

@router.post("/logout")
async def logout(
    current_user: UserSchema = Depends(get_current_user),
    request: starlette.requests.Request = None,
    db: Session = Depends(get_auth_db),
):
    with belief_scope("api.auth.logout"):
        log_security_event("LOGOUT", current_user.username)

        # Extract token from Authorization header and blacklist it
        auth_header = request.headers.get("Authorization", "")
        if auth_header.startswith("Bearer "):
            token = auth_header[7:]
            blacklist_token(token, db)
            logger.reason(
                "Token blacklisted on logout",
                extra={"user": current_user.username},
            )

        return {"message": "Successfully logged out"}


# #endregion logout


# #region login_adfs [C:4] [TYPE Function]
# @BRIEF Initiates the ADFS OIDC login flow.
# @POST Redirects the user to ADFS.
# @RELATION CALLS -> [is_adfs_configured]
# @SIDE_EFFECT Redirects user to ADFS external OIDC provider.

@router.get("/login/adfs")
async def login_adfs(request: starlette.requests.Request):
    with belief_scope("api.auth.login_adfs"):
        if not is_adfs_configured():
            raise HTTPException(
                status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
                detail="ADFS is not configured. Please set ADFS_CLIENT_ID, ADFS_CLIENT_SECRET, and ADFS_METADATA_URL environment variables.",
            )
        redirect_uri = request.url_for("auth_callback_adfs")
        return await oauth.adfs.authorize_redirect(request, str(redirect_uri))


# #endregion login_adfs


# #region auth_callback_adfs [C:4] [TYPE Function]
# @BRIEF Handles the callback from ADFS after successful authentication.
# @POST Provisions user JIT and returns session token.
# @SIDE_EFFECT Provisions user in DB, creates auth session, writes security event log.
# @RELATION CALLS -> [AuthService]

@router.get("/callback/adfs", name="auth_callback_adfs")
async def auth_callback_adfs(
    request: starlette.requests.Request, db: Session = Depends(get_auth_db)
):
    with belief_scope("api.auth.callback_adfs"):
        if not is_adfs_configured():
            raise HTTPException(
                status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
                detail="ADFS is not configured. Please set ADFS_CLIENT_ID, ADFS_CLIENT_SECRET, and ADFS_METADATA_URL environment variables.",
            )
        token = await oauth.adfs.authorize_access_token(request)
        user_info = token.get("userinfo")
        if not user_info:
            raise HTTPException(
                status_code=400, detail="Failed to retrieve user info from ADFS"
            )

        auth_service = AuthService(db)
        user = auth_service.provision_adfs_user(user_info)
        return auth_service.create_session(user)


# #endregion auth_callback_adfs
# #endregion AuthApi
