# -*- coding: utf-8 -*-
"""The tool group class."""
from typing import Literal, Sequence

from ..mcp import MCPClient
from ._base import ToolBase
from ..skill import SkillLoaderBase, Skill, LocalSkillLoader


class ToolGroup:
    """A group of related tools, mcps, and skills that an agent can activate,
    deactivate and use together. The tool group is activated by the meta tool
    `ResetTools`.

    In high-code scenarios, the tools argument accepts any child classes for
    ToolBase class, and the tool groups supports serialization.
    """

    name: Literal["basic"] | str
    """Note the "basic" group is special and represents the default tool group
    that will be always be activated for the agent."""

    description: str
    """A description of the tool group from an agent-oriented perspective,
    outlining its capabilities and the conditions under which it should be
    activated."""

    instructions: str | None
    """Instructions included in the meta tool's result upon activation of
    this tool group, guiding the agent on how to properly use the meta tool."""

    tools: list[ToolBase]
    """The tools in this group."""

    skills_or_loaders: list[Skill | SkillLoaderBase]
    """The skills in this group."""

    mcps: list[MCPClient]
    """The mcps in this group."""

    def __init__(
        self,
        name: Literal["basic"] | str,
        description: str | None = None,
        instructions: str | None = None,
        tools: list[ToolBase] | None = None,
        skills_or_loaders: Sequence[str | Skill | SkillLoaderBase]
        | None = None,
        mcps: list[MCPClient] | None = None,
    ) -> None:
        """Initialize the tool group.

        Args:
            name (`Literal["basic"] | str`):
                The name of the tool group.
            description (`str | None`):
                The description of the tool group.
            instructions (`str | None`, optional):
                Instructions included in the meta tool's result upon
                activation of this tool group, guiding the agent on how to
                properly use the meta tool.
            tools (`list[ToolBase] | None`, optional):
                The tools in this group.
            skills_or_loaders (`list[str | Skill | SkillLoaderBase] | None`, \
            optional):
                The skill paths, data, and loaders to access skills in this
                group.
            mcps (`list[MCPClient] | None`, optional):
                The mcps in this group.
        """
        if name != "basic" and description is None:
            raise ValueError(
                f"The tool group description is required for tool group "
                f"'{name}' (Only the 'basic' tool group can have an optional "
                f"description).",
            )

        self.name = name
        self.description = description or ""
        self.instructions = instructions
        self.tools = tools or []
        self.mcps = mcps or []

        # Skill
        self.skills_or_loaders = []
        for _ in skills_or_loaders or []:
            if isinstance(_, str):
                self.skills_or_loaders.append(LocalSkillLoader(directory=_))

            elif isinstance(_, (Skill, SkillLoaderBase)):
                self.skills_or_loaders.append(_)

            else:
                raise TypeError(
                    f"Invalid skill or loader: {_}. Must be a skill, "
                    f"skill loader, or directory path.",
                )

    async def list_skills(self) -> list[Skill]:
        """List all the skills in this tool group."""
        skills = []
        for skill_or_loader in self.skills_or_loaders:
            if isinstance(skill_or_loader, Skill):
                skills.append(skill_or_loader)
            elif isinstance(skill_or_loader, SkillLoaderBase):
                skills.extend(await skill_or_loader.list_skills())

        return skills
