"""Type definitions for novyx-agent."""

from __future__ import annotations

from dataclasses import dataclass, field
from enum import Enum
from typing import Any, Callable, Dict, List, Optional, Sequence

from pydantic import BaseModel, ConfigDict, Field


# ---------------------------------------------------------------------------
# Tool definitions
# ---------------------------------------------------------------------------

class ToolParam(BaseModel):
    """Schema for a single tool parameter."""
    name: str
    type: str
    description: str
    required: bool = True
    default: Any = None
    enum: Optional[List[str]] = None


class ToolDef(BaseModel):
    """Complete tool definition — built automatically from decorated functions."""
    name: str
    description: str
    parameters: List[ToolParam] = Field(default_factory=list)
    fn: Any = Field(exclude=True, default=None)

    model_config = ConfigDict(arbitrary_types_allowed=True)

    def to_openai_schema(self) -> Dict[str, Any]:
        """Convert to OpenAI function-calling schema."""
        properties: Dict[str, Any] = {}
        required: List[str] = []
        for p in self.parameters:
            prop: Dict[str, Any] = {"type": p.type, "description": p.description}
            if p.enum:
                prop["enum"] = p.enum
            properties[p.name] = prop
            if p.required:
                required.append(p.name)
        return {
            "type": "function",
            "function": {
                "name": self.name,
                "description": self.description,
                "parameters": {
                    "type": "object",
                    "properties": properties,
                    "required": required,
                },
            },
        }

    def to_anthropic_schema(self) -> Dict[str, Any]:
        """Convert to Anthropic tool-use schema."""
        properties: Dict[str, Any] = {}
        required: List[str] = []
        for p in self.parameters:
            prop: Dict[str, Any] = {"type": p.type, "description": p.description}
            if p.enum:
                prop["enum"] = p.enum
            properties[p.name] = prop
            if p.required:
                required.append(p.name)
        return {
            "name": self.name,
            "description": self.description,
            "input_schema": {
                "type": "object",
                "properties": properties,
                "required": required,
            },
        }


# ---------------------------------------------------------------------------
# Message types
# ---------------------------------------------------------------------------

class Role(str, Enum):
    SYSTEM = "system"
    USER = "user"
    ASSISTANT = "assistant"
    TOOL = "tool"


class Message(BaseModel):
    """A single message in a conversation."""
    role: Role
    content: str
    name: Optional[str] = None
    tool_call_id: Optional[str] = None
    tool_calls: Optional[List[Dict[str, Any]]] = None


# ---------------------------------------------------------------------------
# Run result
# ---------------------------------------------------------------------------

class ToolCall(BaseModel):
    """A tool invocation recorded during a run."""
    tool: str
    args: Dict[str, Any] = Field(default_factory=dict)
    result: Any = None


class RunResult(BaseModel):
    """Result of Agent.run() — the final answer plus metadata."""
    output: str
    tool_calls: List[ToolCall] = Field(default_factory=list)
    memories_recalled: int = 0
    memories_stored: int = 0
    model: str = ""
    usage: Dict[str, int] = Field(default_factory=dict)

    def __str__(self) -> str:
        return self.output


# ---------------------------------------------------------------------------
# Agent config
# ---------------------------------------------------------------------------

class MemoryConfig(BaseModel):
    """Controls how the agent uses Novyx memory."""
    auto_recall: bool = True
    auto_remember: bool = True
    recall_limit: int = 5
    recall_threshold: float = 0.3
    recall_tags: Optional[List[str]] = None
    remember_tags: Optional[List[str]] = None
    remember_importance: int = 5


class AgentConfig(BaseModel):
    """Full agent configuration."""
    name: str = "Agent"
    instructions: str = "You are a helpful assistant."
    model: str = "gpt-4o-mini"
    provider: str = "openai"
    temperature: float = 0.7
    max_tokens: int = 4096
    max_tool_rounds: int = 10
    memory: MemoryConfig = Field(default_factory=MemoryConfig)
