"""Public endpoints that describe the app itself."""

from fastapi import APIRouter, Depends, Request

from app import bw_auth
from app.config import get_settings
from app.constants import (
    ASPECT_LABELS,
    LEVEL_LABELS,
    OPTION_STATUS_LABELS,
    PROJECT_STATUS_LABELS,
    RATING_LABELS,
    vocabulary,
)
from app.models.account import Account
from app.models.schemas import AccountOut, Me, Meta
from app.services import bw, levels
from app.services import view_as as view_as_service
from app.services.authz import optional_account, real_username
from app.version import VERSION

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


@router.get("/meta", response_model=Meta)
def meta() -> Meta:
    """Version and shared vocabularies. The UI renders the version in its footer —
    that is how a deploy is confirmed to have landed."""
    return Meta(
        app="scout",
        version=VERSION,
        # Every level as {value,label}. Which of these a given caller may
        # ASSIGN comes from Me.assignable_levels — levels are rows, so a static
        # "assignable on projects" list would go stale the moment one is added.
        levels=vocabulary(
            {name: LEVEL_LABELS.get(name, name) for name in levels.level_names()}
        ),
        project_statuses=vocabulary(PROJECT_STATUS_LABELS),
        option_statuses=vocabulary(OPTION_STATUS_LABELS),
        aspects=vocabulary(ASPECT_LABELS),
        rating_scale=vocabulary(RATING_LABELS),
    )


@router.get("/me", response_model=Me)
def me(request: Request, account: Account | None = Depends(optional_account)) -> Me:
    """Public. The SPA calls this on load to pick its view.

    When the caller is signed in this doubles as the presence heartbeat, so the
    access console shows their name rather than an anonymous IP — Scout runs its
    own login, so id-auth has no other way to know who is here.
    """
    settings = get_settings()
    real = real_username(request)
    if account is not None:
        client_ip = (request.headers.get("x-forwarded-for", "").split(",")[0].strip()
                     or (request.client.host if request.client else None))
        # Presence is about who is actually here, so it reports the REAL user
        # even while a View As is active; the impersonation heartbeat below is
        # what shows "rian -> viewing as darren" on the central Live page.
        bw.report_presence(real or account.username, ip=client_ip)
        view_as_service.heartbeat(request)

    view_as_fields = view_as_service.me_fields(request) if account is not None else {}
    if view_as_fields.get("impersonating") and account is not None:
        # `account` IS the effective (impersonated) user here, so its display
        # name is exactly the banner label.
        view_as_fields["viewing_as_label"] = account.display_name
    return Me(
        authenticated=account is not None,
        should_probe=(
            settings.has_bw_client
            and bw_auth.should_probe(settings.domain_mode, account is not None, request.cookies)
        ),
        sign_in_available=settings.has_bw_client,
        account=AccountOut.model_validate(account) if account is not None else None,
        # Capabilities are decided here, once, and the UI switches on them. It
        # must never re-derive policy by comparing level names.
        is_owner=account is not None and levels.is_owner(account.username),
        is_staff=account is not None and levels.app_can(account.username, levels.PROJECT_MANAGE),
        can_create_projects=(
            account is not None and levels.app_can(account.username, levels.PROJECTS_CREATE)
        ),
        can_manage_accounts=(
            account is not None and levels.app_can(account.username, levels.ACCOUNTS_VIEW)
        ),
        assignable_levels=(
            levels.assignable_by(account.username) if account is not None else []
        ),
        account_url=(
            levels.account_url(settings.bw_app_domain) if settings.has_bw_client else None
        ),
        can_view_as_others=(
            real is not None
            and account is not None
            and view_as_service.can_view_as(real, "")
        ),
        **view_as_fields,
    )
