"""Deadlock markers, in the background.

Three stages, deliberately separate because they cost wildly different
things. The sync is two HTTP calls. Pinning the clock decodes video and
costs minutes of CPU per match. Materializing is pure SQLite.

Nothing here runs unless the user has told Settings which Deadlock account
is theirs — there is no way to guess it, and a community API is not
somewhere to go fishing.
"""
import threading
import time

from clipshelf import config, db, problems
from clipshelf.games import deadlock_record as dl

POLL_S = 1800            # the record does not change while you are playing
IDLE_POLL_S = 3600       # nothing configured: check far less often
SETTLE_S = 300           # same rule the proxy worker uses: still recording


def _configured() -> bool:
    return bool(str(config.CFG.get("deadlock_accounts", "")).strip())


def _vods_needing_markers(conn) -> list:
    """Recordings that have Deadlock matches but no markers from them yet.

    Keyed on the chapters rather than the events: a match with a pinned
    clock always produces a chapter, but a quiet match can legitimately
    produce zero events, and keying on events would re-do it forever."""
    return [r[0] for r in conn.execute(
        "SELECT DISTINCT s.vod_id FROM deadlock_match_stats s"
        " WHERE s.vod_id IS NOT NULL AND s.clock_offset0 IS NOT NULL"
        " AND NOT EXISTS (SELECT 1 FROM vod_chapters c WHERE c.vod_id = s.vod_id"
        "                 AND c.game_tag IN ('deadlock','street_brawl'))"
        " ORDER BY s.start_time DESC")]


def cycle(verbose: bool = False) -> dict:
    """One pass. Returns what it did, so the worker can log something true."""
    conn = db.connect()
    try:
        out = dl.sync(conn, verbose=verbose)

        # Pinning reads the recording, so leave a file OBS may still be
        # writing alone — a half-written night reads as a short one and
        # would pin against footage that is about to change underneath it.
        dl.calibrate_all(conn, verbose=verbose, vod_dir=config.VOD_DIR,
                             only_missing=True)

        marked = []
        for vod_id in _vods_needing_markers(conn):
            dl.ensure_metadata(conn, vod_id)
            marked.append(dl.materialize(conn, vod_id))
        out["marked"] = marked
        return out
    finally:
        conn.close()


def loop():
    time.sleep(90)                      # let the library sync find the night first
    while True:
        wait = POLL_S
        try:
            if not _configured():
                wait = IDLE_POLL_S
            elif dl.backoff_left() > 0:
                # the API told us to stop; do not spend the cycle finding out
                # again, and do not report it as a problem the user can fix
                wait = min(POLL_S, dl.backoff_left() + 30)
            else:
                r = cycle()
                for m in r.get("marked", []):
                    print(f"[deadlock] vod {m['vod_id']}: {m['events']} markers "
                          f"across {m['chapters']} matches"
                          + (f" ({m['unaligned']} unpinned)" if m.get("unaligned") else ""),
                          flush=True)
        except Exception as e:                                  # noqa: BLE001
            problems.record("deadlock markers", e)
        time.sleep(wait)


def start():
    threading.Thread(target=loop, daemon=True, name="deadlock").start()
