from __future__ import annotations

import copy
import logging
from typing import TYPE_CHECKING, List

from ...common.registry import DomainRegistry
from ...common.types import BindingStatus, SyncAction
from ...sync_system.config import OrphanAction
from ...sync_system.resolve_ids import IDResolver
from ...engine import (
    decision_note,
    e20_create_prepare,
)

if TYPE_CHECKING:
    from ...common.binding import BindingManager
    from ...common.collection import DataCollection
    from ...common.sync_node import SyncNode
    from ...engine import StateMachineRuntime

logger = logging.getLogger(__name__)


async def add_create_action(
    strategy,
    *,
    from_collection: "DataCollection",
    to_collection: "DataCollection",
    source_is_local: bool,
) -> List["SyncNode"]:
    nodes_to_create = from_collection.filter_by_state_ids(
        node_type=strategy.node_type,
        state_ids=["S13"],
    )

    if not nodes_to_create:
        return []

    id_resolver = IDResolver(strategy.local_collection, strategy.remote_collection, strategy.binding_manager)
    created_nodes = []

    for src_node in nodes_to_create:
        src_data = src_node.get_data()
        if not src_data:
            logger.debug(
                f"[{strategy.node_type}] {'本地' if source_is_local else '远程'}节点 {src_node.node_id} "
                "状态为 MISSING 但数据为空，跳过创建"
            )
            continue

        resolved_data, _ = await id_resolver.resolve_and_validate(
            data=src_data,
            node_type=strategy.node_type,
            depend_fields=dict(strategy.config.depend_fields) if strategy.config.depend_fields else {},
            to_remote=source_is_local,
        )
        if resolved_data is None:
            commit = e20_create_prepare(
                strategy.ensure_runtime(),
                node=src_node,
                spawn_success=False,
            )
            if commit.to_state is not None:
                raise RuntimeError(f"[{strategy.node_type}] E20 fail expected no-transition, got: {commit.to_state}")
            src_node.error = "spawn_failed: 创建前依赖ID解析失败"
            logger.debug(f"[{strategy.node_type}] 创建副作用失败: node={src_node.node_id}, error=创建前依赖ID解析失败")
            continue

        node_class = DomainRegistry.get_node_class(strategy.node_type)
        new_node = None
        try:
            new_node = node_class(
                node_id=to_collection.generate_node_id(),
                data_id="",
                data=resolved_data,
                depend_ids=list(src_node.depend_ids or []),
                binding_status=BindingStatus.NORMAL,
                action=SyncAction.CREATE,
            )
            if src_node.context:
                new_node.context.update(copy.deepcopy(src_node.context))
            await to_collection.add(new_node)

            if source_is_local:
                local_id, remote_id = src_node.node_id, new_node.node_id
            else:
                local_id, remote_id = new_node.node_id, src_node.node_id

            await strategy.binding_manager.bind(strategy.node_type, local_id, remote_id)
            created_nodes.append(new_node)
            new_node.append_log(f"准备创建: 源节点 {src_node.node_id} 复制数据")
            logger.debug(f"[{strategy.node_type}] 创建准备完成: source={src_node.node_id}, target={new_node.node_id}")
        except Exception as exc:
            if new_node is not None:
                try:
                    await to_collection.delete(new_node.node_id)
                except Exception:
                    pass

            commit = e20_create_prepare(
                strategy.ensure_runtime(),
                node=src_node,
                spawn_success=False,
            )
            if commit.to_state is not None:
                raise RuntimeError(f"[{strategy.node_type}] E20 fail expected no-transition, got: {commit.to_state}")

            src_node.error = f"spawn_failed: {exc}"
            logger.debug(f"[{strategy.node_type}] 创建副作用失败: node={src_node.node_id}, error={exc}")
            continue

    return created_nodes


async def run_create(strategy) -> List["SyncNode"]:
    created_nodes: List["SyncNode"] = []

    if strategy.config.local_orphan_action == OrphanAction.CREATE_REMOTE:
        local_nodes = await add_create_action(
            strategy,
            from_collection=strategy.local_collection,
            to_collection=strategy.remote_collection,
            source_is_local=True,
        )
        created_nodes.extend(local_nodes)
        logger.info(f"[{strategy.node_type}] Created {len(local_nodes)} nodes in remote collection (Push)")
    elif strategy.config.local_orphan_action == OrphanAction.DELETE_LOCAL:
        raise NotImplementedError(
            f"[{strategy.node_type}] local_orphan_action=DELETE_LOCAL is not supported in run_create; "
            "use delete stage (delete_ops) explicitly."
        )

    if strategy.config.remote_orphan_action == OrphanAction.CREATE_LOCAL:
        remote_nodes = await add_create_action(
            strategy,
            from_collection=strategy.remote_collection,
            to_collection=strategy.local_collection,
            source_is_local=False,
        )
        created_nodes.extend(remote_nodes)
        logger.info(f"[{strategy.node_type}] Created {len(remote_nodes)} nodes in local collection (Pull)")
    elif strategy.config.remote_orphan_action == OrphanAction.DELETE_REMOTE:
        raise NotImplementedError(
            f"[{strategy.node_type}] remote_orphan_action=DELETE_REMOTE is not supported in run_create; "
            "use delete stage (delete_ops) explicitly."
        )

    return created_nodes


async def finalize_peer_creating_sources(
    *,
    node_type: str,
    local_collection: "DataCollection",
    remote_collection: "DataCollection",
    binding_manager: "BindingManager",
    runtime: "StateMachineRuntime",
) -> tuple[int, int]:
    finalized_success = 0
    finalized_failed = 0

    async def _handle_target_node(*, target_node: "SyncNode", target_is_remote: bool) -> None:
        nonlocal finalized_success, finalized_failed

        if target_is_remote:
            local_id = await binding_manager.get_local_id(node_type, target_node.node_id)
            if not local_id:
                return
            source_node = local_collection.get(local_id)
        else:
            remote_id = await binding_manager.get_remote_id(node_type, target_node.node_id)
            if not remote_id:
                return
            source_node = remote_collection.get(remote_id)

        if source_node is None:
            return
        if source_node.binding_status != BindingStatus.PEER_CREATING:
            return

        if target_node.status.value == "SUCCESS":
            commit = e20_create_prepare(runtime, node=source_node, spawn_success=True)
            if commit.to_state != "S01":
                raise RuntimeError(
                    f"[{node_type}] E20 success expected S01, got {commit.to_state} for source={source_node.node_id}"
                )
            source_node.error = None
            finalized_success += 1
            return

        if target_node.status.value == "FAILED":
            source_node.error = (
                f"peer_create_failed: peer_node={target_node.node_id}, detail={target_node.error or 'unknown'}"
            )
            source_node.append_log(
                f"对侧创建失败，保持 PEER_CREATING: peer={target_node.node_id}, detail={target_node.error or 'unknown'}"
            )
            finalized_failed += 1

    for target_node in remote_collection.filter_by_state_ids(node_type=node_type, state_ids=["S11C", "S12C"]):
        await _handle_target_node(target_node=target_node, target_is_remote=True)

    for target_node in local_collection.filter_by_state_ids(node_type=node_type, state_ids=["S11C", "S12C"]):
        await _handle_target_node(target_node=target_node, target_is_remote=False)

    return finalized_success, finalized_failed
