# -*- coding: utf-8 -*-
"""
对外 API：check、parse、build、YamlConfigManager 及 YAML 读写函数。

- 输出位置与组合位置一致：parse 输出到指定 yaml_dir，build 从同一 yaml_dir 读取并写出 pb.txt。
- yaml_dir 可任意指定，保证解析输出与组装输入使用同一目录即可。
"""

from pathlib import Path
from typing import Any, Dict, Optional, Tuple

from ._parser import PBParser
from ._builder import PBBuilder
from ._proto_io import parse_pb_text
from ._yaml_io import read_yaml_file, update_yaml_file


def check(
    pb_path: str,
    proto_templates_dir: Optional[str] = None,
) -> Tuple[bool, Optional[str]]:
    """
    反序列化检查 pb.txt 是否存在格式问题。
    严格按 vehicle_config.proto 定义做标准反序列化，成功即通过。

    :param pb_path: .pb.txt 文件路径
    :param proto_templates_dir: 可选，保留兼容，检查时未使用
    :return: (是否通过, 错误信息)，通过时错误信息为 None
    """
    try:
        with open(pb_path, "r", encoding="utf-8") as f:
            content = f.read()
        parse_pb_text(content)
        return True, None
    except Exception as e:
        return False, str(e)


def parse(
    pb_path: str,
    yaml_dir: str,
    proto_templates_dir: Optional[str] = None,
) -> Tuple[dict, dict]:
    """
    解析 pb.txt 为每个单独的 yaml 文件，输出到指定目录。
    该目录与后续 build 时使用的「组合位置」一致，可指定为同一路径。

    :param pb_path: .pb.txt 文件路径
    :param yaml_dir: 输出目录（与 build 的输入目录一致，可指定）
    :param proto_templates_dir: 可选，proto 模板目录
    :return: (解析后的结构化数据, 缺项/漏项报告)
    :raises: 解析失败时抛出异常
    """
    parser = PBParser(proto_templates_dir=proto_templates_dir)
    return parser.parse_and_dump(pb_path, yaml_dir)


def build(
    yaml_dir: str,
    output_pb_path: str,
    proto_templates_dir: Optional[str] = None,
) -> None:
    """
    按顺序将 yaml_dir 中的单独 yaml 文件组装成 pb.txt。
    输入目录应与 parse 时的输出目录一致（可指定为同一路径）。

    :param yaml_dir: 包含 order_manifest.yaml 及分组 yaml 的目录（与 parse 输出位置一致）
    :param output_pb_path: 输出的 .pb.txt 文件路径
    :param proto_templates_dir: 可选，proto 模板目录
    """
    builder = PBBuilder(proto_templates_dir=proto_templates_dir)
    builder.build_file(yaml_dir, output_pb_path)


# ---------------------------------------------------------------------------
# YamlConfigManager：默认路径 + 无缓存、每次从磁盘读取
# ---------------------------------------------------------------------------


