"""
Ch4 配套代码 2 / 3 —— EXPIRE 的常见坑实测

  坑 1: SET 默认会清除 TTL
  坑 2: RENAME 会继承源 Key 的 TTL
  坑 3: EXPIRE 0 / 负数 = 立即删除
  坑 4: KEEPTTL 的正确用法
"""

import time
import redis

r = redis.Redis(host="127.0.0.1", port=6379, decode_responses=True)


def section(t): print("\n" + "=" * 60 + f"\n{t}\n" + "=" * 60)


def pitfall_1_set_clears_ttl() -> None:
    section("坑 1: SET 默认会清除 TTL")
    r.set("token", "v1", ex=60)
    print(f"  SET token 'v1' EX 60     →  TTL = {r.ttl('token')}")
    r.set("token", "v2")  # 不带 EX
    print(f"  SET token 'v2' (无 EX)   →  TTL = {r.ttl('token')}  ❌ 过期被清掉了")
    r.delete("token")


def pitfall_2_rename_inherits_ttl() -> None:
    section("坑 2: RENAME 会继承源 Key 的 TTL")
    r.set("k1", "v1", ex=120)
    r.set("k2", "v2")          # k2 没有过期
    print(f"  k1 TTL = {r.ttl('k1')}, k2 TTL = {r.ttl('k2')}")
    r.rename("k1", "k2")
    print(f"  RENAME k1 k2 之后：")
    print(f"  k2 TTL = {r.ttl('k2')}  ← 继承自 k1")
    r.delete("k2")


def pitfall_3_expire_zero() -> None:
    section("坑 3: EXPIRE 0/负数 = 立即删除")
    r.set("k", "v")
    print(f"  EXISTS 之前: {r.exists('k')}")
    r.expire("k", 0)
    print(f"  EXPIRE k 0 之后 EXISTS: {r.exists('k')}  ← 立即被删")


def fix_keep_ttl() -> None:
    section("正确用法: SET ... KEEPTTL（6.0+）")
    r.set("token", "v1", ex=60)
    print(f"  SET ... EX 60          →  TTL = {r.ttl('token')}")
    r.set("token", "v2", keepttl=True)
    print(f"  SET ... KEEPTTL        →  TTL = {r.ttl('token')}  ✅ 保留")
    r.delete("token")


def demo_expireat_vs_expire() -> None:
    section("EXPIRE vs EXPIREAT：相对时间 vs 绝对时间")

    r.set("k1", "v", ex=10)
    r.expireat("k2", int(time.time()) + 10)
    r.set("k2", "v")  # 注意：SET 又会清掉 TTL！
    r.expireat("k2", int(time.time()) + 10)

    print(f"  EXPIRE k1 10        →  TTL = {r.ttl('k1')}")
    print(f"  EXPIREAT k2 +10s    →  TTL = {r.ttl('k2')}")
    print("  💡 进程睡眠/重启时，EXPIREAT 不受影响，EXPIRE 会被推迟")
    r.delete("k1", "k2")


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