# #region Api.Auth [C:5] [TYPE Module] [SEMANTICS api,auth,fastapi]
# @defgroup Auth Authentication API endpoints — login, logout, token, ADFS.
# @LAYER API
# @PRE Database available; JWT secret configured.
# @POST FastAPI routes registered for all auth operations.
# @SIDE_EFFECT Registers API routes; configures OAuth and CORS middleware.
# @DATA_CONTRACT OAuth2PasswordRequestForm -> Token | User
# @INVARIANT All auth endpoints return consistent error codes (401/403/422).
# @RELATION DEPENDS_ON -> [Auth.Jwt]
# @RELATION DEPENDS_ON -> [Auth.Service]
# @RELATION DEPENDS_ON -> [Auth.OAuth]

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 Api.Auth.Login [C:4] [TYPE Function] [SEMANTICS api,auth,login,token]
# @ingroup Auth
# @BRIEF Authenticate user by credentials and return JWT access token.
# @PRE form_data contains username and password.
# @POST Returns Token(access_token, token_type) on success; 401 on failure.
# @SIDE_EFFECT DB read for user verification; writes security event LOGIN.
# @SIDE_EFFECT Molecular CoT: REASON on entry, REFLECT on success, EXPLORE on failure.
# @RELATION CALLS -> [Auth.Service]
# @TEST_EDGE: invalid_credentials -> 401
# @TEST_EDGE: locked_account -> 423
# @TEST_EDGE: missing_fields -> 422

@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 Api.Auth.Login

# #region Api.Auth.Me [C:3] [TYPE Function] [SEMANTICS api,auth,profile]
# @ingroup Auth
# @BRIEF Retrieve the profile of the currently authenticated user.
# @PRE Valid JWT token in Authorization header.
# @POST Returns UserSchema with id, username, email, roles.
# @SIDE_EFFECT Reads current user from DB via auth middleware.
# @RELATION DEPENDS_ON -> [Auth.Dependency.GetCurrentUser]

@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 Api.Auth.Me


# #region Api.Auth.Logout [C:4] [TYPE Function] [SEMANTICS api,auth,logout,revoke]
# @ingroup Auth
# @BRIEF Log out current user — blacklists the JWT token server-side.
# @PRE Valid JWT token in Authorization header.
# @POST Token added to blacklist; subsequent requests with same token rejected.
# @SIDE_EFFECT Writes security event LOGOUT; writes to token_blacklist table.
# @SIDE_EFFECT Molecular CoT: REASON/REFLECT/EXPLORE markers.
# @RELATION DEPENDS_ON -> [Auth.Dependency.GetCurrentUser]
# @RELATION CALLS -> [Auth.Jwt.BlacklistToken]
# @TEST_EDGE: already_expired_token -> 200 (idempotent)

@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 Api.Auth.Logout


# #region Api.Auth.LoginAdfs [C:3] [TYPE Function] [SEMANTICS api,auth,adfs,oidc]
# @ingroup Auth
# @BRIEF Initiate ADFS OIDC login flow — redirects user to identity provider.
# @POST Redirects user to ADFS authorization endpoint.
# @SIDE_EFFECT Redirects browser to external OIDC provider.
# @RELATION CALLS -> [Auth.OAuth]

@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 Api.Auth.LoginAdfs


# #region Api.Auth.CallbackAdfs [C:4] [TYPE Function] [SEMANTICS api,auth,adfs,callback]
# @ingroup Auth
# @BRIEF Handle ADFS OIDC callback — provisions user JIT, returns session token.
# @POST Provisions user in DB (JIT), creates auth session.
# @SIDE_EFFECT DB write for user provisioning; writes security event LOGIN_ADFS.
# @SIDE_EFFECT Molecular CoT: REASON/REFLECT/EXPLORE markers.
# @RELATION CALLS -> [Auth.Service]
# @TEST_EDGE: adfs_timeout -> 504
# @TEST_EDGE: invalid_state -> 401

@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 Api.Auth.CallbackAdfs
# #endregion Api.Auth