class YamlConfigManager:
    """
    统一默认 YAML 目录及读写 API。不缓存 YAML 内容，每次从 default_yaml_dir 对应文件读取，
    保证多程序共同维护同一目录时数据为最新。
    """

    def __init__(self, default_yaml_dir: str) -> None:
        self._default_yaml_dir = str(default_yaml_dir)

    def _path(self, relative_path: str) -> str:
        return str(Path(self._default_yaml_dir) / relative_path)

    def read_yaml(self, relative_path: str) -> dict:
        """从磁盘读 default_yaml_dir / relative_path，返回该 dict。"""
        return read_yaml_file(self._path(relative_path))

    def update_yaml(
        self,
        relative_path: str,
        updates: dict,
        output_relative_path: Optional[str] = None,
    ) -> None:
        """从磁盘读当前文件，深合并 updates 后写回。不维护缓存。"""
        out = self._path(output_relative_path) if output_relative_path is not None else None
        update_yaml_file(self._path(relative_path), updates, out)

    def read_order_manifest(self) -> dict:
        """从磁盘读 default_yaml_dir/order_manifest.yaml，返回该 dict。"""
        return read_yaml_file(self._path("order_manifest.yaml"))

    def update_order_manifest(
        self,
        updates: dict,
        output_dir: Optional[str] = None,
    ) -> None:
        """从磁盘读当前 order_manifest，深合并 updates 后写回。不维护缓存。"""
        src = self._path("order_manifest.yaml")
        if output_dir is None:
            update_yaml_file(src, updates, None)
        else:
            update_yaml_file(src, updates, str(Path(output_dir) / "order_manifest.yaml"))

    def rescan(self) -> Dict[str, Any]:
        """
        从磁盘重新扫描 default_yaml_dir：读 order_manifest.yaml，再按 manifest 列出的
        vehicle_info_file、vehicle_param_file、extrinsics、intrinsics 逐个从磁盘加载，
        组装为 dict 并直接返回，供其他程序获取最新数据。
        """
        return _load_all_from_yaml_dir(self._default_yaml_dir)

    def get_cached(self) -> Dict[str, Any]:
        """与 rescan() 同义：从磁盘读取当前全部 YAML 并返回组装结果，保证数据为最新。"""
        return self.rescan()

    def get(self, key: str) -> Optional[dict]:
        """
        按逻辑名从磁盘读取对应文件并返回，如 "order_manifest"、"vehicle_info"、
        "vehicle_param"、"extrinsics/000"、"intrinsics/xxx"；文件不存在则返回 None。
        """
        if not key:
            return None
        rel = key if key.endswith(".yaml") else f"{key}.yaml"
        path = Path(self._path(rel))
        if not path.exists() or not path.is_file():
            return None
        return read_yaml_file(str(path))


def _load_all_from_yaml_dir(yaml_dir: str) -> Dict[str, Any]:
    """从目录按 order_manifest 加载全部 YAML，组装为 dict（含 order_manifest、vehicle_info 等）。"""
    root = Path(yaml_dir)
    om_path = root / "order_manifest.yaml"
    if not om_path.exists():
        return {"order_manifest": {}, "vehicle_info": {}, "vehicle_param": {}, "extrinsics": [], "intrinsics": []}
    manifest = read_yaml_file(str(om_path)) or {}
    result: Dict[str, Any] = {
        "order_manifest": manifest,
        "vehicle_info": {},
        "vehicle_param": {},
        "extrinsics": [],
        "intrinsics": [],
    }
    vi_file = manifest.get("vehicle_info_file", "vehicle_info.yaml")
    vp_file = manifest.get("vehicle_param_file", "vehicle_param.yaml")
    if (root / vi_file).exists():
        result["vehicle_info"] = read_yaml_file(str(root / vi_file)) or {}
    if (root / vp_file).exists():
        result["vehicle_param"] = read_yaml_file(str(root / vp_file)) or {}
    for ext_item in manifest.get("extrinsics", []):
        path = root / ext_item.get("file", "")
        if path.exists():
            result["extrinsics"].append(read_yaml_file(str(path)) or {})
    for int_item in manifest.get("intrinsics", []):
        path = root / int_item.get("file", "")
        if path.exists():
            result["intrinsics"].append(read_yaml_file(str(path)) or {})
    return result


# ---------------------------------------------------------------------------
# 函数式 API（无状态，显式传路径）
# ---------------------------------------------------------------------------


def read_yaml(yaml_path: str) -> dict:
    """读取任意 YAML 文件，返回字典。文件不存在或非合法 YAML 时返回空 dict。"""
    return read_yaml_file(yaml_path)


def update_yaml(
    yaml_path: str,
    updates: dict,
    output_path: Optional[str] = None,
) -> None:
    """根据 updates 对 YAML 做深合并后写回。output_path 为 None 时写回 yaml_path，否则写出到 output_path。"""
    update_yaml_file(yaml_path, updates, output_path)


def read_order_manifest(yaml_dir: str) -> dict:
    """读取 yaml_dir/order_manifest.yaml，返回字典。"""
    return read_yaml_file(str(Path(yaml_dir) / "order_manifest.yaml"))


def update_order_manifest(
    yaml_dir: str,
    updates: dict,
    output_dir: Optional[str] = None,
) -> None:
    """读取 order_manifest，深合并 updates 后写回。output_dir 为 None 时写回 yaml_dir，否则写入 output_dir。"""
    src = str(Path(yaml_dir) / "order_manifest.yaml")
    out = str(Path(output_dir) / "order_manifest.yaml") if output_dir else None
    update_yaml_file(src, updates, out)
