"""Маршруты OAuth-флоу через gnexus-gauth.

Поток: /auth/login → redirect на auth.gnexus.space (PKCE) → /auth/callback →
обмен code на токены → сессия (подписанная cookie через SessionMiddleware) →
redirect на return_to.

InMemory state/pkce store из gnexus-gauth достаточно для одного пользователя;
TODO(M0): заменить на постоянные хранилища до прод-деплоя (см. M0-заметки).
"""

from typing import Any

from fastapi import APIRouter, HTTPException, Request
from fastapi.responses import RedirectResponse

from app.auth.client import get_gauth_client
from app.config import get_settings
from app.dependencies import DbDep
from app.models import User
from app.services.users import claim_global_settings, claim_orphan_data, upsert_user

router = APIRouter(prefix="/auth", tags=["auth"])

SCOPES = ["openid", "email", "profile"]


def _safe_return_to(return_to: str) -> str:
    """Только внутренние пути: open redirect через ?return_to=https://evil.com закрыт."""
    if return_to.startswith("/") and not return_to.startswith("//"):
        return return_to
    return "/"


@router.get("/login")
async def login(request: Request, return_to: str = "/") -> RedirectResponse:
    return_to = _safe_return_to(return_to)
    client = get_gauth_client()
    auth_request = client.build_authorization_request(return_to=return_to, scopes=SCOPES)
    # return_to запоминаем в сессии и забираем в callback после обмена code.
    request.session["return_to"] = return_to
    return RedirectResponse(auth_request.authorization_url)


@router.get("/callback")
async def callback(request: Request, code: str, state: str, db: DbDep) -> RedirectResponse:
    client = get_gauth_client()

    try:
        token_set = client.exchange_authorization_code(code, state)
    except Exception as exc:  # gnexus_gauth.exceptions.* — общий обработчик на M0
        raise HTTPException(status_code=400, detail=f"OAuth callback failed: {exc}") from exc

    user = client.fetch_user(token_set.access_token)

    session_user = {
        "user_id": user.user_id,
        "email": user.email,
        # display_name из профиля SSO; без него — username
        "name": user.profile.get("display_name") or user.profile.get("username") or "",
        "avatar_url": user.avatar_url,
        # Язык UI: SSO отдаёт profile.locale (default 'ru'); '' в поле — нет значения
        "locale": user.profile.get("locale") or "",
    }
    request.session["access_token"] = token_set.access_token
    request.session["refresh_token"] = token_set.refresh_token
    request.session["user"] = session_user

    # Профиль в БД + claim: бесхозные данные (user_id NULL) забирает вошедший
    # (ТЗ 1.2) — идемпотентно, при повторных логинах уже ничего не находит
    upsert_user(
        db,
        user.user_id,
        user.email,
        user.avatar_url,
        session_user["locale"],
        name=session_user["name"] or None,
    )
    claim_orphan_data(db, user.user_id)
    claim_global_settings(db, user.user_id)

    return_to = _safe_return_to(request.session.pop("return_to", "/"))
    return RedirectResponse(return_to)


@router.get("/logout")
async def logout(request: Request) -> RedirectResponse:
    request.session.clear()
    # на экран входа SPA: там единственная кнопка «Войти» (инициирует SSO-флоу)
    return RedirectResponse("/login")


@router.get("/me")
async def me(request: Request, db: DbDep) -> dict[str, Any]:
    session_user = request.session.get("user")
    if not session_user:
        raise HTTPException(status_code=401, detail="Not authenticated")
    # Профиль берём из БД, а не из cookie-сессии: сессия stateless, и изменения
    # от webhook SSO (webhooks.py) иначе видны только после следующего логина.
    user = db.get(User, session_user["user_id"])
    if user is not None:
        session_user = {
            "user_id": user.id,
            "email": user.email,
            "name": user.name or "",
            "avatar_url": user.avatar_url,
            "locale": user.locale or "",
        }
    # Раздел профиля в системе авторизации (SSO): {base}/account
    account_url = get_settings().gauth_base_url.rstrip("/") + "/account"
    return {"user": session_user, "account_url": account_url}
