"""Pydantic models for the server registry."""

from __future__ import annotations

import uuid
from datetime import datetime
from enum import StrEnum
from typing import Any

from pydantic import BaseModel, Field


class TransportType(StrEnum):
    """MCP transport types."""

    STDIO = "stdio"
    HTTP = "http"
    SSE = "sse"


class ServerCategory(StrEnum):
    """Predefined server categories."""

    BROWSER_AUTOMATION = "browser-automation"
    DATA_ANALYTICS = "data-analytics"
    DEVELOPMENT = "development"
    AI_ML = "ai-ml"
    COMMUNICATION = "communication"
    FILE_SYSTEMS = "file-systems"
    SEARCH_WEB = "search-web"
    UTILITIES = "utilities"
    CUSTOM = "custom"


class ConfigOption(BaseModel):
    """A configurable option for a server."""

    name: str = Field(description="Option name")
    type: str = Field(description="Type: string, int, float, boolean, enum, object")
    description: str = Field(default="", description="Human-readable description")
    default: Any = Field(default=None, description="Default value")
    required: bool = Field(default=False, description="Whether this option is required")
    enum_values: list[str] | None = Field(default=None, description="Possible values for enum type")


class ToolFilter(BaseModel):
    """User-defined tool filtering for a server."""

    enabled_tools: list[str] | None = Field(default=None, description="Whitelist: only these tools are exposed")
    disabled_tools: list[str] | None = Field(default=None, description="Blacklist: these tools are hidden")


class ServerEntry(BaseModel):
    """A registered MCP server in the catalog."""

    id: str = Field(description="Unique server identifier (e.g., 'playwright-mcp')")
    name: str = Field(description="Human-readable server name")
    description: str = Field(default="", description="Server description")
    category: ServerCategory = Field(default=ServerCategory.CUSTOM, description="Server category")
    version: str = Field(default="0.0.0", description="Server version")
    tags: list[str] = Field(default_factory=list, description="Search tags")

    # Launch configuration
    command: str = Field(description="Command to start the server (e.g., 'npx @playwright/mcp')")
    args: list[str] = Field(default_factory=list, description="Additional command arguments")
    transport: TransportType = Field(default=TransportType.STDIO, description="Transport type")
    port: int | None = Field(default=None, description="Port for HTTP/SSE transport")
    endpoint: str | None = Field(default=None, description="HTTP endpoint path")
    cwd: str | None = Field(default=None, description="Working directory for the server process")

    # Environment
    env: dict[str, str] = Field(default_factory=dict, description="Environment variables to set")
    required_env: list[str] = Field(default_factory=list, description="Required env vars (must be set externally)")

    # Configuration
    configurable_options: list[ConfigOption] = Field(default_factory=list, description="User-configurable options")

    # Limits
    max_instances: int = Field(default=5, description="Maximum concurrent instances of this server")
    auto_shutdown_idle_seconds: int | None = Field(default=None, description="Override global idle timeout")

    # Tool filtering (user-defined)
    tool_filter: ToolFilter = Field(default_factory=ToolFilter, description="Tool whitelist/blacklist")

    # Metadata
    source: str = Field(default="catalog", description="Where this entry came from: catalog, import, user")
    enabled: bool = Field(default=True, description="Whether this server is available for use")
    created_at: datetime = Field(default_factory=datetime.now)
    updated_at: datetime = Field(default_factory=datetime.now)


class ToolInfo(BaseModel):
    """Information about a discovered tool from an MCP server."""

    name: str = Field(description="Tool name as reported by the server")
    description: str = Field(default="", description="Tool description")
    input_schema: dict[str, Any] = Field(default_factory=dict, description="JSON Schema for tool input")
    server_id: str = Field(description="The server this tool belongs to")


class InstanceState(StrEnum):
    """Lifecycle states for a server instance."""

    CONFIGURED = "configured"
    STARTING = "starting"
    ACTIVE = "active"
    IDLE = "idle"
    ERROR = "error"
    SHUTTING_DOWN = "shutting_down"
    STOPPED = "stopped"


class ServerInstance(BaseModel):
    """A running instance of an MCP server."""

    instance_id: str = Field(default_factory=lambda: str(uuid.uuid4())[:12])
    server_id: str = Field(description="Which server entry this is an instance of")
    agent_session_id: str = Field(description="Which agent session owns this instance")
    state: InstanceState = Field(default=InstanceState.CONFIGURED)
    config: dict[str, Any] = Field(default_factory=dict, description="Instance-specific configuration")

    # Runtime info
    pid: int | None = Field(default=None, description="Process ID if running")
    port: int | None = Field(default=None, description="Actual port if HTTP transport")
    tools: list[ToolInfo] = Field(default_factory=list, description="Discovered tools")

    # Timestamps
    created_at: datetime = Field(default_factory=datetime.now)
    started_at: datetime | None = Field(default=None)
    last_activity_at: datetime | None = Field(default=None)

    # Metrics
    total_tool_calls: int = Field(default=0)
    total_errors: int = Field(default=0)


class ToolCallResult(BaseModel):
    """Result from a tool call proxied through the bridge."""

    instance_id: str
    tool_name: str
    success: bool
    result: Any = None
    error: str | None = None
    latency_ms: float = 0.0
    timestamp: datetime = Field(default_factory=datetime.now)


class ParallelCallRequest(BaseModel):
    """A request for parallel tool execution across instances."""

    calls: list[SingleCallRequest]


class SingleCallRequest(BaseModel):
    """A single tool call request."""

    instance_id: str
    tool_name: str
    arguments: dict[str, Any] = Field(default_factory=dict)
