"""
LangChain tools for SkillBroker integration.
"""

from typing import Optional, Type, Any, Dict, List
from pydantic import BaseModel, Field
from langchain_core.tools import BaseTool
from langchain_core.callbacks import CallbackManagerForToolRun

from .client import SkillBrokerClient, SkillBrokerError


class SkillQueryInput(BaseModel):
    """Input schema for SkillBrokerTool."""
    query: str = Field(description="The question or request to send to the skill")


class SkillSearchInput(BaseModel):
    """Input schema for SkillBrokerSearchTool."""
    query: str = Field(description="Search query to find relevant skills")
    category: Optional[str] = Field(
        default=None,
        description="Optional category filter (e.g., 'Finance', 'AI & ML', 'Data & APIs')"
    )


class SkillBrokerTool(BaseTool):
    """
    LangChain tool for invoking a specific SkillBroker skill.

    This tool allows your LangChain agent to query a specific skill
    from the SkillBroker marketplace to get expert knowledge.

    Example:
        ```python
        from langchain.agents import AgentExecutor, create_openai_tools_agent
        from langchain_openai import ChatOpenAI
        from skillbroker_langchain import SkillBrokerTool

        # Create a tool for a specific skill
        tax_tool = SkillBrokerTool(
            skill_id="freelancer-tax-advisor",
            name="tax_advisor",
            description="Get expert advice on freelancer tax questions"
        )

        # Use in your agent
        llm = ChatOpenAI(model="gpt-4")
        agent = create_openai_tools_agent(llm, [tax_tool], prompt)
        executor = AgentExecutor(agent=agent, tools=[tax_tool])

        result = executor.invoke({"input": "Can I deduct my home office?"})
        ```
    """

    name: str = "skillbroker_skill"
    description: str = "Query an expert skill from SkillBroker"
    args_schema: Type[BaseModel] = SkillQueryInput
    return_direct: bool = False

    skill_id: str
    client: Optional[SkillBrokerClient] = None
    api_url: Optional[str] = None
    api_key: Optional[str] = None

    def __init__(
        self,
        skill_id: str,
        name: Optional[str] = None,
        description: Optional[str] = None,
        api_url: Optional[str] = None,
        api_key: Optional[str] = None,
        **kwargs
    ):
        """
        Initialize the SkillBroker tool.

        Args:
            skill_id: ID of the skill to invoke.
            name: Custom name for the tool.
            description: Custom description for the tool.
            api_url: SkillBroker API URL (defaults to production).
            api_key: Optional API key for authentication.
        """
        super().__init__(
            skill_id=skill_id,
            name=name or f"skillbroker_{skill_id.replace('-', '_')}",
            description=description or f"Query the {skill_id} skill from SkillBroker",
            api_url=api_url,
            api_key=api_key,
            **kwargs
        )
        self.client = SkillBrokerClient(api_url=api_url, api_key=api_key)

    def _run(
        self,
        query: str,
        run_manager: Optional[CallbackManagerForToolRun] = None,
    ) -> str:
        """Execute the tool synchronously."""
        try:
            response = self.client.invoke(self.skill_id, query)
            return response.response
        except SkillBrokerError as e:
            return f"Error querying skill: {str(e)}"

    async def _arun(
        self,
        query: str,
        run_manager: Optional[CallbackManagerForToolRun] = None,
    ) -> str:
        """Execute the tool asynchronously."""
        # For now, use sync version. Can add async client later.
        return self._run(query, run_manager)


