"""収集 DB の定期整理 (2026-09-21、 user 要望 「オーバーフローしないように定期的に整理」)。

方針 = 集計してから間引く:
  - 生の行は全ソース KEEP_DAYS (90 日) 保持
  - 大量ソース (genre が sns / board = Bluesky / Fediverse / 5ch) は、 KEEP_DAYS を過ぎた count=1 の行だけ削除
    (2 回以上出た本文は語彙の証拠なので残す。 配信コメント youtube/twitch は全保持)
  - 削除後に VACUUM。 実行前後のサイズと削除数を data/health_prune.tsv に追記
  - DB が WARN_GB を超えていたら stderr に警告 (systemd の journal に出る)

  python scripts/prune_db.py [--db data/comments.sqlite] [--dry]
systemd user timer で月 1 回 (deploy 時に stream-comments-prune.timer を入れる)。 日次バックアップ (04:30) の後に走らせる。
"""
from __future__ import annotations

import argparse
import pathlib
import sqlite3
import sys
import time

ROOT = pathlib.Path(__file__).resolve().parent.parent
KEEP_DAYS = 90
BULK_GENRES = ("sns", "board")
WARN_GB = 20.0


def main() -> int:
    ap = argparse.ArgumentParser()
    ap.add_argument("--db", default=str(ROOT / "data" / "comments.sqlite"))
    ap.add_argument("--dry", action="store_true")
    ap.add_argument("--keep-days", type=int, default=KEEP_DAYS)
    a = ap.parse_args()
    db = pathlib.Path(a.db)
    before = db.stat().st_size / 2**30
    conn = sqlite3.connect(db)
    cutoff = int((time.time() - a.keep_days * 86400) * 1000)
    gids = [r[0] for r in conn.execute("SELECT id FROM genres WHERE name IN (%s)" % ",".join("?" * len(BULK_GENRES)), BULK_GENRES)]
    if not gids:
        print("[prune] bulk genre 無し (何もしない)")
        return 0
    q = ",".join(str(g) for g in gids)
    # 対象: 90 日より古く、 count=1 で、 bulk genre にだけ属する行 (配信コメントとの共有行は残す)
    sel = f"""
        SELECT c.rowid FROM comments c
        WHERE c.count = 1 AND c.first_ts < ?
          AND EXISTS (SELECT 1 FROM comment_genres g WHERE g.cid = c.rowid AND g.gid IN ({q}))
          AND NOT EXISTS (SELECT 1 FROM comment_genres g WHERE g.cid = c.rowid AND g.gid NOT IN ({q}))
    """
    n = conn.execute(f"SELECT COUNT(*) FROM ({sel})", (cutoff,)).fetchone()[0]
    total = conn.execute("SELECT COUNT(*) FROM comments").fetchone()[0]
    print(f"[prune] db={before:.2f} GB rows={total:,} 対象 (bulk, count=1, >{a.keep_days}d) = {n:,}", flush=True)
    if a.dry or n == 0:
        conn.close()
        return 0
    conn.execute(f"DELETE FROM comment_genres WHERE cid IN ({sel})", (cutoff,))
    conn.execute(f"DELETE FROM comments WHERE rowid IN ({sel})", (cutoff,))
    conn.commit()
    conn.execute("VACUUM")
    conn.close()
    after = db.stat().st_size / 2**30
    with (ROOT / "data" / "health_prune.tsv").open("a", encoding="utf-8") as f:
        f.write(f"{int(time.time())}\t{before:.2f}\t{after:.2f}\t{n}\n")
    print(f"[prune] deleted {n:,} rows, {before:.2f} → {after:.2f} GB", flush=True)
    if after > WARN_GB:
        print(f"[prune] WARNING: db {after:.1f} GB > {WARN_GB} GB", file=sys.stderr, flush=True)
    return 0


if __name__ == "__main__":
    sys.exit(main())
