"""人工验证用:按「金币/现金/邀请」排列组合 + 活跃/新用户对照,造一批账号。

用法(仓库根目录,venv 解释器):
    .venv/Scripts/python.exe scripts/seed_inactivity_cases.py          # 造号(会先清掉上次 vcase*)
    .venv/Scripts/python.exe scripts/seed_inactivity_cases.py --clean  # 只清理,不造

配合默认配置 INACTIVITY_RESET_DAYS=15 / INACTIVITY_WARN_DAYS_BEFORE=7,2 验证。
造完把 worker 打开(见 README/对话里的 .env),启动服务即会在 RUN_HOUR 后跑一轮。

⚠️ worker 清零针对**库里所有**符合条件的用户,不止 vcase*——dev 库里若有其它"老且有余额、
无近期活跃事件"的用户,也会被一起清。要干净验证建议用一个空/副本 dev 库。
"""
from __future__ import annotations

import os
import sys
from datetime import UTC, datetime, timedelta

sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

from sqlalchemy import delete, select  # noqa: E402

from app.db.session import SessionLocal  # noqa: E402
from app.models.analytics_event import AnalyticsEvent  # noqa: E402
from app.models.inactivity import (  # noqa: E402
    InactivityNotificationLog,
    InactivityResetLog,
)
from app.models.user import User  # noqa: E402
from app.models.wallet import (  # noqa: E402
    CashTransaction,
    CoinAccount,
    CoinTransaction,
    InviteCashTransaction,
)
from app.repositories import activity  # noqa: E402
from app.repositories import wallet as wallet_repo  # noqa: E402

MARK = "vcase"  # username 前缀,用于清理

# label, 创建于N天前, coin, cash, invite, 近期事件(N天前)or None, 预期
CASES = [
    ("1 三桶全有",        30, 100, 200, 300, None, "清 coin+cash;invite=300 保留;审计1行+2流水"),
    ("2 金币+现金",       30, 100, 200,   0, None, "清 coin+cash;审计1行+2流水"),
    ("3 金币+邀请",       30, 100,   0, 300, None, "清 coin;invite=300 保留;审计1行+1流水"),
    ("4 现金+邀请",       30,   0, 200, 300, None, "清 cash;invite=300 保留;审计1行+1流水"),
    ("5 只有金币",        30, 100,   0,   0, None, "清 coin;审计1行+1流水"),
    ("6 只有现金",        30,   0, 200,   0, None, "清 cash;审计1行+1流水"),
    ("7 只有邀请(红线)",  30,   0,   0, 300, None, "不选中/不清/无审计/无流水;invite=300 原封"),
    ("8 预警窗(10天)",    10,  50,  60,  70, None, "不清;发 T-7 预警;notification_log 1行;余额不动"),
    ("9 活跃兜底",        30, 100, 200, 300,    1, "昨日 home_visible→last_active 近→不清不警"),
    ("10 新用户(3天)",     3, 100, 200,   0, None, "created_at 近→不清不警"),
]


def _mark_uids(db) -> list[int]:
    return list(db.execute(select(User.id).where(User.username.like(f"{MARK}%"))).scalars())


def clean(db) -> int:
    uids = _mark_uids(db)
    if uids:
        for model in (
            InactivityResetLog, InactivityNotificationLog,
            CoinTransaction, CashTransaction, InviteCashTransaction,
            AnalyticsEvent, CoinAccount,
        ):
            db.execute(delete(model).where(model.user_id.in_(uids)))
        db.execute(delete(User).where(User.id.in_(uids)))
        db.commit()
    return len(uids)


def seed(db) -> None:
    now = datetime.now(UTC)
    print(f"{'#':>3} {'uid':>5}  {'案例':<16} {'coin/cash/invite':<18} {'创建':<7} 预期")
    for i, (label, days_ago, coin, cash, invite, ev_days, expected) in enumerate(CASES, 1):
        u = User(
            phone=f"seed_tmp_{i}", username=f"{MARK}{i}", status="active",
            created_at=now - timedelta(days=days_ago),
            last_login_at=now,  # 登录很新——但登录不算活跃,清零该发生照发生
        )
        db.add(u)
        db.flush()                       # 拿自增 id
        u.phone = f"1{u.id:010d}"         # 用全局唯一 id 拼 "100…" 段手机号,dev 库里绝不撞
        acc = wallet_repo.get_or_create_account(db, u.id, commit=False)
        acc.coin_balance, acc.cash_balance_cents, acc.invite_cash_balance_cents = coin, cash, invite
        acc.total_coin_earned = coin
        if ev_days is not None:
            db.add(AnalyticsEvent(  # 首页可见 = event=home_visible(单一 event 名,见 activity.ACTIVE_EVENTS)
                event=activity.HOME_VISIBLE_EVENT, device_id=MARK, user_id=u.id,
                client_ts=0, created_at=now - timedelta(days=ev_days),
            ))
        db.flush()
        print(f"{i:>3} {u.id:>5}  {label:<16} {f'{coin}/{cash}/{invite}':<18} {f'{days_ago}天前':<7} {expected}")
    db.commit()


def check(db) -> None:
    """worker 跑完后:打印每个 vcase 账号的当前三桶余额 + 是否有审计/预警行。"""
    rows = db.execute(
        select(User.id, User.username).where(User.username.like(f"{MARK}%")).order_by(User.id)
    ).all()
    if not rows:
        print("没有 vcase* 账号(先跑一次不带参数造号)")
        return
    print(f"{'uid':>5} {'账号':<8} {'coin/cash/invite(现在)':<24} {'审计':<5} 预警")
    for uid, uname in rows:
        acc = db.get(CoinAccount, uid)
        bal = f"{acc.coin_balance}/{acc.cash_balance_cents}/{acc.invite_cash_balance_cents}" if acc else "—"
        has_reset = db.execute(
            select(InactivityResetLog.id).where(InactivityResetLog.user_id == uid).limit(1)
        ).first()
        stages = db.execute(
            select(InactivityNotificationLog.stage).where(InactivityNotificationLog.user_id == uid)
        ).scalars().all()
        warn = ",".join(f"T-{s}" for s in stages) if stages else "—"
        print(f"{uid:>5} {uname:<8} {bal:<24} {'有' if has_reset else '—':<5} {warn}")


def main() -> None:
    db = SessionLocal()
    try:
        if "--check" in sys.argv:
            check(db)
            return
        removed = clean(db)
        if removed:
            print(f"已清理上次 {removed} 个 {MARK}* 账号")
        if "--clean" in sys.argv:
            return
        seed(db)
        print("\n造号完成。打开 worker(INACTIVITY_RESET_ENABLED=true, RUN_HOUR=17)后启动服务,"
              "≥17:00 首个 tick 即跑一轮。验完 `--clean` 清理。")
    finally:
        db.close()


if __name__ == "__main__":
    main()
