"""
Ch8 配套代码 1 / 3 —— 用 INFO replication 看主从信息

⚠️ 本脚本需要主从环境才能看到完整的 connected_slaves / slave0 等字段。
   若教程环境只有单机 Redis，脚本会自动 fallback：先打真实输出，
   再额外渲染一份「典型主从环境」的 mock 输出，对照学习每个字段。

主要演示：
  1. 主节点视角：role / connected_slaves / master_repl_offset
                 / master_replid / repl_backlog_*
  2. 从节点视角：role / master_link_status / slave_repl_offset
                 / master_last_io_seconds_ago / master_sync_in_progress
  3. 派生指标：积压缓冲区利用率、各从节点滞后字节数等
"""

import redis

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


FIELD_DOC = {
    "role": "节点角色：master / slave",
    "connected_slaves": "在线从节点数量",
    "master_replid": "复制 ID（40 位 hex），节点身份证。从节点用它判断「跟的是不是同一个主」",
    "master_replid2": "上一次的复制 ID（PSYNC2），用于支持主重启 / 哨兵切换不全量",
    "master_repl_offset": "主节点累计发出字节数，单调递增，永不回退",
    "second_repl_offset": "切换 replid 时的 offset 断点，配合 replid2",
    "repl_backlog_active": "积压缓冲区是否启用（1 / 0）",
    "repl_backlog_size": "积压缓冲区大小（字节）。**生产必调**，默认 1MB 太小",
    "repl_backlog_first_byte_offset": "backlog 中最旧字节的 offset，从节点 offset < 它就只能全量",
    "repl_backlog_histlen": "backlog 中实际有效字节数（未写满时 < repl_backlog_size）",
    "master_host": "主节点 IP（slave 视角）",
    "master_port": "主节点端口（slave 视角）",
    "master_link_status": "与主连接状态：up / down",
    "master_last_io_seconds_ago": "距上次收到主的数据多少秒（长时间不动可能要排查心跳）",
    "master_sync_in_progress": "是否正在做全量同步（1 = loading 中）",
    "slave_repl_offset": "从节点已同步到的 offset，与 master_repl_offset 之差 = 滞后字节数",
    "slave_priority": "哨兵选主优先级（值越小越优先；0 = 永不当选）",
    "slave_read_only": "从节点是否只读（强烈建议保持 1）",
    "master_link_down_since_seconds": "与主断连了多少秒（-1 = 当前正常）",
}


def section(title: str) -> None:
    print("\n" + "=" * 70)
    print(title)
    print("=" * 70)


def show_real_info() -> dict:
    section("Demo 1: 当前真实环境的 INFO replication 输出")
    info = r.info("replication")
    for k, v in info.items():
        print(f"  {k:36} = {v}")
    print(f"\n  → 当前节点 role = {info.get('role')}, "
          f"在线 replica = {info.get('connected_slaves', 0)}")
    return info


def explain_each_field(info: dict) -> None:
    section("Demo 2: 字段逐项解读")
    for k, v in info.items():
        doc = FIELD_DOC.get(k, "(暂未收录解释)")
        print(f"  📌 {k} = {v}")
        print(f"     → {doc}\n")


def derive_metrics(info: dict) -> None:
    section("Demo 3: 派生指标 + 健康度评估")
    if info.get("role") == "master":
        size = info.get("repl_backlog_size", 0)
        used = info.get("repl_backlog_histlen", 0)
        print(f"  积压缓冲区: {size:,} 字节 ({size/1024/1024:.2f} MB)")
        print(f"  已使用    : {used:,} 字节 (利用率 "
              f"{(used/size*100 if size else 0):.1f}%)")
        if size <= 1024 * 1024:
            print("  ⚠️  backlog 仅 1MB（默认值），生产建议 ≥ 64MB")
            print("     配置示例: repl-backlog-size 64mb")
        if info.get("connected_slaves", 0) == 0:
            print("  ℹ️  当前没有在线从节点（单机 demo 环境正常）")


def show_mock_master() -> None:
    section("Demo 4: 典型【主节点】INFO replication 的样子（mock）")
    mock = """\
# Replication
role:master
connected_slaves:2
slave0:ip=192.168.1.11,port=6379,state=online,offset=15234,lag=0
slave1:ip=192.168.1.12,port=6379,state=online,offset=15180,lag=1
master_failover_state:no-failover
master_replid:7f0d9c5b8a2e1f4d6c8a3b5e7f9d1c3a5e7f9d1c
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:15234
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:14186
repl_backlog_histlen:1048
"""
    print(mock)
    master_offset = 15234
    print("  📊 由 mock 输出推导：")
    print(f"     - slave0 滞后 = {master_offset - 15234} bytes (完全同步)")
    print(f"     - slave1 滞后 = {master_offset - 15180} bytes (54 字节落后)")
    print(f"     - backlog 有效区间 = [14186, {master_offset}]")
    print(f"     - 任何 slave_offset < 14186 → FULLRESYNC；否则 CONTINUE")


def show_mock_slave() -> None:
    section("Demo 5: 典型【从节点】INFO replication 的样子（mock）")
    mock = """\
# Replication
role:slave
master_host:192.168.1.10
master_port:6379
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
slave_read_repl_offset:15234
slave_repl_offset:15234
master_link_down_since_seconds:-1
slave_priority:100
slave_read_only:1
connected_slaves:0
"""
    print(mock)
    print("  📊 健康判断要点：")
    print("     - master_link_status=up + master_sync_in_progress=0 → 正常服务")
    print("     - master_last_io_seconds_ago ≤ 1s → 心跳正常")
    print("     - slave_read_only=1 → 写命令会被拒绝（只读保护）")


if __name__ == "__main__":
    try:
        info = show_real_info()
        explain_each_field(info)
        derive_metrics(info)
        if info.get("role") == "master" and info.get("connected_slaves", 0) == 0:
            print("\n  ↓ 单机环境检测到无 replica，下面给出主/从典型 mock 输出 ↓")
            show_mock_master()
            show_mock_slave()
        print("\n✅ 完成。把这个脚本拿到真实主从集群上跑，能看到更丰富的字段。")
    except redis.ConnectionError as e:
        print(f"❌ Redis 连接失败: {e}")
