"""Loop infrastructure for agent execution.

This module provides:
- pydantic_graph primitives for building loops and state machines
- Skill context helper for system prompt injection
- Re-exports of typed models from core.types

Loops are the iterative reasoning patterns expressed as pathway topologies:
- ReAct: Think → Act → Observe → repeat (single LLMNode with tool rounds)
- Plan-Execute: Plan → Execute steps → Synthesize (three-node pipeline)
- Reflection: Act → Reflect → refine (two-node pipeline)
- Simple: Single LLM call (single-shot)
- CodeAct: Hybrid code execution + structured tools (single LLMNode)

Pathways are loaded by PathwayService from config directories; agents
reference a pathway by ID.
"""

from __future__ import annotations

from typing import TYPE_CHECKING

from core.skills import get_skill_registry

if TYPE_CHECKING:
    from core.pathways.context import Context


# =============================================================================
# SKILL CONTEXT HELPER
# =============================================================================


def get_skill_instructions_for_tools(tool_patterns: list[str], ctx: Context) -> str:
    r"""Get skill instructions for the tools being used.

    When an agent uses tools like "web.*", this auto-injects the web skill's
    instructions into the system prompt. This gives the LLM domain knowledge
    about HOW to use the tools effectively.

    Uses the global skill registry as the source of truth.

    Args:
        tool_patterns: List of tool patterns (e.g., ["web.*", "memory.get"])
        ctx: Execution context (unused, kept for API compatibility)

    Returns
    -------
        Formatted skill instructions string (empty if no skills found)

    Example:
        >>> get_skill_instructions_for_tools(["web.*"], ctx)
        "## Web Skill\\nUse web.search for queries...\\n"
    """
    skill_registry = get_skill_registry()

    if len(skill_registry) == 0:
        return ""

    # Extract skill IDs from tool patterns
    skill_ids: set[str] = set()
    include_all = False

    for pattern in tool_patterns:
        if pattern == "*":
            include_all = True
            break
        if "." in pattern:
            skill_ids.add(pattern.split(".")[0])
        else:
            skill_ids.add(pattern)

    if include_all:
        skill_ids = set(skill_registry.keys())

    # Build instructions sections
    sections: list[str] = []
    for skill_id in sorted(skill_ids):
        skill = skill_registry.get(skill_id)
        if skill is None:
            continue

        # Skills are Pydantic models - direct attribute access
        instructions = skill.instructions
        name = skill.name or skill_id

        if instructions:
            sections.append(f"## {name} Skill\n{instructions}")

    return "\n\n".join(sections)


# =============================================================================
# PYDANTIC_GRAPH PRIMITIVES (re-exported for custom state machines)
# =============================================================================

from pydantic_graph import (
    BaseNode,
    End,
    Graph,
    GraphRunContext,
)

# =============================================================================
# TYPED MODELS (re-exported from core.types)
# =============================================================================
from core.types import (
    LoopResult,
    StepFailure,
    StepOutcome,
    StepSkipped,
    StepSuccess,
    ToolCallRecord,
    count_failures,
    get_last_error,
    is_step_successful,
)

__all__ = [
    # pydantic_graph primitives (for building custom orchestrators)
    "BaseNode",
    "End",
    "Graph",
    "GraphRunContext",
    # Typed Models (from core.types)
    "LoopResult",
    "StepFailure",
    "StepOutcome",
    "StepSkipped",
    "StepSuccess",
    "ToolCallRecord",
    # Model helpers
    "count_failures",
    "get_last_error",
    "is_step_successful",
    # Helpers
    "get_skill_instructions_for_tools",
]
