"""GET /api/me — the signed-in identity + view-as state, in one call.

The SPA shell consumes this on load and on a poll; that poll doubles as
the Pattern B heartbeat: report_presence tells the BW hub WHO is online
(id-auth is not in a Pattern B app's login loop, so it can't know
otherwise), report_role mirrors the app-decided role for display, and
report_impersonation keeps any active view-as visible on the hub's
oversight page. All three are best-effort telemetry — a hub blip must
never break or slow this endpoint beyond its short timeout, and a
missing BW config (dev) must never 500 it.

Auth: the default-deny middleware gates this route — reaching the
handler proves a valid session (or the dev stub).
"""
from __future__ import annotations

import logging

from fastapi import APIRouter, Request, Response

from ..services import roles
from ..services import view_as as view_as_service
from ..services.auth import bw_auth, bw_view_as
from ..services.auth import session as auth_session

log = logging.getLogger("volleyboard.me")

router = APIRouter()


def _client_ip(request: Request) -> str | None:
    """First X-Forwarded-For hop (Caddy appends the real client first),
    else the direct peer."""
    fwd = (request.headers.get("X-Forwarded-For") or "").split(",")[0].strip()
    if fwd:
        return fwd
    return request.client.host if request.client else None


def _report_safe(fn, *args, **kwargs) -> None:
    """Best-effort hub telemetry: report_* swallow transport errors
    themselves, but a missing BW config (dev/test) raises BWAuthError
    from the lazy env readers — never let that surface here."""
    try:
        fn(*args, **kwargs)
    except bw_auth.BWAuthError as e:
        log.debug("hub report skipped: %s", e)


@router.get("/api/me")
def api_me(request: Request, response: Response) -> dict:
    identity: dict = getattr(request.state, "identity", None) or {}
    username = request.state.username

    # PER-REQUEST RE-AUTHORIZATION of any active view-as (VIEW-AS.md step
    # 3) BEFORE resolving the effective user or reporting anything. A
    # revoked/deleted/escalated case fail-closes here; if the dropped
    # state came from the cookie, delete it so it can't come back.
    session, drop_cookie = view_as_service.resolve(request)
    if drop_cookie:
        response.delete_cookie(auth_session.VIEW_AS_COOKIE, path="/")

    # Heartbeats (best-effort, after verify() so a just-dropped view-as
    # is never reported as live).
    role = roles.role_of(username)
    _report_safe(bw_auth.report_presence, username, ip=_client_ip(request))
    _report_safe(bw_auth.report_role, username, role)
    if bw_view_as.is_impersonating(session):
        _report_safe(
            bw_auth.report_impersonation,
            bw_view_as.real_username(session),
            bw_view_as.effective_username(session),
            mode=bw_view_as.mode(session),
        )

    # Same policy shape as view_as_service.can_view_as, reusing the role
    # already computed above (the picker is offered only to admins/leads).
    can_start_view_as = roles.is_admin(username) or role == roles.ROLE_LEAD
    body: dict = {
        "authenticated": True,
        "username": username,
        "email": identity.get("email", ""),
        "display_name": view_as_service.display_label(username),
        "role": role,
        "is_admin": roles.is_admin(username),
    }
    # The STANDARD impersonation block (drop-in helper — same shape in
    # every BW app): {impersonating, viewing_as, viewing_as_label,
    # real_user, can_write, mode}. The SPA draws its banner + gates
    # writes off these.
    body.update(bw_view_as.me_fields(session, label=view_as_service.display_label))
    body["view_as_targets"] = (
        view_as_service.viewable_targets(username) if can_start_view_as else []
    )
    return body
