"""Auto-provisioning: a first-ever BW username gets a Player row.

COLUMN-SCOPED on re-login (kit §5c / with's provision.py): only the
BW-derived mirror (email) is refreshed. display_name, avatar_color, and
sound_settings are APP-OWNED and are NEVER touched by a login — so an
edit made between two logins survives the second one. display_name is
seeded ONCE on first insert (from the BW first/last claims) as a
starting value; thereafter the app owns it.
"""

import logging
from collections.abc import Mapping

from sqlalchemy import select
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import Session

from app.models import Player

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


def provision_player(db_session: Session, claims: Mapping) -> Player:
    """Upsert the Player row for a VERIFIED identity (id_token claims from
    bw_auth.verify_id_token — never an unverified source) and return it.
    Commits."""
    username = (claims.get("username") or claims.get("sub") or "").strip()
    if not username:
        raise ValueError("verified claims carry no username")
    email = (claims.get("email") or "").strip()

    player = db_session.scalar(select(Player).where(Player.username == username))
    if player is None:
        seeded_name = f"{claims.get('first') or ''} {claims.get('last') or ''}".strip()
        player = Player(
            username=username,
            email=email or None,
            display_name=seeded_name or None,  # seed once; app-owned thereafter
        )
        db_session.add(player)
        try:
            db_session.commit()
            log.info("auto-provisioned player row for %r", username)
            return player
        except IntegrityError:
            # Concurrent first login for the same username — take the winner's row.
            db_session.rollback()
            player = db_session.scalar(
                select(Player).where(Player.username == username)
            )
            if player is None:  # pragma: no cover — unique race with no row can't happen
                raise

    if email and player.email != email:
        player.email = email  # the ONLY column a re-login may touch
        db_session.commit()
    return player
