from __future__ import annotations

from dataclasses import dataclass
from enum import Enum
from typing import Any, Callable, Iterable, TypeVar, cast

F = TypeVar("F", bound=Callable[..., Any])


class Status(str, Enum):
    PENDING = "pending"
    EXECUTING = "executing"
    COMPLETED = "completed"
    FAILED = "failed"


@dataclass(frozen=True, slots=True)
class ToolMetadata:
    name: str
    description: str
    category: str
    verification_level: str
    safe_for_retrieval: bool


def get_tool_metadata(func: Any) -> ToolMetadata | None:
    meta = getattr(func, "__armorer_tool_meta__", None)
    return meta if isinstance(meta, ToolMetadata) else None


def attach_tool_metadata(
    tool_obj: Any,
    *,
    name: str | None = None,
    description: str | None = None,
    category: str = "general",
    verification_level: str = "unknown",
    safe_for_retrieval: bool = False,
) -> Any:
    setattr(
        tool_obj,
        "__armorer_tool_meta__",
        ToolMetadata(
            name=str(name or getattr(tool_obj, "name", getattr(tool_obj, "__name__", "")) or "").strip(),
            description=str(description or getattr(tool_obj, "description", getattr(tool_obj, "__doc__", "")) or "").strip(),
            category=str(category or "general").strip() or "general",
            verification_level=str(verification_level or "unknown").strip() or "unknown",
            safe_for_retrieval=bool(safe_for_retrieval),
        ),
    )
    return tool_obj


def tool_name(func: Any) -> str:
    meta = get_tool_metadata(func)
    if meta and meta.name:
        return meta.name
    return str(getattr(func, "__name__", "") or "").strip()


def filter_tools_by_category(tools: Iterable[Any], *categories: str) -> list[Any]:
    requested = {str(category or "").strip() for category in categories if str(category or "").strip()}
    if not requested:
        return list(tools)
    return [
        tool_obj
        for tool_obj in tools
        if (meta := get_tool_metadata(tool_obj)) is not None and meta.category in requested
    ]


def group_tools_by_category(tools: Iterable[Any]) -> dict[str, list[Any]]:
    grouped: dict[str, list[Any]] = {}
    for tool_obj in tools:
        meta = get_tool_metadata(tool_obj)
        category = meta.category if meta else "uncategorized"
        grouped.setdefault(category, []).append(tool_obj)
    return grouped


def tool(
    func: F | None = None,
    *,
    name: str | None = None,
    description: str | None = None,
    category: str = "general",
    verification_level: str = "unknown",
    safe_for_retrieval: bool = False,
) -> F | Callable[[F], F]:
    """
    Lightweight tool decorator used by Armorer.

    We keep tools as regular Python callables and convert them to OpenAI Agents
    function tools at runtime inside BaseAgent.
    """

    def _decorate(f: F) -> F:
        setattr(f, "__armorer_tool__", True)
        setattr(
            f,
            "__armorer_tool_meta__",
            ToolMetadata(
                name=str(name or getattr(f, "__name__", "") or "").strip(),
                description=str(description or getattr(f, "__doc__", "") or "").strip(),
                category=str(category or "general").strip() or "general",
                verification_level=str(verification_level or "unknown").strip() or "unknown",
                safe_for_retrieval=bool(safe_for_retrieval),
            ),
        )
        return f

    if func is None:
        return _decorate
    return cast(F, _decorate(func))
