from __future__ import annotations

import hashlib
import uuid
from typing import List, Optional

try:
    import winreg  # Windows-only
except Exception:  # pragma: no cover
    winreg = None


def 获取机器码(长度: int = 16, 分组: int = 0) -> Optional[str]:
    """
    获取当前计算机的“短机器码”（稳定优先、尽量唯一）。

    设计初衷
        - **短**：默认输出 16 位十六进制字符串（可调）。
        - **尽量唯一**：不依赖单一硬件序列号，采用“多特征组合 + 哈希”来降低重复概率。
        - **尽量稳定**：优先使用 Windows 安装实例级标识（MachineGuid），避免单一硬件序列号在虚拟化/转接盒/厂商默认值下重复。
        - **不抛异常**：本函数面向工具库场景设计，内部吞掉异常，失败返回 None。

    获取逻辑（从稳定到可用的顺序）
    1) 读取 Windows 注册表中的 MachineGuid（高稳定性）
       - 路径：HKLM\\SOFTWARE\\Microsoft\\Cryptography
       - 键名：MachineGuid
       - 说明：通常在 Windows 安装时生成；重装系统可能变化；普通用户一般可读。
    2) 获取 MAC（网卡）标识（作为补充特征）
       - 使用 uuid.getnode() 获取 48-bit MAC（或一个伪随机值）
       - 说明：在虚拟机、部分云环境、容器/多网卡情况下可能变化或重复，因此不单独作为唯一依据。
    3) 组合原始特征 → 规范化 → SHA-256 → 截断输出
       - 将多个来源的特征按固定顺序拼接，空值会被忽略
       - 进行 SHA-256 哈希，得到 64 位十六进制摘要
       - 按 `长度` 截断并转大写，得到短机器码

    风险与边界（务实版）
    - **不存在“绝对唯一且永不变化”的机器码**：换主板/重装系统/虚拟机迁移等都会改变某些特征。
    - 如果你的业务是“授权绑定”，建议再引入“软件安装实例ID”（首次运行生成 UUID 并落盘），与本机器码一起使用会更稳。

    Args:
        长度: 输出机器码的长度（十六进制字符数）。建议 8~32。默认 16。
        分组: 可选的分组显示长度（例如 4 则输出 XXXX-XXXX-XXXX-XXXX）。0 表示不分组。

    Returns:
        成功返回机器码字符串（如 "0D5FC4B0F4D496DA" 或分组形式），失败返回 None。

    """
    try:
        if not isinstance(长度, int) or 长度 <= 0:
            return None
        if not isinstance(分组, int) or 分组 < 0:
            return None

        # 1) Windows MachineGuid（最推荐的稳定来源）
        machine_guid = _win_取_machine_guid() or ""

        # 2) MAC（补充来源；注意可能是伪随机或在某些环境下不稳定）
        mac = _取_mac_十六进制() or ""

        # 3) 组合并哈希：固定顺序 + 分隔符，减少拼接歧义
        parts: List[str] = []
        if machine_guid:
            parts.append(f"GUID={machine_guid}")
        if mac:
            parts.append(f"MAC={mac}")

        # 若两者都拿不到，就没必要生成“看似正常但不可追溯”的码
        if not parts:
            return None

        raw = "|".join(parts).strip().lower()

        digest = hashlib.sha256(raw.encode("utf-8", errors="ignore")).hexdigest().upper()
        out = digest[:长度]

        if 分组 and 分组 > 0:
            out = "-".join(out[i:i + 分组] for i in range(0, len(out), 分组))

        return out
    except Exception:
        return None


def _win_取_machine_guid() -> Optional[str]:
    """
    从 Windows 注册表读取 MachineGuid。

    Returns:
        成功返回 MachineGuid 字符串，失败返回 None。
    """
    try:
        if winreg is None:
            return None

        key = winreg.OpenKey(
            winreg.HKEY_LOCAL_MACHINE,
            r"SOFTWARE\Microsoft\Cryptography",
            0,
            winreg.KEY_READ,
        )
        value, _ = winreg.QueryValueEx(key, "MachineGuid")

        if isinstance(value, str):
            value = value.strip()
            return value if value else None

        return None
    except Exception:
        return None


def _取_mac_十六进制() -> Optional[str]:
    """
    获取 MAC 地址并格式化为 12 位十六进制字符串。

    说明：
    - uuid.getnode() 通常返回真实 MAC（48-bit）
    - 在某些环境（虚拟机/容器/隐私策略）可能返回一个伪随机值
    - 本函数只负责“获取并标准化”，不保证唯一性

    Returns:
        成功返回形如 "0D5FC4B0F4D4" 的字符串，失败返回 None。
    """
    try:
        node = uuid.getnode()
        if not isinstance(node, int) or node <= 0:
            return None

        # 48-bit 格式化（不足补零）
        return f"{node:012X}"
    except Exception:
        return None


if __name__ == "__main__":
    print(获取机器码())           # 例如：0D5FC4B0F4D496DA
    print(获取机器码(16, 4))      # 例如：0D5F-C4B0-F4D4-96DA
    print(获取机器码(24, 6))      # 例如：0D5FC4-B0F4D4-96DA12