"""Output Validator - 校验提取结果是否符合定义结构.

校验维度:
1. 结构完整性: 是否包含所有定义中的字段
2. 类型正确性: 值类型是否匹配定义
3. 多重性: is_list 字段是否为数组
4. 额外字段: 是否包含定义中没有的字段
"""

from __future__ import annotations

from typing import Any

DefinitionNode = dict[str, Any]


def validate(instance: dict[str, Any], definition: dict[str, Any]) -> list[str]:
    """校验实例是否符合定义结构.

    Args:
        instance: 提取得到的实例 JSON
        definition: 信息定义 JSON

    Returns:
        错误信息列表，为空表示校验通过
    """
    errors: list[str] = []
    _validate_recursive(instance, definition, path="", errors=errors)
    return errors


def _validate_recursive(
    instance: Any,
    definition: dict[str, Any],
    path: str,
    errors: list[str],
) -> None:
    metadata = definition["metadata"]
    label = metadata["node_label"]
    current_path = f"{path}.{label}" if path else label

    if metadata["node_type"] == "concept":
        _validate_concept(instance, definition, current_path, errors)
    else:
        _validate_property(instance, definition, current_path, errors)


def _validate_concept(
    instance: Any,
    definition: dict[str, Any],
    path: str,
    errors: list[str],
) -> None:
    if not isinstance(instance, dict):
        errors.append(f"[{path}] 应为 JSON 对象，实际为 {type(instance).__name__}")
        return

    subnodes = definition.get("subnodes", {})

    # 检查所有子节点是否存在
    for key, subnode_def in subnodes.items():
        if key not in instance:
            errors.append(f"[{path}.{key}] 字段缺失")
        else:
            _validate_recursive(instance[key], subnode_def, path, errors)

    # 检查额外字段
    expected_keys = set(subnodes.keys())
    actual_keys = set(instance.keys())
    extra_keys = actual_keys - expected_keys
    if extra_keys:
        errors.append(f"[{path}] 存在未定义字段: {extra_keys}")


def _validate_property(
    instance: Any,
    definition: dict[str, Any],
    path: str,
    errors: list[str],
) -> None:
    metadata = definition["metadata"]
    value_type = metadata["value_type"]
    is_list = metadata.get("is_list", False)

    # null 是合法的缺失值
    if instance is None:
        return

    if value_type == "class":
        _validate_class_property(instance, definition, path, is_list, errors)
    else:
        _validate_basic_property(instance, value_type, path, is_list, errors)


def _validate_class_property(
    instance: Any,
    definition: dict[str, Any],
    path: str,
    is_list: bool,
    errors: list[str],
) -> None:
    subnodes = definition.get("subnodes", {})

    if is_list:
        if not isinstance(instance, list):
            errors.append(f"[{path}] 应为数组，实际为 {type(instance).__name__}")
            return
        for i, item in enumerate(instance):
            item_path = f"{path}[{i}]"
            if not isinstance(item, dict):
                errors.append(f"[{item_path}] 应为 JSON 对象")
                continue
            for key, subnode_def in subnodes.items():
                if key not in item:
                    errors.append(f"[{item_path}.{key}] 字段缺失")
                else:
                    _validate_recursive(item[key], subnode_def, item_path, errors)
            extra_keys = set(item.keys()) - set(subnodes.keys())
            if extra_keys:
                errors.append(f"[{item_path}] 存在未定义字段: {extra_keys}")
    else:
        if not isinstance(instance, dict):
            errors.append(f"[{path}] 应为 JSON 对象，实际为 {type(instance).__name__}")
            return
        for key, subnode_def in subnodes.items():
            if key not in instance:
                errors.append(f"[{path}.{key}] 字段缺失")
            else:
                _validate_recursive(instance[key], subnode_def, path, errors)
        extra_keys = set(instance.keys()) - set(subnodes.keys())
        if extra_keys:
            errors.append(f"[{path}] 存在未定义字段: {extra_keys}")


def _validate_basic_property(
    instance: Any,
    value_type: str,
    path: str,
    is_list: bool,
    errors: list[str],
) -> None:
    type_checker = _TYPE_CHECKERS.get(value_type)

    if is_list:
        if not isinstance(instance, list):
            errors.append(f"[{path}] 应为数组，实际为 {type(instance).__name__}")
            return
        for i, item in enumerate(instance):
            if item is not None and type_checker and not type_checker(item):
                errors.append(
                    f"[{path}[{i}]] 应为 {value_type}，"
                    f"实际为 {type(item).__name__}: {_truncate(str(item))}"
                )
    else:
        if type_checker and not type_checker(instance):
            errors.append(
                f"[{path}] 应为 {value_type}，"
                f"实际为 {type(instance).__name__}: {_truncate(str(instance))}"
            )


def _is_integer(v: Any) -> bool:
    return isinstance(v, int) and not isinstance(v, bool)


def _is_float(v: Any) -> bool:
    return isinstance(v, (int, float)) and not isinstance(v, bool)


def _is_string(v: Any) -> bool:
    return isinstance(v, str)


def _is_boolean(v: Any) -> bool:
    return isinstance(v, bool)


_VALID_VALUE_TYPES = ("integer", "float", "string", "boolean", "date_time", "class")
_VALID_NODE_TYPES = ("concept", "property")


