"""
Skill Executor - Execute skills by name

Provides SkillTool that inherits from BaseTool for executing skills.
Skills are loaded from SKILL.md files and their content is injected into prompts.
"""

from dataclasses import dataclass, field
from pathlib import Path
from typing import TYPE_CHECKING, Any, Callable, Optional

from groknroll.skills.loader import Skill, SkillLoader
from groknroll.tools.base_tool import BaseTool

if TYPE_CHECKING:
    from groknroll.permissions.manager import PermissionManager


@dataclass
class SkillExecutionResult:
    """
    Result of skill execution

    Attributes:
        success: Whether execution succeeded
        skill_name: Name of the executed skill
        output: Output from skill execution (LLM response or error message)
        skill: The loaded skill (if found)
        metadata: Additional execution metadata
    """

    success: bool
    skill_name: str
    output: str
    skill: Optional[Skill] = None
    metadata: dict[str, Any] = field(default_factory=dict)

    def __str__(self) -> str:
        status = "success" if self.success else "failed"
        return f"SkillExecutionResult({self.skill_name}: {status})"


class SkillTool(BaseTool):
    """
    Tool for executing skills by name

    Loads skill content from SKILL.md files and sends to LLM for execution.
    Integrates with RLM for unlimited context skill execution.

    Accepts:
        name: str - Skill name to execute
        args: str - Optional arguments to pass to the skill
        context: dict - Optional context variables

    Returns:
        SkillExecutionResult with execution output

    Example:
        tool = SkillTool(project_path=Path.cwd())
        result = await tool.execute(name="git-release", args="v1.0.0")

        if result.success:
            print(result.output)
    """

    def __init__(
        self,
        project_path: Optional[Path] = None,
        llm_callback: Optional[Callable[[str], str]] = None,
        permission_manager: Optional["PermissionManager"] = None,
    ):
        """
        Initialize SkillTool

        Args:
            project_path: Project root directory for skill discovery
            llm_callback: Optional callback for LLM execution.
                         Signature: (prompt: str) -> str
                         If not provided, returns skill content for manual execution.
            permission_manager: Optional permission manager for skill permission checks.
                               If not provided, all skills are allowed.
        """
        self._project_path = project_path or Path.cwd()
        self._loader = SkillLoader(project_path=self._project_path)
        self._llm_callback = llm_callback
        self._permission_manager = permission_manager

    @property
    def name(self) -> str:
        """Tool identifier"""
        return "skill"

    @property
    def description(self) -> str:
        """Human-readable description"""
        return "Execute a skill by name"

    def validate_params(self, **kwargs) -> dict[str, Any]:
        """
        Validate parameters before execution

        Args:
            **kwargs: Must contain 'name' parameter

        Returns:
            Validated parameters dict

        Raises:
            ValueError: If name parameter is missing or invalid
        """
        if "name" not in kwargs:
            raise ValueError("name parameter is required")

        name = kwargs["name"]
        if not isinstance(name, str):
            raise ValueError(f"name must be a string, got {type(name).__name__}")

        if not name.strip():
            raise ValueError("name cannot be empty")

        return kwargs

    async def execute(
        self,
        name: str,
        args: str = "",
        context: Optional[dict[str, Any]] = None,
        **kwargs,
    ) -> SkillExecutionResult:
        """
        Execute a skill by name

        Args:
            name: Skill name to execute
            args: Optional arguments for the skill
            context: Optional context variables to inject

        Returns:
            SkillExecutionResult with execution output

        Example:
            result = await tool.execute(name="git-release", args="v1.0.0")
        """
        # Validate parameters
        self.validate_params(name=name)

        # Check skill permissions
        permission = self.check_skill_permission(name)
        if permission == "deny":
            return SkillExecutionResult(
                success=False,
                skill_name=name,
                output=f"Permission denied for skill '{name}'",
                metadata={"error": "permission_denied", "permission": permission},
            )

        # Load the skill
        skill = self._loader.load(name)
        if not skill:
            return SkillExecutionResult(
                success=False,
                skill_name=name,
                output=f"Skill '{name}' not found",
                metadata={"error": "skill_not_found"},
            )

        # Build the prompt
        prompt = self._build_prompt(skill, args, context)

        # Execute via LLM callback or return prompt for manual execution
        if self._llm_callback:
            try:
                output = self._llm_callback(prompt)
                return SkillExecutionResult(
                    success=True,
                    skill_name=name,
                    output=output,
                    skill=skill,
                    metadata={"args": args, "context": context},
                )
            except Exception as e:
                return SkillExecutionResult(
                    success=False,
                    skill_name=name,
                    output=f"Execution error: {e}",
                    skill=skill,
                    metadata={"error": str(e), "args": args},
                )
        else:
            # No LLM callback - return the prepared prompt
            return SkillExecutionResult(
                success=True,
                skill_name=name,
                output=prompt,
                skill=skill,
                metadata={"mode": "prompt_only", "args": args, "context": context},
            )

    def execute_sync(
        self,
        name: str,
        args: str = "",
        context: Optional[dict[str, Any]] = None,
    ) -> SkillExecutionResult:
        """
        Synchronous version of execute

        Args:
            name: Skill name to execute
            args: Optional arguments for the skill
            context: Optional context variables

        Returns:
            SkillExecutionResult
        """
        import asyncio

        return asyncio.get_event_loop().run_until_complete(
            self.execute(name=name, args=args, context=context)
        )

    def _build_prompt(
        self,
        skill: Skill,
        args: str = "",
        context: Optional[dict[str, Any]] = None,
    ) -> str:
        """
        Build the execution prompt from skill content

        Args:
            skill: Loaded skill
            args: Arguments to inject
            context: Context variables to inject

        Returns:
            Complete prompt for LLM execution
        """
        prompt_parts = []

        # Skill instruction header
        prompt_parts.append(f"# Executing Skill: {skill.name}")
        prompt_parts.append("")

        # Skill description if available
        if skill.description:
            prompt_parts.append(f"**Description**: {skill.description}")
            prompt_parts.append("")

        # Arguments if provided
        if args:
            prompt_parts.append(f"**Arguments**: {args}")
            prompt_parts.append("")

        # Context variables if provided
        if context:
            prompt_parts.append("**Context**:")
            for key, value in context.items():
                prompt_parts.append(f"- {key}: {value}")
            prompt_parts.append("")

        # Skill content (the main instructions)
        prompt_parts.append("---")
        prompt_parts.append("")
        prompt_parts.append(skill.content)

        return "\n".join(prompt_parts)

    def list_skills(self) -> list[str]:
        """
        List all available skill names

        Returns:
            List of skill names
        """
        return self._loader.get_skill_names()

    def get_skill(self, name: str) -> Optional[Skill]:
        """
        Get a skill by name without executing

        Args:
            name: Skill name

        Returns:
            Skill if found, None otherwise
        """
        return self._loader.load(name)

    def reload_skills(self) -> None:
        """Reload skills from disk"""
        self._loader.reload()

    def check_skill_permission(self, skill_name: str) -> str:
        """
        Check permission for executing a skill.

        Args:
            skill_name: Name of skill to check

        Returns:
            "allow", "ask", or "deny"
        """
        if self._permission_manager is None:
            return "allow"  # No permission manager = allow all
        return self._permission_manager.check_skill(skill_name)

    def set_permission_manager(self, permission_manager: Optional["PermissionManager"]) -> None:
        """
        Set or update the permission manager.

        Args:
            permission_manager: Permission manager or None to disable checks
        """
        self._permission_manager = permission_manager

    def __str__(self) -> str:
        return f"SkillTool(project={self._project_path})"

    def __repr__(self) -> str:
        skill_count = len(self._loader)
        return f"SkillTool(project_path='{self._project_path}', skills={skill_count})"


# Convenience function for quick skill execution
def execute_skill(
    name: str,
    args: str = "",
    context: Optional[dict[str, Any]] = None,
    project_path: Optional[Path] = None,
    llm_callback: Optional[Callable[[str], str]] = None,
    permission_manager: Optional["PermissionManager"] = None,
) -> SkillExecutionResult:
    """
    Execute a skill by name (convenience function)

    Args:
        name: Skill name
        args: Optional arguments
        context: Optional context variables
        project_path: Project root directory
        llm_callback: Optional LLM callback
        permission_manager: Optional permission manager

    Returns:
        SkillExecutionResult
    """
    tool = SkillTool(
        project_path=project_path,
        llm_callback=llm_callback,
        permission_manager=permission_manager,
    )
    return tool.execute_sync(name=name, args=args, context=context)
