"""
Redis 连接封装：单机 Redis、集群 RedisCluster，统一接口。
"""
from __future__ import annotations

import contextlib
from typing import Any, Generator

from redis import Redis
from redis.cluster import RedisCluster
from redis.connection import ConnectionPool

from .config import ConnectionConfig, SafetyConfig


def get_connection_config(
    profile: str | None,
    default: ConnectionConfig,
    connection_map: dict[str, ConnectionConfig],
) -> ConnectionConfig:
    """解析 profile，返回对应连接配置。"""
    if not (profile or "").strip():
        return default
    key = (profile or "").strip()
    if key not in connection_map:
        raise ValueError(f"未知的 connection profile: {profile}，可用: {', '.join(sorted(connection_map))}")
    return connection_map[key]


@contextlib.contextmanager
def connect(
    config: ConnectionConfig,
    safety: SafetyConfig,
) -> Generator[Redis | RedisCluster, None, None]:
    """创建 Redis 或 RedisCluster 连接。"""
    if config.mode == "standalone":
        client = Redis(
            host=config.host,
            port=config.port,
            password=config.password or None,
            db=config.db,
            socket_connect_timeout=safety.command_timeout_seconds,
            socket_timeout=safety.command_timeout_seconds,
            decode_responses=True,
        )
    else:
        # cluster: hosts 格式 ["host:port", ...]
        startup_nodes = []
        for h in config.hosts or []:
            if ":" in h:
                host, _, port_str = h.rpartition(":")
                try:
                    port = int(port_str)
                except ValueError:
                    port = config.port
            else:
                host, port = h, config.port
            startup_nodes.append({"host": host.strip(), "port": port})
        if not startup_nodes:
            startup_nodes = [{"host": config.host, "port": config.port}]
        client = RedisCluster(
            startup_nodes=startup_nodes,
            password=config.password or None,
            decode_responses=True,
        )
    try:
        yield client
    finally:
        client.close()


def list_keys(
    client: Redis | RedisCluster,
    pattern: str = "*",
    max_keys: int = 1000,
) -> list[str]:
    """使用 SCAN 列出 key，不超过 max_keys。"""
    keys: list[str] = []
    cursor = 0
    while True:
        cursor, batch = client.scan(cursor=cursor, match=pattern, count=100)
        for k in batch:
            keys.append(k)
            if len(keys) >= max_keys:
                return keys
        if cursor == 0:
            break
    return keys


def info_sections(client: Redis | RedisCluster, section: str | None = None) -> dict[str, Any]:
    """INFO 命令，section 可选（server, memory, clients 等），None 表示 all。"""
    if section:
        return client.info(section) or {}
    return client.info() or {}


def execute_command(
    client: Redis | RedisCluster,
    *args: Any,
) -> Any:
    """执行一条 Redis 命令，返回结果。调用方负责安全检查。"""
    return client.execute_command(*args)