def validate_definition(definition: dict[str, Any]) -> list[str]:
    """校验信息定义 JSON 的结构是否合法.

    校验维度:
    1. 每个节点必须包含 metadata
    2. metadata 必须包含 node_label、node_name、node_type
    3. node_label 必须为全大写字母，且与外层 key 一致
    4. node_type 必须为 concept 或 property
    5. concept 节点只能包含 metadata 和 subnodes
    6. property 节点必须包含 value_type 和 is_list
    7. value_type 必须在允许取值范围内
    8. value_type=class 时必须包含 subnodes，基础类型时不应有 subnodes
    9. 递归校验所有子节点

    Args:
        definition: 信息定义 JSON

    Returns:
        错误信息列表，为空表示校验通过
    """
    errors: list[str] = []
    _validate_definition_node(definition, expected_label=None, path="", errors=errors)
    return errors


def _validate_definition_node(
    node: Any,
    expected_label: str | None,
    path: str,
    errors: list[str],
) -> None:
    """递归校验定义树的单个节点."""
    if not isinstance(node, dict):
        errors.append(f"[{path}] 节点必须是 JSON 对象")
        return

    # 节点层面只允许 metadata 和 subnodes
    extra_node_fields = set(node.keys()) - {"metadata", "subnodes"}
    if extra_node_fields:
        errors.append(f"[{path}] 存在未允许字段: {extra_node_fields}")

    if "metadata" not in node:
        errors.append(f"[{path}] 缺少 metadata 字段")
        return

    metadata = node.get("metadata")
    if not isinstance(metadata, dict):
        errors.append(f"[{path}] metadata 必须是 JSON 对象")
        return

    # 必填字段
    for field in ("node_label", "node_name", "node_type"):
        if field not in metadata:
            errors.append(f"[{path}.metadata] 缺少 {field} 字段")

    label = metadata.get("node_label")
    if not isinstance(label, str) or not label.isalpha() or not label.isupper():
        errors.append(
            f"[{path}.metadata.node_label] 必须是由大写字母组成的拼音首字母，"
            f"实际为: {_truncate(str(label))}"
        )
    elif expected_label is not None and label != expected_label:
        errors.append(
            f"[{path}.metadata.node_label] 值 '{label}' 与外层 key '{expected_label}' 不一致"
        )

    name = metadata.get("node_name")
    if not isinstance(name, str) or not name.strip():
        errors.append(f"[{path}.metadata.node_name] 必须是非空字符串")

    node_type = metadata.get("node_type")
    if node_type not in _VALID_NODE_TYPES:
        errors.append(
            f"[{path}.metadata.node_type] 必须是 {_VALID_NODE_TYPES} 之一，"
            f"实际为: {_truncate(str(node_type))}"
        )
        # node_type 非法时，后续规则无法判断，直接返回
        return

    if node_type == "concept":
        _validate_concept_definition(node, path, errors)
    else:
        _validate_property_definition(node, path, errors)

    # 递归校验子节点
    subnodes = node.get("subnodes", {})
    if isinstance(subnodes, dict):
        for key, child in subnodes.items():
            child_path = f"{path}.{key}" if path else key
            _validate_definition_node(child, expected_label=key, path=child_path, errors=errors)


def _validate_concept_definition(
    node: dict[str, Any], path: str, errors: list[str]
) -> None:
    """校验 concept 类型节点的定义规则."""
    metadata = node["metadata"]
    if "value_type" in metadata:
        errors.append(f"[{path}.metadata] concept 节点不应包含 value_type")
    if "is_list" in metadata:
        errors.append(f"[{path}.metadata] concept 节点不应包含 is_list")

    subnodes = node.get("subnodes")
    if not isinstance(subnodes, dict) or not subnodes:
        errors.append(f"[{path}] concept 节点必须包含非空的 subnodes")


def _validate_property_definition(
    node: dict[str, Any], path: str, errors: list[str]
) -> None:
    """校验 property 类型节点的定义规则."""
    metadata = node["metadata"]

    if "value_type" not in metadata:
        errors.append(f"[{path}.metadata] property 节点必须包含 value_type")
        return

    value_type = metadata["value_type"]
    if value_type not in _VALID_VALUE_TYPES:
        errors.append(
            f"[{path}.metadata.value_type] 必须是 {_VALID_VALUE_TYPES} 之一，"
            f"实际为: {_truncate(str(value_type))}"
        )
        return

    if "is_list" not in metadata:
        errors.append(f"[{path}.metadata] property 节点必须包含 is_list")
    elif not isinstance(metadata["is_list"], bool):
        errors.append(f"[{path}.metadata.is_list] 必须是布尔值")

    if value_type == "class":
        subnodes = node.get("subnodes")
        if not isinstance(subnodes, dict) or not subnodes:
            errors.append(
                f"[{path}] value_type=class 的 property 节点必须包含非空的 subnodes"
            )
    else:
        if "subnodes" in node:
            errors.append(
                f"[{path}] 基础类型（{value_type}）的 property 节点不应包含 subnodes"
            )


_TYPE_CHECKERS = {
    "integer": _is_integer,
    "float": _is_float,
    "string": _is_string,
    "boolean": _is_boolean,
    "date_time": _is_string,  # date_time 在 JSON 中为字符串
}


def _truncate(s: str, max_len: int = 50) -> str:
    return s[:max_len] + "..." if len(s) > max_len else s
