"""
第 1 章配套实战代码 —— Hello Redis

演示内容：
1. 基本连接与 PING 探活
2. String 类型的基础操作（SET/GET/INCR）
3. 用 socket 手动发送 RESP 协议报文，模拟一个最小客户端
4. 简单的性能基准测试，直观感受 Redis 的速度

运行前提：
    1) 本机已有可用的 Redis 服务（127.0.0.1:6379）
    2) pip install redis

运行方式：
    python hello_redis.py
"""

import socket
import time

import redis


HOST = "127.0.0.1"
PORT = 6379


# ----------------------------------------------------------------------
# Demo 1：使用 redis-py 客户端
# ----------------------------------------------------------------------
def demo_basic_client() -> None:
    print("\n" + "=" * 60)
    print("Demo 1: 使用 redis-py 客户端的基本操作")
    print("=" * 60)

    # decode_responses=True 让返回值自动从 bytes 解码为 str
    r = redis.Redis(host=HOST, port=PORT, decode_responses=True)

    # 探活
    print(f"PING -> {r.ping()}")  # True

    # String 类型
    r.set("greeting", "Hello, Redis!")
    print(f"GET greeting -> {r.get('greeting')}")

    # 计数器（INCR 是原子操作）
    r.set("counter", 0)
    r.incr("counter")
    r.incr("counter")
    r.incr("counter")
    print(f"counter after 3 INCR -> {r.get('counter')}")

    # 查看类型与底层编码（验证文档中说的 embstr）
    print(f"TYPE greeting           -> {r.type('greeting')}")
    print(f"OBJECT ENCODING greeting -> {r.object('encoding', 'greeting')}")

    # 设置过期时间（10 秒后自动消失）
    r.set("temp_key", "will expire", ex=10)
    print(f"TTL temp_key -> {r.ttl('temp_key')} 秒")

    # 清理
    r.delete("greeting", "counter", "temp_key")


# ----------------------------------------------------------------------
# Demo 2：手动构造 RESP 协议报文
# ----------------------------------------------------------------------
def encode_resp(*args: str) -> bytes:
    """把命令编码成 RESP 协议字节流。

    示例：encode_resp("SET", "name", "Alice")
        -> b'*3\\r\\n$3\\r\\nSET\\r\\n$4\\r\\nname\\r\\n$5\\r\\nAlice\\r\\n'
    """
    parts: list[bytes] = [f"*{len(args)}\r\n".encode()]
    for a in args:
        ab = a.encode()
        parts.append(f"${len(ab)}\r\n".encode())
        parts.append(ab + b"\r\n")
    return b"".join(parts)


def demo_raw_resp() -> None:
    print("\n" + "=" * 60)
    print("Demo 2: 用 socket 手敲 RESP 协议（模拟最小客户端）")
    print("=" * 60)

    # 建立 TCP 连接
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect((HOST, PORT))

    # 发送 SET name Alice
    request = encode_resp("SET", "name", "Alice")
    print("发送字节流：")
    print(f"  {request!r}")
    sock.sendall(request)
    response = sock.recv(1024)
    print(f"收到回复：{response!r}    # +OK\\r\\n 表示成功")

    # 发送 GET name
    request = encode_resp("GET", "name")
    print("\n发送字节流：")
    print(f"  {request!r}")
    sock.sendall(request)
    response = sock.recv(1024)
    print(f"收到回复：{response!r}    # $5\\r\\nAlice\\r\\n")

    # 清理 + 关闭
    sock.sendall(encode_resp("DEL", "name"))
    sock.recv(1024)
    sock.close()


# ----------------------------------------------------------------------
# Demo 3：感受一下 Redis 的速度
# ----------------------------------------------------------------------
def demo_benchmark(n: int = 10_000) -> None:
    print("\n" + "=" * 60)
    print(f"Demo 3: 简单基准测试（{n} 次 SET + {n} 次 GET）")
    print("=" * 60)

    r = redis.Redis(host=HOST, port=PORT, decode_responses=True)

    # 1) 单条命令循环
    start = time.perf_counter()
    for i in range(n):
        r.set(f"k:{i}", i)
    set_cost = time.perf_counter() - start

    start = time.perf_counter()
    for i in range(n):
        r.get(f"k:{i}")
    get_cost = time.perf_counter() - start

    print(f"单条命令 {n} 次 SET 耗时 {set_cost * 1000:.2f} ms"
          f"  → QPS ≈ {n / set_cost:,.0f}")
    print(f"单条命令 {n} 次 GET 耗时 {get_cost * 1000:.2f} ms"
          f"  → QPS ≈ {n / get_cost:,.0f}")

    # 2) Pipeline 批处理（提前剧透：第 7 章会详讲）
    start = time.perf_counter()
    pipe = r.pipeline()
    for i in range(n):
        pipe.set(f"k:{i}", i)
    pipe.execute()
    pipe_cost = time.perf_counter() - start
    print(f"Pipeline {n} 次 SET 耗时 {pipe_cost * 1000:.2f} ms"
          f"  → QPS ≈ {n / pipe_cost:,.0f}"
          f"  （提速 {set_cost / pipe_cost:.1f}x）")

    # 清理
    keys = [f"k:{i}" for i in range(n)]
    r.delete(*keys)


# ----------------------------------------------------------------------
if __name__ == "__main__":
    try:
        demo_basic_client()
        demo_raw_resp()
        demo_benchmark()
        print("\n✅ 所有 Demo 执行完毕。"
              "\n   下一步：打开 ../demo.html 在浏览器中可视化探索 IO 多路复用！")
    except redis.ConnectionError as e:
        print(f"❌ 连接 Redis 失败：{e}")
        print(f"   请确认 {HOST}:{PORT} 上有可用的 Redis 服务。")
