import json
import logging
from typing import Optional, Dict, Any, List
from .persistence import PersistenceBackend
from .types import BindingStatus, BindingRecord

logger = logging.getLogger(__name__)


class BindingManager:
    """
    BindingManager 负责维护不同 DataCollection 间节点的绑定关系。
    所有查询均在内存中进行，持久化仅作为备份。
    """
    def __init__(self, persistence: PersistenceBackend, auto_persist: bool = True, scope: str = "global"):
        self.persistence = persistence
        self.auto_persist = auto_persist
        self.scope = scope
        # { node_type: { local_id: remote_id } }
        self._bindings: Dict[str, Dict[str, Optional[str]]] = {}
        self._deleted: Dict[str, set[str]] = {}

    def _get_type_bindings(self, node_type: str) -> Dict[str, Optional[str]]:
        if node_type not in self._bindings:
            self._bindings[node_type] = {}
        return self._bindings[node_type]

    def _get_deleted(self, node_type: str) -> set[str]:
        if node_type not in self._deleted:
            self._deleted[node_type] = set()
        return self._deleted[node_type]

    async def bind(self, node_type: str, local_id: str, remote_id: Optional[str], manual: bool = False):
        """
        建立本地节点与远程节点间的映射（带中文日志）
        
        Args:
            node_type: 节点类型
            local_id: 本地节点 ID
            remote_id: 远程节点 ID
            manual: 是否为手工绑定（用于日志区分）
        """
        bindings = self._get_type_bindings(node_type)
        bindings[local_id] = remote_id
        self._get_deleted(node_type).discard(local_id)
        
        # 记录日志：手工绑定保留 info，自动绑定降为 debug 避免日志过量
        op_type = "手工" if manual else "自动"
        log_line = (
            f"[{op_type}绑定] 节点类型={node_type}, "
            f"本地节点={local_id} <-> 远程节点={remote_id}"
        )
        if manual:
            logger.info(log_line)
        else:
            logger.debug(log_line)
        
        if self.auto_persist:
            await self.persistence.save_binding(self.scope, node_type, local_id, {"remote_id": remote_id})

    async def unbind(self, node_type: str, local_id: str, manual: bool = False):
        """
        解除映射关系并物理删除（带中文日志）
        
        Args:
            node_type: 节点类型
            local_id: 本地节点 ID
            manual: 是否为手工解绑（用于日志区分）
        """
        bindings = self._get_type_bindings(node_type)
        remote_id = bindings.get(local_id)
        
        if local_id in bindings:
            del bindings[local_id]
        
        # 记录日志（中文，区分手工/自动）
        op_type = "手工" if manual else "自动"
        logger.info(
            f"[{op_type}解绑] 节点类型={node_type}, "
            f"本地节点={local_id} (原远程节点={remote_id})"
        )
        
        if self.auto_persist:
            await self.persistence.delete_binding(self.scope, node_type, local_id)
        else:
            self._get_deleted(node_type).add(local_id)

    async def get_remote_id(self, node_type: str, local_id: str) -> Optional[str]:
        """查询本地节点对应的远程 ID"""
        bindings = self._get_type_bindings(node_type)
        return bindings.get(local_id)

    async def get_local_id(self, node_type: str, remote_id: str) -> Optional[str]:
        """查询远程节点对应的本地 ID (内存遍历)"""
        bindings = self._get_type_bindings(node_type)
        for lid, rid in bindings.items():
            if rid == remote_id:
                return lid
        return None

    async def get_record(self, node_type: str, local_id: str) -> Optional[BindingRecord]:
        """获取结构化绑定记录"""
        remote_id = self._get_type_bindings(node_type).get(local_id)
        if remote_id is None:
            return None
        return BindingRecord(
            local_id=local_id,
            remote_id=remote_id
        )

    async def get_all_records(self, node_type: str) -> List[BindingRecord]:
        """获取指定类型的所有完整绑定记录 (结构化对象)"""
        bindings = self._get_type_bindings(node_type)
        return [BindingRecord(local_id=lid, remote_id=rid) for lid, rid in bindings.items()]

    async def get_all_bindings(self, node_type: str) -> List[tuple[str, Optional[str]]]:
        """获取指定类型的所有 ID 映射关系 (local_id, remote_id)"""
        bindings = self._get_type_bindings(node_type)
        return [(lid, rid) for lid, rid in bindings.items()]

    async def load_from_persistence(self, node_type: str):
        """从持久化层加载特定类型的绑定关系到内存"""
        raw_bindings = await self.persistence.load_bindings(self.scope, node_type)
        bindings = self._get_type_bindings(node_type)
        bindings.clear()
        self._get_deleted(node_type).clear()
        for rb in raw_bindings:
            payload = rb.get("payload") or {}
            bindings[rb["local_id"]] = payload.get("remote_id")

    def import_bindings(self, node_type: str, bindings: Dict[str, Optional[str]]) -> None:
        """导入既有绑定到当前 manager，仅写内存，不触发持久化。"""
        target = self._get_type_bindings(node_type)
        target.clear()
        target.update(dict(bindings))
        self._get_deleted(node_type).clear()

    async def prune_missing_nodes(
        self,
        *,
        local_collection,
        remote_collection,
        node_types: Optional[List[str]] = None,
    ) -> int:
        target_types = list(node_types) if node_types is not None else list(self._bindings.keys())
        deleted_count = 0

        for node_type in target_types:
            bindings = self._get_type_bindings(node_type)
            if not bindings:
                continue

            valid_local_ids = {node.node_id for node in local_collection.filter(node_type=node_type)}
            valid_remote_ids = {node.node_id for node in remote_collection.filter(node_type=node_type)}

            stale_local_ids = [
                local_id
                for local_id, remote_id in bindings.items()
                if local_id not in valid_local_ids or (remote_id is not None and remote_id not in valid_remote_ids)
            ]

            for local_id in stale_local_ids:
                del bindings[local_id]
                deleted_count += 1
                if self.auto_persist:
                    await self.persistence.delete_binding(self.scope, node_type, local_id)
                else:
                    self._get_deleted(node_type).add(local_id)

        return deleted_count

    async def persist(self, *, exclude_node_types: Optional[List[str]] = None) -> None:
        """将所有绑定关系写回持久化层"""
        if not self._bindings and not self._deleted:
            logger.info("🔍 [BindingManager.persist] 跳过：无绑定变更")
            return

        excluded = set(node_type for node_type in (exclude_node_types or []) if node_type)

        type_count = 0
        saved_total = 0
        deleted_total = 0
        touched_types: List[str] = []

        for node_type, bindings in self._bindings.items():
            if node_type in excluded:
                continue
            deleted = list(self._get_deleted(node_type))
            binding_count = len(bindings)
            deleted_count = len(deleted)

            type_count += 1
            saved_total += binding_count
            deleted_total += deleted_count
            if binding_count > 0 or deleted_count > 0:
                touched_types.append(node_type)

            if deleted:
                await self.persistence.delete_bindings_bulk(self.scope, node_type, deleted)
                self._get_deleted(node_type).clear()

            if bindings:
                await self.persistence.save_bindings_bulk(self.scope, node_type, bindings)

        touched_text = ",".join(touched_types) if touched_types else "none"
        logger.info(
            f"🔍 [BindingManager.persist] 完成: scope={self.scope}, types={type_count}, saved={saved_total}, deleted={deleted_total}, touched=[{touched_text}]"
        )
