"""Subagent types: specifications, outputs, and data models.

Defines the core data structures for subagent execution including:
- SubagentSpec: configuration and constraints for each subagent
- SubagentOutput: structured output with evidence and confidence
- PatternType: orchestration patterns (subagent, handoff, router, pipeline)
- ContextMode: context isolation options
- TrustLevel: agent trustworthiness scores
- DelegationCaps: capabilities for delegation
"""

from __future__ import annotations

from dataclasses import dataclass, field
from enum import Enum
from typing import Any


class PatternType(str, Enum):
    """Orchestration pattern for subagent execution."""

    SUBAGENT = "subagent"
    HANDOFF = "handoff"
    ROUTER = "router"
    PIPELINE = "pipeline"


class ContextMode(str, Enum):
    """Context isolation mode for subagents."""

    ISOLATED = "isolated"
    FORK = "fork"
    SHARED = "shared"


class TrustLevel(int, Enum):
    """Agent trust level for delegation authorization (1-5 scale per Cedar)."""

    UNTRUSTED = 1
    NEW = 2
    PRODUCTION = 3
    VERIFIED = 4
    OFFICIAL = 5

    @classmethod
    def required_for_tool(cls, tool_category: str) -> TrustLevel:
        """Return minimum trust level for a tool category."""
        if tool_category in ("financial", "destructive", "admin"):
            return cls.PRODUCTION
        return cls.NEW


@dataclass
class DelegationCaps:
    """Capabilities for agent-to-agent delegation (L2 Cedar layer)."""

    max_depth: int = 5  # Hard limit: depth > 5 denied (Cedar blog Jul 2026)
    max_children: int = 5
    allowed_tools: list[str] = field(default_factory=list)
    allowed_roles: list[str] = field(default_factory=list)

    def allows_capability(self, capability: str) -> bool:
        """Check if a capability is in the allowed set."""
        if not self.allowed_tools and not self.allowed_roles:
            return True
        return capability in self.allowed_tools or capability in self.allowed_roles

    def is_subset_of(self, other: DelegationCaps) -> bool:
        """Check if this delegation is a subset of another."""
        if self.max_depth > other.max_depth:
            return False
        if self.max_children > other.max_children:
            return False
        return True


@dataclass
class EvidenceRef:
    """Reference to evidence produced by a subagent."""

    source: str  # Agent role or tool that produced it
    artifact_id: str | None = None  # Optional file or document ID
    page: int | None = None
    paragraph: int | None = None
    quote: str | None = None

    @classmethod
    def from_tool_result(cls, tool_name: str, result: Any) -> EvidenceRef:
        """Create an evidence reference from a tool result."""
        return cls(source=tool_name, artifact_id=None, quote=str(result)[:500])


@dataclass
class ArtifactRef:
    """Reference to an artifact (file, URL, etc.) produced by a subagent."""

    uri: str
    mime_type: str | None = None
    size_bytes: int | None = None
    checksum_sha256: str | None = None


@dataclass
class SubagentSpec:
    """Specification for a subagent execution.

    Combines all configuration needed for a subagent to execute safely
    with context isolation, token budgets, and delegation constraints.
    """

    role: str
    pattern: PatternType = PatternType.SUBAGENT
    tools_whitelist: list[str] = field(default_factory=list)
    context_mode: ContextMode = ContextMode.ISOLATED
    token_budget: int = 16000  # Hard limit per subagent (Claude Guide)
    timeout_seconds: int = 300
    trust_level: TrustLevel = TrustLevel.NEW
    delegation_caps: DelegationCaps = field(default_factory=DelegationCaps)
    model_assignment: dict[str, Any] = field(default_factory=dict)
    max_turns: int = 10
    requires_human_review: bool = False
    evidence_required: bool = True

    def __post_init__(self) -> None:
        if not self.role:
            raise ValueError("SubagentSpec.role cannot be empty")


@dataclass
class SubagentOutput:
    """Structured output from a subagent execution.

    Implements the "output = summary" rule from Claude Code Guide and
    the typed-output contract from SourceShift research.
    """

    summary: str  # ≤ 2000 tokens (Context budget rule)
    evidence_refs: list[EvidenceRef] = field(default_factory=list)
    artifacts: list[ArtifactRef] = field(default_factory=list)
    confidence: float = 1.0  # 0.0-1.0
    requires_human_review: bool = False
    obstacles: list[str] = field(default_factory=list)
    turn_count: int = 1
    tokens_used: int = 0
    duration_ms: int = 0
    checkpoint_ref: str | None = None

    def add_evidence(self, ref: EvidenceRef) -> None:
        """Add an evidence reference to the output."""
        self.evidence_refs.append(ref)

    def add_obstacle(self, obstacle: str) -> None:
        """Record an obstacle encountered during execution."""
        self.obstacles.append(obstacle)

    def merge(self, other: SubagentOutput) -> None:
        """Merge another output into this one (for aggregation)."""
        self.evidence_refs.extend(other.evidence_refs)
        self.artifacts.extend(other.artifacts)
        self.obstacles.extend(other.obstacles)
        self.confidence = min(self.confidence, other.confidence)


@dataclass
class SubagentDecision:
    """Decision from the supervisor on which subagent to invoke next."""

    next_subagent: str
    reason: str
    input_data: dict[str, Any] = field(default_factory=dict)
    stop: bool = False

    class Config:
        client_skip_validation = True