"""Route-layer shared helpers for the API contract (R6 S1a).

Small, dependency-light guards that several route modules had hand-rolled with subtle
drift. Keeping them here (imported by the route modules) means the "fetch a row or 404"
and "fetch a row this user owns or 404" idioms have one definition, one status code, and
one existence-leak policy.
"""
from typing import Any, TypeVar

from fastapi import HTTPException
from sqlalchemy.orm import Session

from app.db import Base
from app.models import User

M = TypeVar("M", bound=Base)


def get_or_404(db: Session, model: type[M], id_: Any, *, detail: str = "Not found") -> M:
    """Fetch a row by primary key or raise 404. Replaces the `db.get(Model, id); if None: 404`
    idiom repeated across routes."""
    obj = db.get(model, id_)
    if obj is None:
        raise HTTPException(status_code=404, detail=detail)
    return obj


def owned_or_404(
    db: Session,
    model: type[M],
    id_: Any,
    user: User,
    *,
    owner_attr: str = "user_id",
    detail: str = "Not found",
) -> M:
    """Fetch a row by primary key that `user` owns, or raise 404. A row owned by someone
    else 404s (NOT 403) on purpose — a 403 would confirm the id exists to a stranger.
    Generalizes the near-identical per-route ownership helpers (e.g. plex's
    `_own_playlist_or_404`, the saved-views owner check)."""
    obj = get_or_404(db, model, id_, detail=detail)
    if getattr(obj, owner_attr) != user.id:
        raise HTTPException(status_code=404, detail=detail)
    return obj
