#!/usr/bin/env python3
"""[TR-CLI-2026] Read-only диагностика «Торги без риска» (tbr) — сайт lots.zbrcheck.ru + бот @TorgiBezRiska_bot.

Обёртка над существующим кодом/БД/логами tbr — ничего не мутирует. Метод «CLI вместо MCP».
Запуск на mac mini:
  TBR_DB_PATH=~/Services/tbr/data/db/tbr.db PYTHONPATH=~/Services/tbr:~/Services/zemlya-bot \
  ~/Services/zemlya-bot/venv/bin/python3 tools/tbr_cli.py <cmd> [args]

Команды:
  health                 сервисы tbr (launchctl) + HTTP API :20152 + torgi.gov.ru статус
  districts <region>     фасет районов области (коды: 47=Ленобл, 50=МО, 78=СПб…) со счётчиками
  feed <region> [дистрикты csv]  проверка фильтра ленты по районам (total + сверка)
  db                     счётчики lots/valuations/bookings + покрытие district
  kind                   [TR-MULTISRC-2026] лоты по источнику/типу торгов (муниц/банкрот) + сверка ленты
  logs <фильтр> [-n N]   греп по логу бота (~/Services/shared/logs/tbr-bot.log)
"""
import os, sys, json, subprocess, urllib.request, urllib.parse

HOME = os.path.expanduser("~")
API = "http://127.0.0.1:20152"


def _c(s, code): return f"\033[{code}m{s}\033[0m" if sys.stdout.isatty() else str(s)
def ok(s): return _c(s, "32")
def bad(s): return _c(s, "31")
def dim(s): return _c(s, "90")


def _get(path):
    try:
        with urllib.request.urlopen(API + path, timeout=10) as r:
            return r.status, json.loads(r.read().decode("utf-8", "replace"))
    except Exception as e:
        return None, str(e)


def cmd_health(args):
    out = subprocess.run(["launchctl", "list"], capture_output=True, text=True).stdout
    print("Сервисы Торгов:")
    for line in out.splitlines():
        p = line.split("\t")
        if len(p) < 3 or "tbr" not in p[2]:
            continue
        pid, ex, label = p
        st = ok("● up  ") if pid.strip().isdigit() else (bad("○ down") if ex not in ("0", "-") else dim("· idle"))
        print(f"  {st} {label:32} pid={pid:>7} exit={ex}")
    st, body = _get("/health")
    print(f"\nAPI :20152/health -> {ok('ok') if st == 200 else bad(st)}")
    st, body = _get("/api/source-status")
    if st == 200:
        s = body.get("ok"); print(f"torgi.gov.ru -> {ok('в норме') if s else bad(body.get('label','?'))}")


def cmd_districts(args):
    if not args:
        print(bad("нужен код региона: tbr districts 47")); return
    reg = args[0]
    st, body = _get(f"/api/facets?region={reg}")
    if st != 200:
        print(bad(f"HTTP {st}: {body}")); return
    ds = body.get("districts", [])
    print(f"Районы региона {reg} (выгодные лоты ленты):")
    tot = 0
    for d in ds:
        name = "Прочие" if d["value"] == "__none__" else d["value"]
        print(f"   {d['count']:>4}  {name}")
        tot += d["count"]
    print(dim(f"— всего {tot} лотов в {len(ds)} группах"))


def cmd_feed(args):
    if not args:
        print(bad("нужен код региона: tbr feed 47 [Всеволожский,Гатчинский]")); return
    reg = args[0]
    q = {"region": reg, "limit": "1"}
    st, base = _get("/api/lots/feed?" + urllib.parse.urlencode(q))
    print(f"регион {reg}: всего в ленте = {base.get('total') if st==200 else bad(st)}")
    if len(args) > 1:
        q2 = {"region": reg, "limit": "1", "districts": args[1]}
        st, f = _get("/api/lots/feed?" + urllib.parse.urlencode(q2))
        print(f"районы [{args[1]}]: {ok(f.get('total')) if st==200 else bad(st)}")


