"""SDK Agent Runner - Uses Anthropic Agent SDK for Claude models.

This module provides an alternative runner that uses the official Anthropic
Agent SDK (claude-agent-sdk) for Claude models. This enables native support
for Skills, MCP tools, and extended thinking.

For non-Claude models (Fireworks, OpenAI, etc.), use the standard AgentRunner.
"""

from pathlib import Path
from typing import AsyncIterator, Optional, TYPE_CHECKING
import json

from ...utils.logger import log

if TYPE_CHECKING:
    from ..events import AgentEventEmitter


class SDKAgentRunner:
    """Agent runner using Anthropic Agent SDK directly.

    This runner uses the official claude-agent-sdk package which bundles
    Claude Code CLI. It provides native support for:
    - Skills (.claude/skills/)
    - MCP servers (in-process and external)
    - Extended thinking
    - Built-in tools (Read, Write, Bash, Glob, Grep)

    Example:
        runner = SDKAgentRunner(
            model="claude-sonnet-4-20250514",
            cwd="/path/to/project",
        )

        async for event in runner.run("Find authentication code"):
            print(event)
    """

    # Default model for SDK (must be a Claude model)
    DEFAULT_MODEL = "claude-sonnet-4-20250514"

    def __init__(
        self,
        model: str = DEFAULT_MODEL,
        cwd: Optional[str] = None,
        emitter: Optional["AgentEventEmitter"] = None,
        system_prompt: Optional[str] = None,
        plan_mode: bool = False,
    ):
        """Initialize the SDK runner.

        Args:
            model: Claude model to use (e.g., "claude-sonnet-4-20250514")
            cwd: Working directory for the agent
            emitter: Event emitter for streaming events to UI
            system_prompt: Custom system prompt
            plan_mode: If True, restrict to read-only tools
        """
        self.model = model
        self.cwd = cwd or str(Path.cwd())
        self.emitter = emitter
        self.system_prompt = system_prompt
        self.plan_mode = plan_mode
        self._emdash_server = None  # Lazy init

    def _get_emdash_mcp_server(self):
        """Get or create in-process MCP server for emdash-specific tools."""
        if self._emdash_server is not None:
            return self._emdash_server

        from claude_agent_sdk import tool, create_sdk_mcp_server

        @tool(
            "semantic_search",
            "Search code using natural language. Returns relevant functions, classes, and files.",
            {"query": str, "limit": int, "entity_types": list}
        )
        async def semantic_search(args):
            """Run semantic search on the codebase."""
            try:
                from ...graph.connection import get_connection
                from ..tools.search import SemanticSearchTool

                conn = get_connection()
                search_tool = SemanticSearchTool(conn)
                result = search_tool.execute(
                    query=args["query"],
                    limit=args.get("limit", 10),
                    entity_types=args.get("entity_types"),
                )

                if result.success:
                    # Format result for LLM
                    result_text = json.dumps(result.to_dict(), indent=2)
                    return {
                        "content": [
                            {"type": "text", "text": result_text}
                        ]
                    }
                else:
                    return {
                        "content": [
                            {"type": "text", "text": f"Search failed: {result.error}"}
                        ],
                        "isError": True,
                    }
            except Exception as e:
                log.exception("Semantic search failed")
                return {
                    "content": [
                        {"type": "text", "text": f"Search error: {str(e)}"}
                    ],
                    "isError": True,
                }

        self._emdash_server = create_sdk_mcp_server(
            name="emdash",
            version="1.0.0",
            tools=[semantic_search]
        )
        return self._emdash_server

    def _get_options(self):
        """Build SDK options with native features."""
        from claude_agent_sdk import ClaudeAgentOptions

        # Base allowed tools - SDK built-ins
        allowed_tools = [
            # Read-only tools (always available)
            "Read", "Glob", "Grep", "Skill",
            # emdash custom tools
            "mcp__emdash__semantic_search",
        ]

        # Add write tools unless in plan mode
        if not self.plan_mode:
            allowed_tools.extend([
                "Write", "Bash",
            ])

        # Build MCP servers config
        mcp_servers = {
            "emdash": self._get_emdash_mcp_server(),  # In-process (semantic search)
        }

        # Add graph MCP if available
        graph_mcp_path = Path(self.cwd) / ".emdash" / "mcp.json"
        if graph_mcp_path.exists():
            # External graph MCP server for code analysis
            mcp_servers["graph"] = {
                "type": "stdio",
                "command": "python",
                "args": ["-m", "emdash_graph_mcp.server"],
                "env": {"REPO_ROOT": self.cwd},
            }
            allowed_tools.extend([
                "mcp__graph__expand_node",
                "mcp__graph__get_callers",
                "mcp__graph__get_callees",
            ])

        return ClaudeAgentOptions(
            model=self.model,
            cwd=self.cwd,
            system_prompt=self.system_prompt,

            # Native Skills support - load from .claude/skills/
            setting_sources=["user", "project"],

            # MCP servers
            mcp_servers=mcp_servers,

            # Allowed tools
            allowed_tools=allowed_tools,

            # Permission mode
            permission_mode="acceptEdits" if not self.plan_mode else "plan",
        )

    def _emit(self, event_type, data: dict):
        """Emit event to emitter if available."""
        if self.emitter:
            from ..events import EventType
            self.emitter.emit(getattr(EventType, event_type), data)

    async def run(self, prompt: str) -> AsyncIterator[dict]:
        """Execute agent with SDK.

        Args:
            prompt: User prompt/task

        Yields:
            Event dicts for UI streaming
        """
        from claude_agent_sdk import (
            ClaudeSDKClient,
            AssistantMessage,
            TextBlock,
            ToolUseBlock,
            ToolResultBlock,
            ResultMessage,
        )

        options = self._get_options()

        # Emit session start
        self._emit("SESSION_START", {
            "model": self.model,
            "agent_name": "Emdash Code (SDK)",
        })

        try:
            async with ClaudeSDKClient(options=options) as client:
                await client.query(prompt)

                async for message in client.receive_response():
                    # Process message and yield events
                    if isinstance(message, AssistantMessage):
                        for block in message.content:
                            if isinstance(block, TextBlock):
                                self._emit("PARTIAL_RESPONSE", {"text": block.text})
                                yield {"type": "text", "content": block.text}

                            elif isinstance(block, ToolUseBlock):
                                self._emit("TOOL_START", {
                                    "tool": block.name,
                                    "input": block.input,
                                })
                                yield {
                                    "type": "tool_use",
                                    "name": block.name,
                                    "input": block.input,
                                }

                            elif isinstance(block, ToolResultBlock):
                                self._emit("TOOL_RESULT", {
                                    "tool": block.tool_use_id,
                                    "result": str(block.content)[:500],
                                })
                                yield {
                                    "type": "tool_result",
                                    "tool_use_id": block.tool_use_id,
                                    "content": block.content,
                                }

                    elif isinstance(message, ResultMessage):
                        yield {
                            "type": "result",
                            "duration_ms": getattr(message, "duration_ms", None),
                            "cost_usd": getattr(message, "total_cost_usd", None),
                        }

        except Exception as e:
            log.exception("SDK agent execution failed")
            self._emit("ERROR", {"error": str(e)})
            yield {"type": "error", "error": str(e)}

        finally:
            self._emit("SESSION_END", {})

    async def chat(self, message: str) -> str:
        """Simple chat method for compatibility.

        Args:
            message: User message

        Returns:
            Assistant response text
        """
        response_text = ""
        async for event in self.run(message):
            if event.get("type") == "text":
                response_text += event.get("content", "")
        return response_text


def is_claude_model(model: str) -> bool:
    """Check if a model string refers to a Claude model.

    Args:
        model: Model string (e.g., "claude-sonnet-4", "haiku", "fireworks:...")

    Returns:
        True if this is a Claude model that can use the SDK
    """
    model_lower = model.lower()

    # Check for non-Claude providers first (more specific)
    non_claude_patterns = [
        "fireworks:",
        "openai:",
        "gpt-",
        "o1",
        "o3",
        "o4",
        "accounts/fireworks",
        "minimax",
        "glm",
        "gemini",
        "llama",
        "mistral",
        "qwen",
    ]

    for pattern in non_claude_patterns:
        if pattern in model_lower:
            return False

    # Check for Claude indicators
    claude_indicators = [
        "claude",
        "anthropic",
        "haiku",
        "sonnet",
        "opus",
    ]

    for indicator in claude_indicators:
        if indicator in model_lower:
            return True

    # Default: assume not Claude (safer)
    return False
