from __future__ import annotations

from enum import Enum
from typing import TYPE_CHECKING, Any, List, Optional, Tuple

from ..common.types import BindingStatus

if TYPE_CHECKING:
    from ..common.sync_node import SyncNode


class CoreBindingResult(str, Enum):
    NORMAL = "NORMAL"
    WARNING = "WARNING"
    ABNORMAL = "ABNORMAL"


def classify_core_pair(
    *,
    has_binding_record: bool,
    local_data: Optional[Any],
    peer_data: Optional[Any],
    local_valid: bool = True,
    peer_valid: bool = True,
) -> Tuple[CoreBindingResult, CoreBindingResult]:
    if not has_binding_record:
        raise ValueError("classify_core_pair requires has_binding_record=True")

    local_exists = local_data is not None
    peer_exists = peer_data is not None

    if local_exists and local_valid and peer_exists and peer_valid:
        return CoreBindingResult.NORMAL, CoreBindingResult.NORMAL
    if local_exists and local_valid and (not peer_exists or not peer_valid):
        return CoreBindingResult.WARNING, CoreBindingResult.ABNORMAL
    if (not local_exists or not local_valid) and peer_exists and peer_valid:
        return CoreBindingResult.ABNORMAL, CoreBindingResult.WARNING
    return CoreBindingResult.ABNORMAL, CoreBindingResult.ABNORMAL


def check_dependency_satisfied(
    dependency_nodes: List[Optional["SyncNode"]],
    field_values: Optional[List[str]] = None,
    *,
    allow_empty_dependency_ref: bool = True,
    allow_missing_dep_data_id: bool = True,
) -> bool:
    """
    判断依赖是否满足（供 strategy 组装 E15 上下文）。

    该函数本质是“业务语义判断”，状态机只消费最终上下文：
    - dep_satisfied (bool)
    - dep_errors (结构化错误列表)

    关键语义：
    1) 空依赖引用可放行（allow_empty_dependency_ref=True）
       - 例如 contract_id=""，表示该字段本轮不参与依赖约束。
    2) 依赖节点 data_id 为空可放行（allow_missing_dep_data_id=True）
       - 这不是严格意义上的“无错误”，而是当前业务策略下的“可继续”。
       - 原因：刚创建/回填中的依赖节点可能暂时无 data_id，但业务仍希望继续同步。
       - 未来可通过 strategy 配置将其切换为严格阻断。
    """
    if not dependency_nodes:
        return True

    values = field_values or []
    for i, dep_node in enumerate(dependency_nodes):
        dep_value = values[i] if i < len(values) else ""
        dep_value_str = "" if dep_value is None else str(dep_value)

        if dep_node is None:
            if allow_empty_dependency_ref and dep_value_str == "":
                continue
            return False
        if dep_node.binding_status != BindingStatus.NORMAL:
            return False
        if not dep_node.data_id and not allow_missing_dep_data_id:
            return False
    return True


def collect_dependency_errors(
    dependency_nodes: List[Optional["SyncNode"]],
    field_names: List[str],
    field_types: List[str],
    field_values: List[str],
    *,
    allow_empty_dependency_ref: bool = True,
    allow_missing_dep_data_id: bool = True,
) -> List[str]:
    """
    收集依赖错误（结构化错误码），由 strategy 传给状态机上下文。

    设计原则：
    - 该函数负责“业务层事实归纳”，不是状态迁移本身。
    - 状态机只关心：是否满足(dep_satisfied) 与错误列表(dep_errors)。

    关于“依赖项缺乏数据不一定是错”：
    - 某些业务场景里，父节点可能刚创建或尚未回填 data_id；
      但子节点仍需要进入同步队列，等待后续补齐。
    - 因此默认 allow_missing_dep_data_id=True：不产出 no_data_id 错误。
    - 后续可由 strategy 配置切换为严格模式（False），届时 no_data_id 将参与阻断。
    """
    errors: List[str] = []
    for i, dep_node in enumerate(dependency_nodes):
        field_name = field_names[i]
        field_value = field_values[i] if i < len(field_values) else ""
        field_value_str = "" if field_value is None else str(field_value)

        if dep_node is None:
            if allow_empty_dependency_ref and field_value_str == "":
                continue
            errors.append(f"not_found|{field_name}|{field_types[i]}|{field_values[i]}")
            continue
        if dep_node.binding_status != BindingStatus.NORMAL:
            errors.append(f"bad_status|{field_name}|{dep_node.binding_status.value}")
            continue
        if not dep_node.data_id and not allow_missing_dep_data_id:
            status_info = f"status={dep_node.status.value}" if dep_node.status else "status=未知"
            errors.append(f"no_data_id|{field_name}|{status_info}")
    return errors
