from __future__ import annotations

from dataclasses import dataclass, field
from typing import Any, Literal

from app.agents.registry import load_builtin_registry_indexes
from app.agents.registry.models import CapabilityManifest, PermissionClass, SideEffectScope


IsolationMode = Literal["none", "session", "worktree"]

_WORKTREE_QUERY_MARKERS = (
    "code",
    "repo",
    "repository",
    "git",
    "worktree",
    "branch",
    "patch",
    "diff",
    "refactor",
    "build",
    "test",
    "fix",
    "file",
    "files",
    "python",
    "typescript",
    "javascript",
    "代码",
    "仓库",
    "分支",
    "补丁",
    "重构",
    "构建",
    "测试",
    "修复",
    "文件",
)


@dataclass(frozen=True)
class IsolationDecision:
    mode: IsolationMode
    reason: str
    tool_names: tuple[str, ...] = ()
    capability_ids: tuple[str, ...] = ()
    metadata: dict[str, Any] = field(default_factory=dict)


def _capability_metadata(capability: CapabilityManifest | None) -> dict[str, Any]:
    if capability is None:
        return {}
    return {
        "capability_id": capability.capability_id,
        "tool_name": capability.tool_name,
        "permission_class": capability.permission_class.value,
        "side_effect_scope": capability.side_effect_scope.value,
        "supports_retry": capability.supports_retry,
        "idempotent": capability.idempotent,
        "safe_for_parallel_use": capability.safe_for_parallel_use,
        "requires_confirmation": capability.requires_confirmation,
    }


def select_isolation_strategy(
    *,
    user_query: str,
    tool_names: list[str] | tuple[str, ...],
    role_value: str,
    execution_mode: str | None,
) -> IsolationDecision:
    indexes = load_builtin_registry_indexes()
    capabilities: list[CapabilityManifest] = []
    capability_ids: list[str] = []

    for tool_name in tool_names:
        capability_id = indexes.capability_id_by_tool_name.get(tool_name)
        capability = indexes.capability_by_id.get(capability_id) if capability_id else None
        if capability is not None:
            capabilities.append(capability)
            capability_ids.append(capability.capability_id)

    normalized_query = (user_query or "").strip().lower()
    has_worktree_query_signal = any(marker in normalized_query for marker in _WORKTREE_QUERY_MARKERS)
    has_write_capability = any(cap.permission_class == PermissionClass.WRITE for cap in capabilities)
    has_external_capability = any(cap.permission_class == PermissionClass.EXTERNAL for cap in capabilities)
    has_non_parallel_capability = any(not cap.safe_for_parallel_use for cap in capabilities)
    has_stateful_side_effect = any(
        cap.side_effect_scope in {SideEffectScope.LOCAL_STATE, SideEffectScope.DB_WRITE}
        for cap in capabilities
    )

    metadata = {
        "role": role_value,
        "execution_mode": execution_mode,
        "capabilities": [_capability_metadata(capability) for capability in capabilities],
        "workspace_strategy": "inline",
        "risk_level": "low",
    }

    if has_worktree_query_signal:
        return IsolationDecision(
            mode="worktree",
            reason="workspace_mutation_signals_detected",
            tool_names=tuple(tool_names),
            capability_ids=tuple(capability_ids),
            metadata={
                **metadata,
                "workspace_strategy": "ephemeral_worktree",
                "risk_level": "high",
            },
        )

    if has_write_capability or has_stateful_side_effect or has_non_parallel_capability:
        return IsolationDecision(
            mode="session",
            reason="stateful_or_non_parallel_tooling",
            tool_names=tuple(tool_names),
            capability_ids=tuple(capability_ids),
            metadata={
                **metadata,
                "workspace_strategy": "isolated_session",
                "risk_level": "medium",
            },
        )

    if execution_mode == "collaboration" or role_value in {"analyst", "librarian"} or has_external_capability:
        return IsolationDecision(
            mode="session",
            reason="context_heavy_or_external_retrieval",
            tool_names=tuple(tool_names),
            capability_ids=tuple(capability_ids),
            metadata={
                **metadata,
                "workspace_strategy": "isolated_session",
                "risk_level": "medium",
            },
        )

    return IsolationDecision(
        mode="none",
        reason="inline_execution_is_sufficient",
        tool_names=tuple(tool_names),
        capability_ids=tuple(capability_ids),
        metadata=metadata,
    )