def cmd_db(args):
    import sqlite3
    p = os.environ.get("TBR_DB_PATH", f"{HOME}/Services/tbr/data/db/tbr.db")
    c = sqlite3.connect(f"file:{p}?mode=ro", uri=True)
    lots = c.execute("SELECT COUNT(*) FROM lots").fetchone()[0]
    withd = c.execute("SELECT COUNT(*) FROM lots WHERE district IS NOT NULL AND district!=''").fetchone()[0]
    vals = c.execute("SELECT COUNT(*) FROM valuations").fetchone()[0]
    bk = dict(c.execute("SELECT status, COUNT(*) FROM bookings GROUP BY status").fetchall())
    print(f"lots:       {lots}  (район у {withd} = {withd*100//lots if lots else 0}%)")
    print(f"valuations: {vals}")
    print(f"bookings:   {bk}")
    c.close()


def cmd_logs(args):
    if not args:
        print(bad("нужен фильтр: tbr logs district")); return
    flt = args[0]
    n = 40
    if "-n" in args:
        try: n = int(args[args.index("-n") + 1])
        except Exception: pass
    log = f"{HOME}/Services/shared/logs/tbr-bot.log"
    if not os.path.exists(log):
        print(bad(f"нет лога {log}")); return
    with open(log, encoding="utf-8", errors="replace") as f:
        hits = [ln.rstrip() for ln in f if flt.lower() in ln.lower()]
    for ln in hits[-n:]:
        print(ln)
    print(dim(f"— {len(hits)} совпадений"))


def cmd_kind(args):
    """[TR-MULTISRC-2026] Источник/тип торгов: счётчики в БД + сверка ленты по kind."""
    import sqlite3
    p = os.environ.get("TBR_DB_PATH", f"{HOME}/Services/tbr/data/db/tbr.db")
    c = sqlite3.connect(f"file:{p}?mode=ro", uri=True)
    print("lots в БД по источнику/типу:")
    for src, ak, n in c.execute(
        "SELECT COALESCE(source,'?'), COALESCE(auction_kind,'?'), COUNT(*) "
        "FROM lots GROUP BY source, auction_kind ORDER BY COUNT(*) DESC"
    ).fetchall():
        print(f"  {src:10s} {ak:12s} {n}")
    print("выгодных (оценка+дисконт в диапазоне) по типу:")
    for ak, n in c.execute(
        "SELECT COALESCE(l.auction_kind,'municipal'), COUNT(*) FROM lots l "
        "JOIN valuations v ON v.lot_id=l.id "
        "WHERE v.discount_pct IS NOT NULL GROUP BY l.auction_kind"
    ).fetchall():
        print(f"  {ak:12s} {n}")
    c.close()
    print("лента API (/api/lots/feed?kind=):")
    for k in ("all", "municipal", "bankruptcy"):
        st, d = _get(f"/api/lots/feed?kind={k}&limit=1")
        tot = d.get("total") if isinstance(d, dict) else d
        print(f"  {k:11s} total={tot}")
    st, fac = _get("/api/facets")
    if isinstance(fac, dict) and fac.get("kinds"):
        print("фасет kinds:", fac["kinds"])


CMDS = {"health": cmd_health, "districts": cmd_districts, "feed": cmd_feed, "db": cmd_db, "logs": cmd_logs, "kind": cmd_kind}


def main():
    if len(sys.argv) < 2 or sys.argv[1] in ("-h", "--help", "help"):
        print(__doc__); return
    fn = CMDS.get(sys.argv[1])
    if not fn:
        print(bad(f"неизвестно: {sys.argv[1]}"), "| доступно:", ", ".join(CMDS)); sys.exit(2)
    fn(sys.argv[2:])


if __name__ == "__main__":
    main()
