"""
Ch14 配套代码 2 / 7 —— 超卖演示（V1 不安全版）
================================================================

用途：
  演示「GET → 判断 → DECR」三步分开做时，多线程并发会超卖。
  你会看到最终库存变成负数（-3、-7 这种），抢购成功数 > 真实库存。

原因：
  线程 A: GET stock = 1
  线程 B: GET stock = 1     ← 同一时刻读到 1
  线程 A: DECR stock → 0
  线程 B: DECR stock → -1   ← 超卖

运行：
  python 01_init_stock.py 1001 100   # 先把库存设为 100
  python 02_seckill_v1_unsafe.py
"""

import sys
import threading
import redis

ITEM_ID    = 1001
STOCK_KEY  = f"seckill:stock:{ITEM_ID}"
THREADS    = 100
PER_THREAD = 50

success_cnt = 0
lock = threading.Lock()


def seckill_v1_unsafe(r: redis.Redis) -> bool:
    """❌ 不安全：GET-判断-DECR 不原子，会超卖"""
    stock = r.get(STOCK_KEY)
    if stock is None:
        return False
    if int(stock) <= 0:
        return False
    r.decr(STOCK_KEY)
    return True


def worker() -> None:
    global success_cnt
    rr = redis.Redis(host="127.0.0.1", port=6379, decode_responses=True)
    local = 0
    for _ in range(PER_THREAD):
        if seckill_v1_unsafe(rr):
            local += 1
    with lock:
        success_cnt += local


def main() -> None:
    r = redis.Redis(host="127.0.0.1", port=6379, decode_responses=True)
    initial = int(r.get(STOCK_KEY) or 0)
    if initial == 0:
        print("❌ 请先运行: python 01_init_stock.py 1001 100")
        sys.exit(1)

    print("=" * 56)
    print(f"  V1 不安全版 · {THREADS} 线程 × {PER_THREAD} 次抢购")
    print(f"  初始库存  : {initial}")
    print("=" * 56)

    ts = [threading.Thread(target=worker) for _ in range(THREADS)]
    for t in ts: t.start()
    for t in ts: t.join()

    final = int(r.get(STOCK_KEY) or 0)
    print(f"  抢购成功  : {success_cnt}")
    print(f"  剩余库存  : {final}")
    print(f"  应剩库存  : {max(0, initial - success_cnt)}")
    if final < 0:
        print(f"  ❌ 超卖 {-final} 件！这就是 V1 的问题")
    elif success_cnt > initial:
        print(f"  ❌ 抢购数 {success_cnt} > 初始 {initial}，超卖")
    else:
        print(f"  🎉 这次刚好没翻车（并发不够，多跑几次）")


if __name__ == "__main__":
    try:
        main()
    except redis.ConnectionError as e:
        print(f"❌ Redis 连接失败: {e}")
