"""Who is the request VIEWING as? (M6, plan §4.3 — one resolver, used by
every projected read.)

The acting viewer for a read is the session player, EXCEPT when View As
is active — then it is the impersonated player, because View As exists
precisely to render someone else's game (§6.2: the build tool that
verifies every §4.2 perspective). Both the signed vb_view_as cookie and
the dev X-View-As header (dev stub only) arrive through the SAME
services.view_as.resolve() path, so the per-request fail-closed
re-authorization applies to both; a revoked/forged/expired view-as
resolves back to the real player here without any special-casing.

The resolved viewer may be None: a signed-in BW user with no Player row
yet. None flows into PerspectiveLens as the OUTSIDE OBSERVER (default-
deny, perspective.py module docstring) — they see front-line-level data
of both sides and nothing below either root. Same for a player who
exists but has no team membership in the match.

WRITES deliberately do NOT use this resolver: actions are attributed to
and validated against the REAL session player (routers keep using
auth.current_player), because a read-only View As must never act as its
target. Reads project; writes act.

Note: resolve() may signal a stale view-as cookie should be dropped
(drop_cookie). Data endpoints ignore that signal — deleting the cookie
needs the response object, and /api/me (the SPA's poll) already cleans
it on its next pass. The fail-closed part (this request renders as the
real user) still applies here.
"""
from __future__ import annotations

from sqlalchemy import select
from sqlalchemy.orm import Session
from starlette.requests import Request

from ..models import Player
from . import view_as as view_as_service
from .auth import bw_view_as


def resolve_viewer(request: Request, db: Session) -> Player | None:
    """The Player whose perspective this request renders (None = outside
    observer). Uses the request-scoped db session — no extra SessionLocal."""
    session, _drop_cookie = view_as_service.resolve(request)
    username = bw_view_as.effective_username(session)
    if not username:
        return None
    return db.execute(
        select(Player).where(Player.username == username)
    ).scalar_one_or_none()
