"""Audyt + remediacja scen "teaser-only" (data quality).

Problem (bug-report 2026-06-01, scena 48d6cc6b): scena ma canonical
`scene.duration_sec` z TPDB (prawdziwa długość studyjnej sceny, np. 22min), ale
JEDYNE żywe playback_source to krótki tubowy teaser/klip (np. xnxx 21s). Apka
pokazuje 22min (hero z canonical) a gra 21s → mylące "22m a wideo <1m".

Fix: gdy WSZYSTKIE żywe źródła sceny są znikomą częścią canonical, scena nie ma
realnego playbacku — oznaczamy te źródła `dead_at` → scena traci żywe źródła →
`has_playback=false` → ukryta (zgodnie z polityką orphanów: nie pokazujemy scen
których nie da się realnie obejrzeć).

Konserwatywne guardy (minimalizują fałszywe ukrycia):
- canonical (scene.duration_sec) musi być > MIN_CANON (znamy prawdziwą długość)
- WSZYSTKIE żywe źródła muszą mieć ZNANĄ duration (n_live == n_with_duration) —
  źródło bez duration mogłoby być pełną sceną, więc wtedy pomijamy
- nawet NAJDŁUŻSZE żywe źródło < RATIO * canonical (czyli żaden nie jest pełny)

Reversible (dead_at, NIE delete). Domyślnie dry-run; --yes zapisuje.

Uruchomienie:
  python -m scripts.audit_teaser_only            # dry-run (podsumowanie)
  python -m scripts.audit_teaser_only --list 30  # + lista scen
  python -m scripts.audit_teaser_only --yes      # wykonaj
"""
from __future__ import annotations

import argparse

from sqlalchemy import text

from app.db import engine

MIN_CANON = 300    # canonical > 5min — znamy realną długość pełnej sceny
RATIO = 0.15       # nawet najdłuższe żywe źródło < 15% canonical = brak pełnej sceny

_TEASER_SCENES_SQL = """
WITH live AS (
    SELECT scene_id,
           count(*) AS n_live,
           count(*) FILTER (WHERE duration_sec IS NOT NULL AND duration_sec > 0) AS n_dur,
           max(duration_sec) AS max_dur
    FROM playback_sources
    WHERE dead_at IS NULL
    GROUP BY scene_id
)
SELECT l.scene_id, sc.duration_sec AS canonical, l.n_live, l.max_dur
FROM live l
JOIN scenes sc ON sc.id = l.scene_id
WHERE sc.duration_sec > :min_canon
  AND l.n_live = l.n_dur                       -- wszystkie żywe źródła mają znaną duration
  AND l.max_dur < sc.duration_sec * :ratio     -- żaden nie jest "pełny"
"""


def main() -> None:
    ap = argparse.ArgumentParser(description=__doc__)
    ap.add_argument("--list", type=int, default=0, metavar="N")
    ap.add_argument("--yes", action="store_true", help="wykonaj (bez tego dry-run)")
    args = ap.parse_args()

    params = {"min_canon": MIN_CANON, "ratio": RATIO}
    with engine.connect() as conn:
        rows = conn.execute(text(_TEASER_SCENES_SQL), params).fetchall()
        n_scenes = len(rows)
        scene_ids = [r[0] for r in rows]
        n_sources = 0
        if scene_ids:
            n_sources = conn.execute(text(
                "SELECT count(*) FROM playback_sources "
                "WHERE dead_at IS NULL AND scene_id = ANY(:ids)"
            ), {"ids": scene_ids}).scalar()
        print(f"teaser-only scenes (canonical>{MIN_CANON}s, all live sources <{int(RATIO*100)}% canonical): {n_scenes}")
        print(f"live sources that would be marked dead: {n_sources}")

        if args.list:
            for r in rows[: args.list]:
                title = conn.execute(text("SELECT title FROM scenes WHERE id=:i"), {"i": r[0]}).scalar()
                print(f"  {str(r[0])[:8]} canonical={r[1]}s n_live={r[2]} max_src={r[3]}s  {(title or '')[:55]}")

        if args.yes and scene_ids:
            res = conn.execute(text("""
                UPDATE playback_sources
                SET dead_at = now(),
                    dead_reason = 'teaser-only audit: all live sources <15% of canonical (orphan-hide)'
                WHERE dead_at IS NULL AND scene_id = ANY(:ids)
            """), {"ids": scene_ids})
            conn.commit()
            print(f"\nAPPLIED: marked {res.rowcount} sources dead across {n_scenes} scenes (reversible: dead_at)")
        elif not args.yes:
            print("\n(dry-run — uruchom z --yes aby zapisać)")


if __name__ == "__main__":
    main()
