# -*- coding: utf-8 -*-
"""
往返测试：读真实 pb.txt → 解析到 temp YAML → 重组 pb.txt → 与原文做语义对比。

运行方式（在 V0.0.6 目录下，先 conda activate py310 && pip install -e .）：
  pytest test/test_roundtrip.py -v
  或
  python test/test_roundtrip.py
"""

import tempfile
from pathlib import Path

import pytest

from pb_calibration import parse, build
from pb_calibration._proto_io import parse_pb_text, message_to_dict


def _deep_eq(a, b, path="", tol=1e-9):
    """递归比较两个结构（dict/list/标量），浮点数用 tol 容差。返回 (是否相等, 差异描述)。"""
    if type(a) != type(b):
        return False, f"{path}: 类型不同 {type(a).__name__} vs {type(b).__name__}"
    if isinstance(a, dict):
        keys_a, keys_b = set(a), set(b)
        if keys_a != keys_b:
            return False, f"{path}: 键不同, 仅左有 {keys_a - keys_b}, 仅右有 {keys_b - keys_a}"
        for k in keys_a:
            ok, msg = _deep_eq(a[k], b[k], path=f"{path}.{k}", tol=tol)
            if not ok:
                return False, msg
        return True, ""
    if isinstance(a, list):
        if len(a) != len(b):
            return False, f"{path}: 列表长度不同 {len(a)} vs {len(b)}"
        for i, (xa, xb) in enumerate(zip(a, b)):
            ok, msg = _deep_eq(xa, xb, path=f"{path}[{i}]", tol=tol)
            if not ok:
                return False, msg
        return True, ""
    if isinstance(a, float):
        if abs(a - b) > tol:
            return False, f"{path}: 浮点不同 {a!r} vs {b!r}"
        return True, ""
    if a != b:
        return False, f"{path}: 值不同 {a!r} vs {b!r}"
    return True, ""


def test_roundtrip_pb_to_yaml_to_pb():
    """原始 pb.txt → parse → temp YAML → build → 重组 pb.txt，与原文语义一致。"""
    test_dir = Path(__file__).resolve().parent
    pb_path = test_dir / "vehicle_config.pb.txt"
    if not pb_path.exists():
        pytest.skip(f"测试输入不存在: {pb_path}")

    with tempfile.TemporaryDirectory(prefix="pb_cal_roundtrip_") as tmpdir:
        yaml_dir = tmpdir
        rebuilt_pb_path = Path(tmpdir) / "rebuilt.pb.txt"

        parse(str(pb_path), yaml_dir)
        build(yaml_dir, str(rebuilt_pb_path))

        with open(pb_path, "r", encoding="utf-8") as f:
            orig_content = f.read()
        with open(rebuilt_pb_path, "r", encoding="utf-8") as f:
            rebuilt_content = f.read()

        orig_msg = parse_pb_text(orig_content)
        rebuilt_msg = parse_pb_text(rebuilt_content)

        # 语义一致：二进制序列化应相同
        orig_bin = orig_msg.SerializeToString()
        rebuilt_bin = rebuilt_msg.SerializeToString()
        assert orig_bin == rebuilt_bin, (
            "往返后 pb 与原文语义不一致（SerializeToString 不同）。"
            " 可检查 parse→yaml→build 过程中是否有字段丢失或精度变化。"
        )

        # 可选：dict 深度比较，便于失败时定位差异（浮点容差）
        orig_dict = message_to_dict(orig_msg)
        rebuilt_dict = message_to_dict(rebuilt_msg)
        ok, diff_msg = _deep_eq(orig_dict, rebuilt_dict, path="root", tol=1e-9)
        assert ok, f"往返后 dict 与原文不一致: {diff_msg}"


if __name__ == "__main__":
    test_roundtrip_pb_to_yaml_to_pb()
    print("OK: 往返测试通过。")
