"""Source ranking — policzona ocena 0-5★ per origin (tube źródło) dla Sites screen.

User request: ranking stron-źródeł wg (1) częstotliwości odświeżania, (2) bogactwa
metadanych (miniaturka/tagi/desc/aktorzy), (3) szybkości działania (czy realnie gra).
0★ = offline. Liczone okresowo przez `run_source_stats` (scheduler), bo richness to
agregat po ~2M live playback_sources × scenach — za drogie na request /sources.

Jeden wiersz per origin. `components` (JSONB) trzyma surowe składowe do rozkładu w UI
(% thumb/tag/perf/desc/studio/dur, telemetria health, basis). Top-level kolumny to
gotowe gwiazdki, żeby /sources tylko czytał i sortował.
"""
from __future__ import annotations

from datetime import datetime

from sqlalchemy import DateTime, Integer, SmallInteger, String, func
from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy.orm import Mapped, mapped_column

from app.models.base import Base


class SourceStats(Base):
    __tablename__ = "source_stats"

    origin: Mapped[str] = mapped_column(String(64), primary_key=True)
    # Ocena ogólna 0-5 (0 = offline). Gate: health==0 → stars 0.
    stars: Mapped[int] = mapped_column(SmallInteger, nullable=False, default=0)
    freshness: Mapped[int] = mapped_column(SmallInteger, nullable=False, default=0)
    richness: Mapped[int] = mapped_column(SmallInteger, nullable=False, default=0)
    # None gdy brak jakiegokolwiek sygnału (nie powinno się zdarzyć — proxy zawsze daje).
    health: Mapped[int | None] = mapped_column(SmallInteger, nullable=True)
    scenes: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    new_7d: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    newest_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    # Surowe składowe do rozkładu w UI + diagnostyki. Patrz run_source_stats.
    components: Mapped[dict] = mapped_column(JSONB, nullable=False, default=dict)
    computed_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now(), nullable=False
    )
