# -*- coding: utf-8 -*-
"""导出 device_liveness 表里所有 device_id 及其对应的推送 regId,按 push_vendor 分组(本地开发工具)。

用法:
  双击 scripts/show_device_regids.bat,或命令行:
    python scripts/show_device_regids.py [filter]

  [filter] 可选:大小写不敏感的子串,匹配 device_id / push_vendor / push_token /
  registration_id 任一列;不传则列出全部。
    show_device_regids.py xiaomi          # 只看小米
    show_device_regids.py device_Pixel    # 按 device_id 片段找

说明:
- 直接以**只读**方式读 SQLite(server 在跑也不会抢写锁),所以开不开服务器都能用。
- push_token = 各厂商的 regId/pushToken(新链路,当前在用);registration_id = 旧极光
  regId(历史兼容)。两列都打出来,方便跟 logcat 现役值逐字比对。
- 输出刻意保持纯 ASCII 排版并竖排展示**完整值**:双击弹出的 cmd 走 GBK 码页,竖排纯
  ASCII 不会乱码;完整值不截断,才能直接复制去比对。
- 若 DATABASE_URL 不是 sqlite(生产 postgres),这里只提示改用 psql。
"""
from __future__ import annotations

import os
import sqlite3
import sys
from pathlib import Path

REPO_ROOT = Path(__file__).resolve().parent.parent


def _resolve_sqlite_path() -> Path | None:
    """按 env > .env > 默认 的顺序拿 DATABASE_URL,解析出 sqlite 文件路径。"""
    url = os.environ.get("DATABASE_URL", "").strip()
    if not url:
        env = REPO_ROOT / ".env"
        if env.exists():
            for line in env.read_text(encoding="utf-8", errors="ignore").splitlines():
                s = line.strip()
                if s.startswith("DATABASE_URL="):
                    url = s.split("=", 1)[1].strip().strip('"').strip("'")
                    break
    if not url:
        url = "sqlite:///./data/app.db"
    if not url.startswith("sqlite"):
        print(f"[!] DATABASE_URL not sqlite: {url}")
        print("    This tool only reads a local SQLite dev DB. For prod use psql.")
        return None
    raw = url.split("///", 1)[1] if "///" in url else "./data/app.db"
    p = Path(raw)
    if not p.is_absolute():
        p = (REPO_ROOT / raw).resolve()
    return p


def main() -> int:
    needle = sys.argv[1].lower() if len(sys.argv) > 1 else None

    db = _resolve_sqlite_path()
    if db is None:
        return 2
    if not db.exists():
        print(f"[X] DB file not found: {db}")
        return 2

    # 只读打开,避免和正在运行的 server 抢写锁。URI 路径用正斜杠(as_posix)规避
    # Windows 反斜杠/盘符在 file: URI 里的歧义;万一 URI 形式打不开再回退普通连接。
    try:
        con = sqlite3.connect(f"file:{db.as_posix()}?mode=ro", uri=True)
    except sqlite3.OperationalError:
        con = sqlite3.connect(str(db))
    con.row_factory = sqlite3.Row
    rows = con.execute(
        """
        SELECT id, user_id, device_id, push_vendor, push_token, registration_id,
               platform, app_version, liveness_state, last_heartbeat_at, updated_at
        FROM device_liveness
        ORDER BY updated_at DESC
        """
    ).fetchall()
    con.close()

    if needle:
        def hit(r: sqlite3.Row) -> bool:
            for k in ("device_id", "push_vendor", "push_token", "registration_id"):
                v = r[k]
                if v and needle in str(v).lower():
                    return True
            return False

        rows = [r for r in rows if hit(r)]

    with_token = sum(1 for r in rows if (r["push_token"] or "").strip())
    with_reg = sum(1 for r in rows if (r["registration_id"] or "").strip())

    # 按 push_vendor 分组;None/空归入 "(none)"。组顺序:设备数多的在前,(none) 垫底;
    # 组内沿用 updated_at DESC(rows 查询时已如此排序,dict 保序即可)。
    groups: dict[str, list] = {}
    for r in rows:
        groups.setdefault(r["push_vendor"] or "(none)", []).append(r)
    ordered = sorted(
        groups.items(), key=lambda kv: (kv[0] == "(none)", -len(kv[1]), kv[0])
    )
    tally = "  ".join(f"{v}={len(items)}" for v, items in ordered) or "-"

    print(f"DB : {db}")
    line = f"device_liveness : {len(rows)} row(s)"
    if needle:
        line += f'   filter="{sys.argv[1]}"'
    print(line)
    print(f"has push_token(vendor regId) : {with_token}"
          f"    has registration_id(jiguang) : {with_reg}")
    print(f"vendors : {tally}")
    print("=" * 72)

    if not rows:
        print("(no rows)")
        return 0

    for vendor, items in ordered:
        print()
        print(f"===== vendor={vendor} : {len(items)} device(s) =====")
        for r in items:
            print(f"  [#{r['id']}]  user_id={r['user_id']}  platform={r['platform']}"
                  f"  state={r['liveness_state']}  app={r['app_version'] or '-'}")
            print(f"    device_id       : {r['device_id']}")
            print(f"    push_token(regId): {r['push_token'] or '(empty)'}")
            print(f"    registration_id : {r['registration_id'] or '(empty)'}")
            print(f"    last_heartbeat  : {r['last_heartbeat_at'] or '-'}"
                  f"    updated : {r['updated_at']}")
            print("    " + "-" * 68)

    return 0


if __name__ == "__main__":
    raise SystemExit(main())
