"""Agent mode management tools."""

from enum import Enum
from typing import Optional

from emdash.agent.tools.base import BaseTool, ToolResult, ToolCategory


class AgentMode(Enum):
    """Available agent modes."""
    PLAN = "plan"      # Generate specification
    TASKS = "tasks"    # Generate implementation tasks
    CODE = "code"      # Execute code changes


class ModeState:
    """Tracks current agent mode."""

    _instance: Optional["ModeState"] = None

    def __init__(self):
        self.mode: AgentMode = AgentMode.CODE
        self.mode_history: list[AgentMode] = []
        self.transition_reason: Optional[str] = None
        self.tasks_to_code_approved: bool = False  # User must approve tasks->code transition

    @classmethod
    def get_instance(cls) -> "ModeState":
        """Get or create singleton instance."""
        if cls._instance is None:
            cls._instance = cls()
        return cls._instance

    @classmethod
    def reset(cls):
        """Reset the singleton instance."""
        cls._instance = None

    def switch_mode(self, new_mode: AgentMode, reason: str = "") -> AgentMode:
        """Switch to a new mode.

        Args:
            new_mode: The mode to switch to
            reason: Why the mode is being switched

        Returns:
            The previous mode
        """
        old_mode = self.mode
        self.mode_history.append(old_mode)
        self.mode = new_mode
        self.transition_reason = reason
        return old_mode

    def get_mode(self) -> AgentMode:
        """Get current mode."""
        return self.mode

    def set_mode(self, mode: AgentMode):
        """Set mode directly (for CLI commands)."""
        if self.mode != mode:
            self.mode_history.append(self.mode)
            self.mode = mode


class SwitchModeTool(BaseTool):
    """Switch between agent modes (plan, tasks, code)."""

    name = "switch_mode"
    description = """Switch the agent to a different mode.

Modes:
- **plan**: Explore codebase and create a specification
- **tasks**: Generate implementation tasks from a spec
- **code**: Read/write files and execute commands

Use this when:
- You've finished planning and want to generate tasks
- You've finished tasks and want to start coding
- You need to go back to planning to refine the spec"""
    category = ToolCategory.PLANNING

    def execute(self, mode: str, reason: str = "") -> ToolResult:
        """Switch to a new mode.

        Args:
            mode: Target mode (plan, tasks, code)
            reason: Why you're switching modes

        Returns:
            ToolResult with mode transition info
        """
        # Validate mode
        mode_lower = mode.lower().strip()
        try:
            new_mode = AgentMode(mode_lower)
        except ValueError:
            return ToolResult.error_result(
                f"Invalid mode: {mode}",
                suggestions=["Valid modes: plan, tasks, code"],
            )

        state = ModeState.get_instance()
        old_mode = state.get_mode()

        # Block automatic tasks->code transition without user approval
        if old_mode == AgentMode.TASKS and new_mode == AgentMode.CODE:
            if not state.tasks_to_code_approved:
                return ToolResult.error_result(
                    "Cannot switch from tasks to code mode without user approval. "
                    "Use ask_followup_question to ask the user if they want to proceed to implementation.",
                    suggestions=[
                        "Call ask_followup_question first to get user confirmation",
                        "Example: 'Would you like to proceed to coding, or refine the tasks first?'",
                    ],
                )
            # Reset the flag after use
            state.tasks_to_code_approved = False

        # Perform the mode switch
        state.switch_mode(new_mode, reason)

        # Check for required state transitions
        from emdash.agent.tools.spec import SpecState
        spec_state = SpecState.get_instance()

        warnings = []
        if new_mode == AgentMode.TASKS and not spec_state.spec:
            warnings.append("No spec found. Consider creating one first with plan mode.")
        if new_mode == AgentMode.CODE and not spec_state.spec:
            warnings.append("No spec found. You can code without a spec, but having one helps.")

        result = {
            "previous_mode": old_mode.value,
            "current_mode": new_mode.value,
            "reason": reason,
            "message": f"Switched from {old_mode.value} to {new_mode.value} mode.",
        }

        if warnings:
            result["warnings"] = warnings

        return ToolResult.success_result(result)

    def get_schema(self) -> dict:
        return self._make_schema(
            properties={
                "mode": {
                    "type": "string",
                    "enum": ["plan", "tasks", "code"],
                    "description": "Target mode to switch to",
                },
                "reason": {
                    "type": "string",
                    "description": "Brief explanation for why you're switching modes",
                },
            },
            required=["mode"],
        )


class GetModeTool(BaseTool):
    """Get current agent mode and available actions."""

    name = "get_mode"
    description = "Get the current agent mode and what actions are available."
    category = ToolCategory.PLANNING

    def execute(self) -> ToolResult:
        """Get current mode info.

        Returns:
            ToolResult with mode information
        """
        state = ModeState.get_instance()
        mode = state.get_mode()

        mode_info = {
            AgentMode.PLAN: {
                "description": "Explore codebase and create specifications",
                "available_tools": [
                    "semantic_search", "text_search", "expand_node",
                    "get_callers", "get_callees", "get_communities",
                    "submit_spec", "update_spec", "get_spec",
                    "ask_followup_question", "switch_mode",
                ],
                "next_mode": "tasks",
            },
            AgentMode.TASKS: {
                "description": "Generate implementation tasks from spec",
                "available_tools": [
                    "get_spec", "semantic_search", "text_search",
                    "expand_node", "submit_tasks", "switch_mode",
                ],
                "next_mode": "code",
            },
            AgentMode.CODE: {
                "description": "Execute code changes",
                "available_tools": [
                    "read_file", "write_to_file", "apply_diff",
                    "delete_file", "list_files", "execute_command",
                    "get_spec", "semantic_search", "switch_mode",
                ],
                "next_mode": "plan",
            },
        }

        info = mode_info[mode]

        return ToolResult.success_result({
            "current_mode": mode.value,
            "description": info["description"],
            "available_tools": info["available_tools"],
            "suggested_next_mode": info["next_mode"],
            "mode_history": [m.value for m in state.mode_history[-5:]],
        })

    def get_schema(self) -> dict:
        return self._make_schema(properties={}, required=[])
