"""
Agent definition models for YAML validation.

This module provides Pydantic models for defining agents in YAML files with
strict validation. All required fields must be non-empty after whitespace
stripping, and unknown fields are rejected to catch configuration errors early.

Requirements: AGENT-01 (agent definition), AGENT-03 (tool whitelisting)
"""

from __future__ import annotations

from enum import Enum
from typing import Any, Dict, List, Optional

from pydantic import BaseModel, Field, field_validator


class LLMProvider(str, Enum):
    """Supported LLM provider types (INT-06, INT-07).

    RLM-Toolkit supports 75+ providers through LiteLLM integration.
    These are the most commonly used providers.
    """

    OLLAMA = "ollama"
    OPENAI = "openai"
    ANTHROPIC = "anthropic"
    GOOGLE = "google"
    CUSTOM = "custom"


class ToolSpec(BaseModel):
    """Tool specification with optional config (AGENT-03).

    Tools are specified by name with an optional configuration dictionary
    that can override default tool behavior for this specific agent.

    Example:
        ```yaml
        tools:
          - name: read_file
          - name: shell
            config:
              timeout: 60
        ```
    """

    model_config = {"extra": "forbid"}

    name: str = Field(..., min_length=1, description="Tool name")
    config: Dict[str, Any] = Field(
        default_factory=dict, description="Tool-specific configuration"
    )


class AgentDefinition(BaseModel):
    """YAML agent definition with required fields (AGENT-01).

    This is the primary model for defining agents. All required fields
    (name, role, goal, backstory) must be non-empty after stripping
    whitespace. Unknown fields are rejected to catch typos early.

    Example:
        ```yaml
        name: Code Reviewer
        role: Senior software engineer specializing in code quality
        goal: Review code for quality, security, and best practices
        backstory: |
          You are a senior software engineer with 15 years of experience.
          You specialize in code quality, security, and best practices.

        tools:
          - name: read_file
          - name: shell
            config:
              timeout: 60

        llm_provider: ollama
        llm_model: llama3.2
        timeout: 300
        ```
    """

    model_config = {"extra": "forbid"}  # Fail on unknown fields

    # Required fields - all must be non-empty
    name: str = Field(..., min_length=1, description="Agent name")
    role: str = Field(..., min_length=1, description="Agent role")
    goal: str = Field(..., min_length=1, description="Agent goal")
    backstory: str = Field(..., min_length=1, description="Agent backstory")

    # Tool configuration (AGENT-03)
    tools: List[ToolSpec] = Field(
        default_factory=list, description="Available tools for this agent"
    )

    # LLM configuration (INT-06, INT-07)
    llm_provider: LLMProvider = Field(
        default=LLMProvider.OLLAMA, description="LLM provider type"
    )
    llm_model: str = Field(default="llama3.2", description="Model name")
    llm_api_key: Optional[str] = Field(
        default=None, description="API key (or use environment variable)"
    )
    llm_base_url: Optional[str] = Field(
        default=None, description="Custom base URL for the LLM API"
    )

    # Execution settings
    timeout: int = Field(
        default=120, ge=1, le=3600, description="Default timeout in seconds"
    )
    max_retries: int = Field(
        default=3, ge=0, le=10, description="Maximum retry attempts"
    )
    fallback_agents: List[str] = Field(
        default_factory=list, description="Fallback agent names on failure"
    )

    @field_validator("name", "role", "goal", "backstory")
    @classmethod
    def validate_required_fields(cls, v: str, info) -> str:
        """Ensure required fields are non-empty after stripping whitespace.

        Args:
            v: The field value to validate
            info: Field validation info

        Returns:
            The stripped value if valid

        Raises:
            ValueError: If the field is empty or contains only whitespace
        """
        if not v or not v.strip():
            raise ValueError(f"{info.field_name} is required and cannot be empty")
        return v.strip()

    def get_system_prompt(self) -> str:
        """Generate system prompt from definition.

        Creates a formatted system prompt that includes the agent's
        name, role, goal, backstory, and available tools.

        Returns:
            A formatted system prompt string
        """
        tools_list = ", ".join(t.name for t in self.tools) or "None"

        return f"""You are {self.name}.

Role: {self.role}
Goal: {self.goal}

Background:
{self.backstory}

Available tools: {tools_list}
"""

    def get_agent_id(self) -> str:
        """Generate a URL-safe agent ID from the name.

        Returns:
            A lowercase, hyphenated agent ID
        """
        return self.name.lower().replace(" ", "-").replace("_", "-")