class SkillBrokerSearchTool(BaseTool):
    """
    LangChain tool for searching the SkillBroker marketplace.

    This tool allows your agent to discover available skills
    based on a search query or category.

    Example:
        ```python
        from skillbroker_langchain import SkillBrokerSearchTool

        search_tool = SkillBrokerSearchTool()

        # Agent can search for relevant skills
        results = search_tool.invoke({"query": "financial advice"})
        ```
    """

    name: str = "skillbroker_search"
    description: str = (
        "Search the SkillBroker marketplace for expert skills. "
        "Use this to find skills that can help answer domain-specific questions. "
        "Returns a list of available skills with their descriptions."
    )
    args_schema: Type[BaseModel] = SkillSearchInput
    return_direct: bool = False

    client: Optional[SkillBrokerClient] = None
    api_url: Optional[str] = None
    api_key: Optional[str] = None
    max_results: int = 5

    def __init__(
        self,
        api_url: Optional[str] = None,
        api_key: Optional[str] = None,
        max_results: int = 5,
        **kwargs
    ):
        """
        Initialize the search tool.

        Args:
            api_url: SkillBroker API URL (defaults to production).
            api_key: Optional API key for authentication.
            max_results: Maximum number of results to return.
        """
        super().__init__(
            api_url=api_url,
            api_key=api_key,
            max_results=max_results,
            **kwargs
        )
        self.client = SkillBrokerClient(api_url=api_url, api_key=api_key)

    def _run(
        self,
        query: str,
        category: Optional[str] = None,
        run_manager: Optional[CallbackManagerForToolRun] = None,
    ) -> str:
        """Execute the search synchronously."""
        try:
            results = self.client.search(
                query=query,
                category=category,
                limit=self.max_results,
            )

            if not results.skills:
                return "No skills found matching your query."

            output = f"Found {len(results.skills)} skills:\n\n"
            for skill in results.skills:
                output += f"- **{skill.name}** (ID: {skill.id})\n"
                output += f"  Category: {skill.category}\n"
                output += f"  {skill.description}\n"
                if skill.quality and skill.quality.quality_score > 0:
                    output += f"  Quality Score: {skill.quality.quality_score}\n"
                output += "\n"

            return output

        except SkillBrokerError as e:
            return f"Error searching skills: {str(e)}"

    async def _arun(
        self,
        query: str,
        category: Optional[str] = None,
        run_manager: Optional[CallbackManagerForToolRun] = None,
    ) -> str:
        """Execute the search asynchronously."""
        return self._run(query, category, run_manager)


class SkillBrokerDynamicTool(BaseTool):
    """
    A dynamic tool that can search for and invoke skills automatically.

    This tool first searches for relevant skills, then invokes the best match.
    Useful when you don't know which skill to use ahead of time.

    Example:
        ```python
        from skillbroker_langchain import SkillBrokerDynamicTool

        # This tool can handle any domain question
        expert_tool = SkillBrokerDynamicTool(
            name="expert_knowledge",
            description="Get expert knowledge on any topic"
        )
        ```
    """

    name: str = "skillbroker_expert"
    description: str = (
        "Get expert knowledge from the SkillBroker marketplace. "
        "Automatically finds and queries the most relevant skill for your question."
    )
    args_schema: Type[BaseModel] = SkillQueryInput
    return_direct: bool = False

    client: Optional[SkillBrokerClient] = None
    api_url: Optional[str] = None
    api_key: Optional[str] = None

    def __init__(
        self,
        api_url: Optional[str] = None,
        api_key: Optional[str] = None,
        **kwargs
    ):
        super().__init__(
            api_url=api_url,
            api_key=api_key,
            **kwargs
        )
        self.client = SkillBrokerClient(api_url=api_url, api_key=api_key)

    def _run(
        self,
        query: str,
        run_manager: Optional[CallbackManagerForToolRun] = None,
    ) -> str:
        """Find and invoke the best skill for the query."""
        try:
            # First, try to get recommendations
            try:
                recommendations = self.client.get_recommendations(query, limit=1)
                if recommendations:
                    skill = recommendations[0]
                    response = self.client.invoke(skill.id, query)
                    return f"[From {skill.name}]: {response.response}"
            except SkillBrokerError:
                pass

            # Fall back to search
            results = self.client.search(query=query, limit=1)
            if not results.skills:
                return "No relevant expert skills found for this question."

            skill = results.skills[0]
            response = self.client.invoke(skill.id, query)
            return f"[From {skill.name}]: {response.response}"

        except SkillBrokerError as e:
            return f"Error: {str(e)}"

    async def _arun(
        self,
        query: str,
        run_manager: Optional[CallbackManagerForToolRun] = None,
    ) -> str:
        return self._run(query, run_manager)
