#!/usr/bin/env python3
# 通用藏分扫描器 (混合加速版)
# 藏分 = 购买功能(免费游戏) 且 赢取=0(投注全亏)
# 加速:
#   1) 预筛: 只查 投注额 >= MIN_BET 的单(小于10不可能是买功能)
#   2) PP游戏: 直连PP接口判 purtr(不开浏览器, 快)
#   3) PG等: 无头浏览器渲染回放读"购买"字样
#   4) 缓存: 注单结算后不变, 查过的存盘, 再扫秒出
import json, os, sys, concurrent.futures
from client import api
from replay_async import check_many_sync
from pp_round import classify as pp_classify

MIN_BET = float(os.environ.get("MIN_BET", "10"))     # 低于此投注额不可能是买功能
CACHE_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "replay_cache.json")


def _load_cache():
    try:
        return json.load(open(CACHE_FILE))
    except Exception:
        return {}


def _save_cache(c):
    try:
        json.dump(c, open(CACHE_FILE, "w"))
    except Exception:
        pass


def is_pragmatic(x):
    return str(x.get("dealerCode") or "").startswith("pp")


def fetch_all_bets(filters=None, page_size=100, max_pages=200):
    filters = filters or {}
    out, page = [], 1
    while page <= max_pages:
        r = api("/betManage/userBet/list", "POST",
                params={"pageNum": page, "pageSize": page_size}, json_body=filters)
        rows = r.get("rows") or []
        out += rows
        if len(out) >= r.get("total", 0) or not rows:
            break
        page += 1
    return out


def _pp_check(bid):
    r = pp_classify(bid)
    mk = r.get("markers") or {}
    markers = [f"{k}={mk[k]}" for k in ("purtr", "req.pur") if mk.get(k) not in (None, "")]
    return {"bought": bool(r.get("bought")), "markers": markers or ["PP购买"], "via": "pp"}


def scan(filters=None, headless=True, concurrency=None):
    bets = fetch_all_bets(filters)
    # 候选: 赢取=0 且 投注额>=MIN_BET
    cands, seen = [], set()
    for x in bets:
        bid = x.get("id")
        if bid in seen:
            continue
        seen.add(bid)
        if float(x.get("betAmount") or 0) >= MIN_BET and float(x.get("win") or 0) == 0:
            cands.append(x)

    cache = _load_cache()
    by_id = {x["id"]: x for x in cands}
    todo = [x for x in cands if str(x["id"]) not in cache]
    pp_ids = [x["id"] for x in todo if is_pragmatic(x)]
    render_ids = [x["id"] for x in todo if not is_pragmatic(x)]

    errors = []
    # PP 直连(并发线程, 仅HTTP)
    if pp_ids:
        with concurrent.futures.ThreadPoolExecutor(max_workers=8) as ex:
            futs = {ex.submit(_pp_check, bid): bid for bid in pp_ids}
            for f in concurrent.futures.as_completed(futs):
                bid = futs[f]
                try:
                    cache[str(bid)] = f.result()
                except Exception as e:
                    errors.append({"betId": bid, "error": f"pp: {e}"})
    # 其它 无头渲染
    if render_ids:
        rr = check_many_sync(render_ids, headless=headless, concurrency=concurrency)
        for bid, v in rr.items():
            if v.get("error"):
                errors.append({"betId": bid, "error": v["error"]})
            else:
                cache[str(bid)] = {"bought": v["bought"], "markers": v.get("buy_markers"),
                                   "via": "render", "freeSpin": v.get("has_free_spin")}
    _save_cache(cache)

    confirmed = []
    for x in cands:
        v = cache.get(str(x["id"]))
        if v and v.get("bought"):
            confirmed.append({
                "betId": x["id"], "orderNo": x.get("orderNo"),
                "userId": x.get("userId"), "tgUserId": x.get("tgUserId"),
                "game": x.get("gameNameZh") or x.get("gameCode"), "dealer": x.get("dealerName"),
                "betAmount": float(x.get("betAmount") or 0), "win": 0.0,
                "time": x.get("createTime"), "markers": v.get("markers"), "via": v.get("via"),
            })
    return {"scanned": len(bets), "win0_ge_minbet": len(cands),
            "newly_checked": len(todo), "from_cache": len(cands) - len(todo),
            "confirmed_hidden_score": confirmed, "errors": errors}


if __name__ == "__main__":
    filters = None
    for a in sys.argv[1:]:
        if a.isdigit():
            filters = {"userId": int(a)}
    print(json.dumps(scan(filters, headless="--show" not in sys.argv), ensure_ascii=False, indent=2))
