"""BW Auth (Pattern B) identity → app account bridge.

The tail shared by the login callback and (future) account-mode middleware:
given a trusted BW identity (verified id_token claims, or a userinfo dict),
upsert the app's `users` row keyed on the BW username, seed the access level
on first login, and mirror that access level to the BW hub via report_role.

Roles/access levels are the APP's decision — BW never assigns them.
report_role is DISPLAY-ONLY in the hub and grants nothing.
"""
from __future__ import annotations

from datetime import datetime, timezone

from sqlalchemy import select
from sqlalchemy.orm import Session

from .. import bw_auth
from ..models.identity import GardenMember, User

# The app seeds an access level on a user's FIRST login. Thereafter it lives
# in the DB (editable) and is left untouched by re-login. BW never sets these.
INITIAL_ACCESS_LEVELS: dict[str, str] = {
    "rian": "Garden Owner",
    "bev": "Garden Admirer",
}
DEFAULT_ACCESS_LEVEL = "Garden Admirer"


def establish_user(db: Session, identity: dict) -> User:
    """Upsert the app account from a trusted BW identity and return the row.

    COLUMN-SCOPED: on an existing user we refresh only the BW-derived fields
    (display_name/email/last_seen_at). access_level and city (app-owned) are
    NEVER overwritten here, so re-login can't clobber them.
    """
    username = (identity.get("username") or "").strip().lower()
    if not username:
        raise ValueError("BW identity missing username")
    email = identity.get("email") or ""
    first = identity.get("first") or ""
    last = identity.get("last") or ""
    display = (f"{first} {last}".strip()) or username
    now = datetime.now(timezone.utc)

    user = db.scalar(select(User).where(User.external_username == username))
    if user is None:
        user = User(
            external_username=username,
            display_name=display,
            email=email,
            access_level=INITIAL_ACCESS_LEVELS.get(username, DEFAULT_ACCESS_LEVEL),
            created_at=now,
            last_seen_at=now,
        )
        db.add(user)
        db.flush()
    else:
        # Refresh BW-derived mirror fields only. city is app-owned, untouched.
        user.display_name = display or user.display_name
        user.email = email or user.email
        user.last_seen_at = now
        # Seed a known user's access level if it isn't set yet — e.g. rian was
        # seeded at migration BEFORE this column existed, so his row pre-exists
        # with access_level=''. Never overwrite a non-empty level (the app owns
        # it thereafter; only the first-ever assignment is seeded).
        if not user.access_level and username in INITIAL_ACCESS_LEVELS:
            user.access_level = INITIAL_ACCESS_LEVELS[username]

    # Ensure garden membership (single-garden today; owner was seeded at
    # migration, other users auto-join garden 1 as member).
    member = db.scalar(
        select(GardenMember).where(GardenMember.user_id == user.id)
    )
    if member is None:
        db.add(
            GardenMember(
                garden_id=1, user_id=user.id, role="member", created_at=now
            )
        )
        db.flush()
    return user


def report_access_level(username: str, access_level: str) -> None:
    """Mirror the app's access level to the BW hub (best-effort; never fails
    login over a cosmetic report)."""
    try:
        bw_auth.report_role(username, access_level or None)
    except bw_auth.BWAuthError:
        # Display-only cosmetics — swallow. The app remains the authority.
        pass
