"""
Ch14 配套代码 3 / 7 —— 安全秒杀（V3 Lua 脚本版）
================================================================

用途：
  用 Redis 单线程执行 Lua 的特性，把「判库存 + 扣减」做成原子操作。
  无论多大并发，最终库存都不会变成负数。

核心 Lua（写在脚本字符串里，不依赖外部文件）：
  local s = tonumber(redis.call('GET', KEYS[1]))
  if not s or s <= 0 then return 0 end
  redis.call('DECR', KEYS[1])
  return 1

运行：
  python 01_init_stock.py 1001 100
  python 03_seckill_v3_lua.py
"""

import sys
import threading
import redis

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

LUA = """
local s = tonumber(redis.call('GET', KEYS[1]))
if not s or s <= 0 then
  return 0
end
redis.call('DECR', KEYS[1])
return 1
"""

success_cnt = 0
lock = threading.Lock()


def worker(script) -> 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 script(keys=[STOCK_KEY], client=rr) == 1:
            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)

    seckill = r.register_script(LUA)

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

    ts = [threading.Thread(target=worker, args=(seckill,)) 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}")
    if final < 0:
        print(f"  ❌ 不该出现的负库存，请检查脚本")
    elif success_cnt == initial and final == 0:
        print(f"  ✅ 完美：恰好卖完 {initial} 件，0 超卖")
    else:
        print(f"  ✅ 安全：成功 {success_cnt} ≤ 初始 {initial}")


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