#!/usr/bin/env python3
"""utils/delegate_controller.py — 서브에이전트 격리 위임 컨트롤러."""

from __future__ import annotations

import threading
from concurrent.futures import ThreadPoolExecutor, as_completed
from dataclasses import dataclass
from typing import Any

from utils.logger import get_logger

logger = get_logger(__name__)

BLOCKED_TOOLS: frozenset[str] = frozenset(["delegate", "clarify", "memory", "send_message"])
MAX_DEPTH: int = 2
MAX_CONCURRENT: int = 3


@dataclass
class SubAgentTask:
    """서브에이전트에 위임할 단일 태스크."""

    goal: str
    context: str | None = None
    toolsets: list[str] | None = None
    max_iterations: int = 50


@dataclass
class SubAgentResult:
    """서브에이전트 실행 결과."""

    task_index: int
    status: str  # "completed" | "failed" | "error" | "interrupted"
    summary: str | None = None
    duration_seconds: float = 0.0
    api_calls: int = 0
    error: str | None = None


class DelegateController:
    """서브에이전트 배치 실행 및 위임 깊이/동시 실행 수 제어."""

    def __init__(self, parent_depth: int = 0) -> None:
        self._depth: int = parent_depth
        self._active_children: list[Any] = []
        self._children_lock = threading.Lock()
        self._interrupted = threading.Event()

    def can_delegate(self) -> tuple[bool, str]:
        """위임 가능 여부와 거부 이유를 반환한다."""
        if self._depth >= MAX_DEPTH:
            return False, f"최대 위임 깊이({MAX_DEPTH}) 초과: 현재 깊이={self._depth}"
        with self._children_lock:
            if len(self._active_children) >= MAX_CONCURRENT:
                return False, f"최대 동시 자식({MAX_CONCURRENT}) 초과"
        return True, "OK"

    def filter_tools(self, toolsets: list[str] | None) -> list[str]:
        """BLOCKED_TOOLS에 포함된 도구를 제거한다."""
        if not toolsets:
            return []
        return [t for t in toolsets if t not in BLOCKED_TOOLS]

    def run(self, tasks: list[SubAgentTask]) -> list[SubAgentResult]:
        """배치 서브에이전트를 ThreadPoolExecutor로 병렬 실행한다."""
        if not tasks:
            return []

        from utils.delegate_runner import run_subagent  # noqa: PLC0415

        results: list[SubAgentResult] = []
        workers = min(len(tasks), MAX_CONCURRENT)

        with ThreadPoolExecutor(max_workers=workers) as executor:
            future_map: dict[Any, int] = {}
            for idx, task in enumerate(tasks):
                if self._interrupted.is_set():
                    results.append(
                        SubAgentResult(
                            task_index=idx,
                            status="interrupted",
                            error="Controller interrupted before dispatch",
                        )
                    )
                    continue

                patched = SubAgentTask(
                    goal=task.goal,
                    context=task.context,
                    toolsets=self.filter_tools(task.toolsets) or None,
                    max_iterations=task.max_iterations,
                )
                with self._children_lock:
                    self._active_children.append(idx)

                fut = executor.submit(run_subagent, patched, self._depth + 1, self._interrupted, idx)
                future_map[fut] = idx

            for fut in as_completed(future_map):
                idx = future_map[fut]
                try:
                    res = fut.result()
                except Exception as exc:  # noqa: BLE001
                    res = SubAgentResult(task_index=idx, status="error", error=str(exc))
                finally:
                    with self._children_lock:
                        if idx in self._active_children:
                            self._active_children.remove(idx)
                results.append(res)
                logger.debug("SubAgent[%d] status=%s dur=%.2fs", res.task_index, res.status, res.duration_seconds)

        results.sort(key=lambda r: r.task_index)
        return results

    def interrupt_children(self) -> None:
        """모든 활성 자식에게 인터럽트를 전파한다."""
        self._interrupted.set()
        logger.debug("DelegateController: interrupt broadcast (depth=%d)", self._depth)
