"""Webhook от gnexus-auth (ТЗ 3.16).

SSO асинхронно доставляет подписанные события (контракт — docs/04-events-and-webhooks.md
в репозитории gnexus-auth): изменения профиля и статуса пользователя. Эндпоинт
открытый — подлинность гарантирует HMAC-SHA256-подпись (заголовки X-GNexus-*),
секрет GAUTH_WEBHOOK_SECRET задаётся при создании webhook в админке SSO.
Не задан — приём webhook выключен (503).

Обрабатываем подмножество, значимое для gntodo:
- user.email_changed / user.profile_updated — синхронизация профиля User;
- user.blocked / user.archived / user.deleted — отзыв всех MCP-токенов
  (аккаунт более недоступен в SSO — агент не должен действовать от его имени).
Ограничение: cookie-сессии stateless, серверно их не отозвать — доступ
пользователю закрывается на стороне SSO. Остальные события (включая
webhook.test и новые типы) подтверждаем 200 без действий — чтобы SSO не
гонял retry'и по незнакомому типу.
"""

import logging

from fastapi import APIRouter, HTTPException, Request
from gnexus_gauth.dto import WebhookEvent
from sqlalchemy import delete

from app.auth.client import get_gauth_client
from app.config import get_settings
from app.dependencies import DbDep
from app.models import McpToken, User

logger = logging.getLogger(__name__)

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

# Аккаунт более недоступен в SSO — MCP-токены отзываем (агент теряет доступ).
REVOKE_EVENTS = frozenset({"user.blocked", "user.archived", "user.deleted"})


@router.post("/webhook")
async def webhook(request: Request, db: DbDep) -> dict[str, bool]:
    secret = get_settings().gauth_webhook_secret
    if not secret:
        raise HTTPException(status_code=503, detail="Webhook is not configured")

    raw = (await request.body()).decode()
    try:
        event = get_gauth_client().verify_and_parse_webhook(raw, dict(request.headers), secret)
    except Exception as exc:  # WebhookVerification/PayloadException из gnexus_gauth
        logger.warning("gauth webhook отклонён: %s", exc)
        raise HTTPException(status_code=400, detail="Invalid webhook") from exc

    _handle(db, event)
    return {"ok": True}


def _handle(db: DbDep, event: WebhookEvent) -> None:
    data = event.metadata
    # user.id в data; для событий без data.user — target.user_id (базовый payload)
    data_user = (data.get("user") or {}).get("id")
    target_user = event.target_identifiers.get("user_id")
    user_id = str(data_user or target_user or "")

    if event.event_type in REVOKE_EVENTS:
        if user_id:
            db.execute(delete(McpToken).where(McpToken.user_id == user_id))
            logger.info(
                "gauth webhook %s: MCP-токены пользователя %s отозваны", event.event_type, user_id
            )
        return

    user = db.get(User, user_id) if user_id else None

    if event.event_type == "user.email_changed":
        email = data.get("new_email")
        if user is not None and email:
            user.email = str(email)
        return

    if event.event_type == "user.profile_updated":
        profile = data.get("profile") or {}
        if user is not None:
            if "locale" in profile:
                user.locale = profile.get("locale") or ""
            if "avatar_url" in profile:
                user.avatar_url = profile.get("avatar_url")
        return

    logger.info("gauth webhook %s (user=%s) — без действий", event.event_type, user_id or "?")
