"""GraphMem Agent Skill — reusable plugin for AI agent message cycles.

Provides two hooks:
- ``beforeResponse(query)`` — auto-retrieves context before generating a response.
- ``afterMessage(message)`` — auto-extracts and stores knowledge from important messages.

Example::

    from graphmem import GraphMem
    from graphmem.agent import create_agent_skill

    mem = GraphMem(api_key="gm_your_key_here")
    skill = create_agent_skill(mem, auto_remember=True, auto_context=True)

    # In your agent loop:
    context = skill.before_response("What does Alice do?")
    # ... generate response using context ...
    skill.after_message("Alice joined Acme Corp as a senior engineer in 2024.")
"""

from __future__ import annotations

import re
from dataclasses import dataclass, field
from typing import TYPE_CHECKING, Any, Callable, Optional

if TYPE_CHECKING:
    from graphmem.client import GraphMem
    from graphmem.types import ContextResult, RememberResult


@dataclass
class AgentSkillConfig:
    """Configuration for the agent skill plugin."""

    auto_context: bool = True
    auto_remember: bool = True
    context_format: str = "markdown"
    context_mode: str = "hybrid"
    min_message_length: int = 30
    filter_patterns: list[re.Pattern[str]] = field(default_factory=list)
    on_remember: Optional[Callable[["RememberResult"], None]] = None
    on_context: Optional[Callable[["ContextResult"], None]] = None
    on_error: Optional[Callable[[Exception], None]] = None


class AgentSkill:
    """Agent skill plugin that hooks into AI agent message cycles."""

    def __init__(self, client: "GraphMem", config: AgentSkillConfig) -> None:
        self._client = client
        self._config = config

    def _should_remember(self, message: str) -> bool:
        if not self._config.auto_remember:
            return False
        if len(message) < self._config.min_message_length:
            return False
        for pattern in self._config.filter_patterns:
            if pattern.search(message):
                return False
        return True

    def before_response(self, query: str) -> Optional["ContextResult"]:
        """Retrieve context from the knowledge graph before generating a response."""
        if not self._config.auto_context:
            return None
        try:
            from graphmem.types import ContextOptions

            result = self._client.get_context(
                query,
                ContextOptions(
                    format=self._config.context_format,  # type: ignore[arg-type]
                    mode=self._config.context_mode,  # type: ignore[arg-type]
                ),
            )
            if self._config.on_context:
                self._config.on_context(result)
            return result
        except Exception as exc:
            if self._config.on_error:
                self._config.on_error(exc)
            return None

    def after_message(self, message: str) -> Optional["RememberResult"]:
        """Extract knowledge from a message and store in the graph."""
        if not self._should_remember(message):
            return None
        try:
            result = self._client.remember(message)
            if self._config.on_remember:
                self._config.on_remember(result)
            return result
        except Exception as exc:
            if self._config.on_error:
                self._config.on_error(exc)
            return None

    def ingest_tool_output(
        self, tool_name: str, output: str
    ) -> Optional["RememberResult"]:
        """Ingest tool output by remembering it with a tool attribution prefix."""
        if not self._config.auto_remember:
            return None
        try:
            result = self._client.remember(f"[Tool: {tool_name}]\n{output}")
            if self._config.on_remember:
                self._config.on_remember(result)
            return result
        except Exception as exc:
            if self._config.on_error:
                self._config.on_error(exc)
            return None


def create_agent_skill(
    client: "GraphMem",
    *,
    auto_context: bool = True,
    auto_remember: bool = True,
    context_format: str = "markdown",
    context_mode: str = "hybrid",
    min_message_length: int = 30,
    filter_patterns: Optional[list[re.Pattern[str]]] = None,
    on_remember: Optional[Callable[["RememberResult"], None]] = None,
    on_context: Optional[Callable[["ContextResult"], None]] = None,
    on_error: Optional[Callable[[Exception], None]] = None,
) -> AgentSkill:
    """Create an agent skill plugin that hooks into AI agent message cycles.

    Args:
        client: GraphMem client instance.
        auto_context: Retrieve context before responses (default: True).
        auto_remember: Remember important messages (default: True).
        context_format: Format for context retrieval (default: "markdown").
        context_mode: Mode for context retrieval (default: "hybrid").
        min_message_length: Skip messages shorter than this (default: 30).
        filter_patterns: Regex patterns to skip (e.g., greetings).
        on_remember: Callback after successful remember.
        on_context: Callback after successful context retrieval.
        on_error: Error callback (default: silent).

    Returns:
        AgentSkill instance with before_response/after_message hooks.
    """
    config = AgentSkillConfig(
        auto_context=auto_context,
        auto_remember=auto_remember,
        context_format=context_format,
        context_mode=context_mode,
        min_message_length=min_message_length,
        filter_patterns=filter_patterns or [],
        on_remember=on_remember,
        on_context=on_context,
        on_error=on_error,
    )
    return AgentSkill(client, config)
