"""
Pydantic models for A2A (Agent-to-Agent) protocol support.

This module defines Agent Cards and related models following the A2A specification
for agent discovery and registration in the MCP Gateway Registry.

Based on: docs/design/a2a-protocol-integration.md
"""

import logging
import re
from datetime import datetime
from typing import Any
from uuid import UUID, uuid4

from pydantic import (
    BaseModel,
    ConfigDict,
    Field,
    field_validator,
    model_validator,
)

# Configure logging with basicConfig
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s,p%(process)s,{%(filename)s:%(lineno)d},%(levelname)s,%(message)s",
)

logger = logging.getLogger(__name__)

GROUP_NAME_PATTERN = re.compile(r"^[a-zA-Z0-9_\-\.]+$")


def _validate_path_format(
    path: str,
) -> str:
    """
    Validate agent path format.

    Args:
        path: Agent path to validate

    Returns:
        Validated path string

    Raises:
        ValueError: If path format is invalid
    """
    if not path.startswith("/"):
        raise ValueError("Path must start with '/'")

    if "//" in path:
        raise ValueError("Path cannot contain consecutive slashes")

    if path.endswith("/") and len(path) > 1:
        raise ValueError("Path cannot end with '/' unless it is the root path")

    return path


def _validate_protocol_version(
    version: str,
) -> str:
    """
    Validate A2A protocol version format.

    Args:
        version: Protocol version string

    Returns:
        Validated version string

    Raises:
        ValueError: If version format is invalid
    """
    if not version:
        raise ValueError("Protocol version cannot be empty")

    parts = version.split(".")
    if len(parts) < 2:
        raise ValueError("Protocol version must be in format 'X.Y' or 'X.Y.Z'")

    for part in parts:
        if not part.isdigit():
            raise ValueError("Protocol version parts must be numeric")

    return version


def _validate_skill_ids_unique(
    skills: list["Skill"],
) -> list["Skill"]:
    """
    Validate that skill IDs are unique within the agent.

    Args:
        skills: List of skill objects

    Returns:
        Validated skills list

    Raises:
        ValueError: If duplicate skill IDs are found
    """
    if not skills:
        return skills

    skill_ids = [skill.id for skill in skills]
    duplicates = [sid for sid in skill_ids if skill_ids.count(sid) > 1]

    if duplicates:
        unique_duplicates = list(set(duplicates))
        raise ValueError(f"Duplicate skill IDs found: {', '.join(unique_duplicates)}")

    return skills


def _validate_url_format(
    url: str,
) -> str:
    """
    Validate URL format and protocol.

    Allows both HTTP and HTTPS for flexibility in local/development environments,
    though HTTPS is required for production per A2A specification.

    Args:
        url: URL string to validate

    Returns:
        Validated URL string

    Raises:
        ValueError: If URL format is invalid or protocol is not HTTP/HTTPS
    """
    if not url:
        raise ValueError("URL cannot be empty")

    if not (url.startswith("http://") or url.startswith("https://")):
        raise ValueError("URL must use HTTP or HTTPS protocol")

    try:
        from urllib.parse import urlparse

        parsed = urlparse(url)
        if not parsed.netloc:
            raise ValueError("URL must include a valid hostname")
    except Exception as e:
        raise ValueError(f"Invalid URL format: {e}")

    return url


def _validate_security_references(
    security: list[dict[str, list[str]]] | None,
    security_schemes: dict[str, "SecurityScheme | dict[str, Any]"],
) -> list[dict[str, list[str]]] | None:
    """
    Validate that security references exist in security_schemes.

    Args:
        security: Security requirements array
        security_schemes: Available security schemes

    Returns:
        Validated security array

    Raises:
        ValueError: If referenced security scheme does not exist
    """
    if not security:
        return security

    for requirement in security:
        for scheme_name in requirement.keys():
            if scheme_name not in security_schemes:
                raise ValueError(f"Security requirement references undefined scheme: {scheme_name}")

    return security


class SecurityScheme(BaseModel):
    """
    Security scheme for agent authentication.

    Supports various authentication methods including OAuth2, bearer tokens,
    API keys, and OpenID Connect.

    Note: Uses snake_case internally but serializes to camelCase for A2A compliance.
    """

    type: str = Field(
        ...,
        description="Security type: apiKey, http, oauth2, openIdConnect",
    )
    scheme: str | None = Field(
        None,
        description="HTTP auth scheme: basic, bearer, digest",
    )
    in_: str | None = Field(
        None,
        alias="in",
        description="API key location: header, query, cookie",
    )
    name: str | None = Field(
        None,
        description="Name of header/query/cookie for API key",
    )
    bearer_format: str | None = Field(
        None,
        alias="bearerFormat",
        description="Bearer token format hint (e.g., JWT)",
    )
    flows: dict[str, Any] | None = Field(
        None,
        description="OAuth2 flows configuration",
    )
    openid_connect_url: str | None = Field(
        None,
        alias="openIdConnectUrl",
        description="OpenID Connect discovery URL",
    )

    model_config = ConfigDict(
        populate_by_name=True  # Allow both snake_case and camelCase on input
    )

    @field_validator("type")
    @classmethod
    def _validate_security_type(
        cls,
        v: str,
    ) -> str:
        """Validate security type is one of the supported types."""
        valid_types = ["apiKey", "http", "oauth2", "openIdConnect"]
        if v not in valid_types:
            raise ValueError(f"Security type must be one of: {', '.join(valid_types)}")
        return v

    @field_validator("in_")
    @classmethod
    def _validate_api_key_location(
        cls,
        v: str | None,
    ) -> str | None:
        """Validate API key location."""
        if v is not None:
            valid_locations = ["header", "query", "cookie"]
            if v not in valid_locations:
                raise ValueError(f"API key location must be one of: {', '.join(valid_locations)}")
        return v


class AgentProvider(BaseModel):
    """
    A2A Agent Provider information.

    Represents the service provider of an agent with organization name and website URL.
    Per A2A specification, if provider is present, both organization and url are required.
    """

    organization: str = Field(
        ...,
        description="Provider organization name",
    )
    url: str = Field(
        ...,
        description="Provider website or documentation URL",
    )

    model_config = ConfigDict(populate_by_name=True)


class Skill(BaseModel):
    """
    Agent skill definition per A2A protocol specification.

    A skill represents a discrete capability that an agent can perform.
    Skills describe high-level capabilities without operation-specific schemas.

    Note: Uses snake_case internally but serializes to camelCase for A2A compliance.
    """

    id: str = Field(
        ...,
        description="Unique skill identifier",
    )
    name: str = Field(
        ...,
        description="Human-readable skill name",
    )
    description: str = Field(
        ...,
        description="Detailed skill description",
    )
    tags: list[str] = Field(
        ...,
        description="Skill categorization tags - keywords describing capability",
    )
    examples: list[str] | None = Field(
        None,
        description="Usage scenarios and examples",
    )
    input_modes: list[str] | None = Field(
        None,
        alias="inputModes",
        description="Skill-specific input MIME types",
    )
    output_modes: list[str] | None = Field(
        None,
        alias="outputModes",
        description="Skill-specific output MIME types",
    )
    security: list[dict[str, list[str]]] | None = Field(
        None,
        description="Skill-level security requirements",
    )

    model_config = ConfigDict(
        populate_by_name=True  # Allow both snake_case and camelCase on input
    )

    @field_validator("id")
    @classmethod
    def _validate_skill_id(
        cls,
        v: str,
    ) -> str:
        """Validate skill ID format."""
        if not v:
            raise ValueError("Skill ID cannot be empty")
        if " " in v:
            raise ValueError("Skill ID cannot contain spaces")
        return v

    @field_validator("name")
    @classmethod
    def _validate_skill_name(
        cls,
        v: str,
    ) -> str:
        """Validate skill name."""
        if not v or not v.strip():
            raise ValueError("Skill name cannot be empty")
        return v.strip()


class AgentCard(BaseModel):
    """
    A2A Agent Card - machine-readable agent profile.

    This model represents a complete agent card following the A2A protocol
    specification (v0.3.0), with extensions for MCP Gateway Registry integration.

    The agent card includes:
    - Required A2A fields (protocolVersion, name, description, url, version, capabilities, etc.)
    - Optional A2A fields (provider, skills, security, etc.)
    - MCP Gateway Registry extensions (path, tags, visibility, trust_level)

    Note: Uses snake_case internally but serializes to camelCase for A2A compliance.
    """

    # Unique identifier
    id: UUID = Field(
        default_factory=uuid4,
        description="Unique identifier (UUID) for this agent",
    )

    # Required A2A fields
    protocol_version: str = Field(
        "1.0",
        alias="protocolVersion",
        description="A2A protocol version (e.g., '1.0')",
    )
    name: str = Field(
        ...,
        description="Agent name",
    )
    description: str = Field(
        ...,
        description="Agent description",
    )
    url: str = Field(
        ...,
        description="Agent endpoint URL (HTTP or HTTPS)",
    )
    version: str = Field(
        ...,
        description="Agent version",
    )
    capabilities: dict[str, Any] = Field(
        default_factory=dict,
        description="Feature declarations (e.g., {'streaming': true})",
    )
    default_input_modes: list[str] = Field(
        default_factory=lambda: ["text/plain"],
        alias="defaultInputModes",
        description="Supported input MIME types",
    )
    default_output_modes: list[str] = Field(
        default_factory=lambda: ["text/plain"],
        alias="defaultOutputModes",
        description="Supported output MIME types",
    )
    skills: list[Skill] = Field(
        default_factory=list,
        description="Agent capabilities (skills)",
    )

    # Optional A2A fields
    preferred_transport: str | None = Field(
        "JSONRPC",
        alias="preferredTransport",
        description="Preferred transport protocol: JSONRPC, GRPC, HTTP+JSON",
    )
    provider: AgentProvider | None = Field(
        None,
        description="Agent provider information per A2A spec",
    )
    icon_url: str | None = Field(
        None,
        alias="iconUrl",
        description="Agent icon URL",
    )
    documentation_url: str | None = Field(
        None,
        alias="documentationUrl",
        description="Documentation URL",
    )
    security_schemes: dict[str, SecurityScheme | dict[str, Any]] = Field(
        default_factory=dict,
        alias="securitySchemes",
        description="Supported authentication methods",
    )
    security: list[dict[str, list[str]]] | None = Field(
        None,
        description="Security requirements array",
    )
    supports_authenticated_extended_card: bool | None = Field(
        None,
        alias="supportsAuthenticatedExtendedCard",
        description="Supports extended card with auth",
    )
    metadata: dict[str, Any] = Field(
        default_factory=dict,
        description="Additional metadata",
    )

    # MCP Gateway Registry extensions (optional - not part of A2A spec)
    path: str | None = Field(
        None,
        description="Registry path (e.g., /agents/my-agent). Optional - auto-generated if not provided.",
    )
    tags: list[str] = Field(
        default_factory=list,
        description="Categorization tags",
    )
    is_enabled: bool = Field(
        False,
        alias="isEnabled",
        description="Whether agent is enabled in registry",
    )
    num_stars: float = Field(
        0.0,
        ge=0.0,
        le=5.0,
        alias="numStars",
        description="Average community rating (0.0-5.0)",
    )
    rating_details: list[dict[str, Any]] = Field(
        default_factory=list,
        alias="ratingDetails",
        description="Individual user ratings with username and rating value",
    )
    license: str = Field(
        "N/A",
        description="License information",
    )

    # Registry metadata
    registered_at: datetime | None = Field(
        None,
        alias="registeredAt",
        description="Registration timestamp",
    )
    updated_at: datetime | None = Field(
        None,
        alias="updatedAt",
        description="Last update timestamp",
    )
    registered_by: str | None = Field(
        None,
        alias="registeredBy",
        description="Username who registered agent",
    )

    # Access control
    visibility: str = Field(
        "public",
        description="public, group-restricted, or internal",
    )
    allowed_groups: list[str] = Field(
        default_factory=list,
        alias="allowedGroups",
        description="Groups with access when visibility is group-restricted",
    )

    # Federation sync metadata
    sync_metadata: dict[str, Any] | None = Field(
        default=None,
        alias="syncMetadata",
        description="Metadata for items synced from peer registries",
    )

    # Validation and trust
    signature: str | None = Field(
        None,
        description="JWS signature for card integrity",
    )
    trust_level: str = Field(
        "community",
        alias="trustLevel",
        description="unverified, community, verified, trusted",
    )

    # ANS Integration
    ans_metadata: dict[str, Any] | None = Field(
        default=None,
        alias="ansMetadata",
        description="ANS (Agent Name Service) verification metadata",
    )

    # Health check status (persisted to MongoDB)
    health_status: str = Field(
        default="unknown",
        alias="healthStatus",
        description="Last known health status: healthy, unhealthy, unknown",
    )
    last_health_check: datetime | None = Field(
        default=None,
        alias="lastHealthCheck",
        description="Timestamp of last health check",
    )

    # Lifecycle and federation metadata
    status: str = Field(
        default="active",
        description="Lifecycle status (default: active for existing assets)",
    )
    source_created_at: datetime | None = Field(
        default=None,
        description="Original creation timestamp from source system",
        alias="sourceCreatedAt",
    )
    source_updated_at: datetime | None = Field(
        default=None,
        description="Last update timestamp from source system",
        alias="sourceUpdatedAt",
    )
    external_tags: list[str] = Field(
        default_factory=list,
        description="Tags from external/source system",
        alias="externalTags",
    )
    supported_protocol: str | None = Field(
        default=None,
        alias="supportedProtocol",
        description="Agent protocol: 'a2a' for A2A protocol agents, 'other' for non-A2A agents",
    )

    model_config = ConfigDict(
        populate_by_name=True  # Allow both snake_case and camelCase on input
    )

    @field_validator("protocol_version")
    @classmethod
    def _validate_protocol_version_field(
        cls,
        v: str,
    ) -> str:
        """Validate protocol version format."""
        return _validate_protocol_version(v)

    @field_validator("url")
    @classmethod
    def _validate_url_field(
        cls,
        v: str,
    ) -> str:
        """Validate URL format and protocol."""
        return _validate_url_format(v)

    @field_validator("path")
    @classmethod
    def _validate_path_field(
        cls,
        v: str | None,
    ) -> str | None:
        """Validate path format if provided."""
        if v is None:
            return None
        return _validate_path_format(v)

    @field_validator("visibility")
    @classmethod
    def _validate_visibility_field(
        cls,
        v: str,
    ) -> str:
        """Validate and normalize visibility value.

        Accepts "internal" as alias for "private" and "group" as alias
        for "group-restricted" for backward compatibility.
        """
        from registry.utils.visibility import validate_visibility

        return validate_visibility(v)

    @field_validator("trust_level")
    @classmethod
    def _validate_trust_level_field(
        cls,
        v: str,
    ) -> str:
        """Validate trust level value."""
        valid_levels = ["unverified", "community", "verified", "trusted"]
        if v not in valid_levels:
            raise ValueError(f"Trust level must be one of: {', '.join(valid_levels)}")
        return v

    @field_validator("tags", mode="before")
    @classmethod
    def _convert_tags_field(
        cls,
        v: Any,
    ) -> list[str]:
        """Convert tags from string or list format to list of strings.

        Supports both:
        - String format: "tag1,tag2,tag3"
        - List format: ["tag1", "tag2", "tag3"]
        """
        if isinstance(v, str):
            return [tag.strip() for tag in v.split(",") if tag.strip()]
        if isinstance(v, list):
            return [str(tag).strip() for tag in v if str(tag).strip()]
        return []

    @field_validator("skills")
    @classmethod
    def _validate_skills_field(
        cls,
        v: list[Skill],
    ) -> list[Skill]:
        """Validate skills have unique IDs."""
        return _validate_skill_ids_unique(v)

    @model_validator(mode="after")
    def _validate_security_requirements(
        self,
    ) -> "AgentCard":
        """Validate security requirements reference existing schemes."""
        if self.security is not None:
            _validate_security_references(self.security, self.security_schemes)
        return self

    @model_validator(mode="after")
    def _validate_group_restricted_access(
        self,
    ) -> "AgentCard":
        """Validate group-restricted visibility has allowed groups."""
        if self.visibility == "group-restricted" and not self.allowed_groups:
            raise ValueError("Group-restricted visibility requires at least one allowed group")
        return self


class AgentInfo(BaseModel):
    """
    Simplified agent information for listing and search.

    This lightweight model is used for agent discovery results and listings,
    containing only the essential information needed for agent selection.

    Note: Uses snake_case internally but serializes to camelCase for A2A compliance.
    """

    name: str = Field(
        ...,
        description="Agent name",
    )
    description: str = Field(
        default="",
        description="Agent description",
    )
    path: str = Field(
        ...,
        description="Registry path",
    )
    url: str = Field(
        ...,
        description="Agent endpoint URL",
    )
    tags: list[str] = Field(
        default_factory=list,
        description="Categorization tags",
    )
    skills: list[str] = Field(
        default_factory=list,
        description="Skill names only",
    )
    num_skills: int = Field(
        0,
        ge=0,
        alias="numSkills",
        description="Number of skills",
    )
    num_stars: float = Field(
        0.0,
        ge=0.0,
        le=5.0,
        alias="numStars",
        description="Average community rating (0.0-5.0)",
    )
    is_enabled: bool = Field(
        False,
        alias="isEnabled",
        description="Whether agent is enabled",
    )
    provider: str | None = Field(
        None,
        description="Agent provider/author",
    )
    streaming: bool = Field(
        False,
        description="Supports streaming responses",
    )
    trust_level: str = Field(
        "community",
        alias="trustLevel",
        description="unverified, community, verified, trusted",
    )
    visibility: str = Field(
        "public",
        description="public, group-restricted, or internal",
    )
    allowed_groups: list[str] = Field(
        default_factory=list,
        alias="allowedGroups",
        description="Groups with access when visibility is group-restricted",
    )
    supported_protocol: str | None = Field(
        default=None,
        alias="supportedProtocol",
        description="Agent protocol: 'a2a' or 'other'",
    )
    sync_metadata: dict[str, Any] | None = Field(
        default=None,
        alias="syncMetadata",
        description="Federation sync metadata for items from peer registries",
    )
    ans_metadata: dict[str, Any] | None = Field(
        default=None,
        alias="ansMetadata",
        description="ANS verification metadata",
    )
    registered_by: str | None = Field(
        None,
        alias="registeredBy",
        description="Username who registered the agent",
    )
    status: str = Field(
        "active",
        description="Lifecycle status: active, deprecated, draft, beta",
    )
    provider_organization: str | None = Field(
        None,
        alias="providerOrganization",
        description="Provider organization name",
    )
    provider_url: str | None = Field(
        None,
        alias="providerUrl",
        description="Provider URL",
    )
    source_created_at: str | None = Field(
        None,
        alias="sourceCreatedAt",
        description="Original creation timestamp from source system (ISO format)",
    )
    source_updated_at: str | None = Field(
        None,
        alias="sourceUpdatedAt",
        description="Last update timestamp from source system (ISO format)",
    )
    registered_at: str | None = Field(
        None,
        alias="registeredAt",
        description="Registration timestamp in this registry (ISO format)",
    )
    updated_at: str | None = Field(
        None,
        alias="updatedAt",
        description="Last update timestamp in this registry (ISO format)",
    )
    health_status: str = Field(
        default="unknown",
        alias="healthStatus",
        description="Last known health status: healthy, unhealthy, unknown",
    )
    last_health_check: str | None = Field(
        default=None,
        alias="lastHealthCheck",
        description="Timestamp of last health check (ISO format)",
    )
    metadata: dict[str, Any] = Field(
        default_factory=dict,
        description="Additional metadata key-value pairs",
    )

    model_config = ConfigDict(
        populate_by_name=True  # Allow both snake_case and camelCase on input
    )


class AgentRegistrationRequest(BaseModel):
    """
    API request model for agent registration.

    This model is used for the agent registration API endpoint and converts
    form-style inputs (e.g., comma-separated tags) into the proper types.
    Accepts both snake_case (Python) and camelCase (A2A spec JSON) field names.
    """

    name: str = Field(
        ...,
        min_length=1,
        description="Agent name",
    )
    description: str = Field(
        default="",
        description="Agent description",
    )
    url: str = Field(
        ...,
        min_length=1,
        description="Agent endpoint URL",
    )
    path: str | None = Field(
        None,
        description="Registry path (optional - auto-generated if not provided)",
    )
    protocol_version: str = Field(
        default="1.0",
        alias="protocolVersion",
        description="A2A protocol version",
    )
    version: str | None = Field(
        None,
        description="Agent version",
    )
    provider: dict[str, str] | None = Field(
        None,
        description="Agent provider information {organization, url}",
    )
    security_schemes: dict[str, dict[str, Any]] | None = Field(
        None,
        alias="securitySchemes",
        description="Security schemes configuration",
    )
    skills: list[dict[str, Any]] | None = Field(
        None,
        description="Agent skills",
    )
    streaming: bool = Field(
        False,
        description="Supports streaming responses",
    )
    tags: str | list[str] = Field(
        default="",
        description="Comma-separated tags or list of tags",
    )
    license: str = Field(
        default="N/A",
        description="License information",
    )
    visibility: str = Field(
        default="public",
        description="Visibility: public, private, or group-restricted (default: public). 'internal' accepted as alias for 'private'.",
    )
    allowed_groups: list[str] = Field(
        default_factory=list,
        alias="allowedGroups",
        max_length=50,
        description="Groups with access when visibility is group-restricted (list or comma-separated string)",
    )
    trust_level: str = Field(
        default="community",
        alias="trustLevel",
        description="Trust level: unverified, community, verified, trusted (default: community)",
    )

    status: str = Field(
        default="draft",
        description="Lifecycle status (default: draft). Allowed: active, deprecated, draft, beta",
    )
    source_created_at: str | None = Field(
        None,
        description="Original creation timestamp from source system (ISO format)",
    )
    source_updated_at: str | None = Field(
        None,
        description="Last update timestamp from source system (ISO format)",
    )
    external_tags: str | list[str] | None = Field(
        None,
        description="Comma-separated tags or list of tags from external/source system",
    )
    default_input_modes: list[str] | None = Field(
        None,
        alias="defaultInputModes",
        description="Supported input MIME types (e.g., ['text', 'text/plain'])",
    )
    default_output_modes: list[str] | None = Field(
        None,
        alias="defaultOutputModes",
        description="Supported output MIME types (e.g., ['text', 'text/plain'])",
    )
    ans_agent_id: str | None = Field(
        default=None,
        description="Optional ANS Agent ID to link during registration",
    )
    supported_protocol: str = Field(
        ...,
        alias="supportedProtocol",
        description="Agent protocol: 'a2a' for A2A protocol agents, 'other' for non-A2A agents",
    )
    metadata: dict[str, Any] = Field(
        default_factory=dict,
        description="Additional metadata key-value pairs",
    )
    capabilities: dict[str, Any] = Field(
        default_factory=dict,
        description="Agent capabilities (e.g., streaming, push_notifications)",
    )

    model_config = ConfigDict(populate_by_name=True)

    @field_validator("tags", mode="before")
    @classmethod
    def _normalize_tags(
        cls,
        v: str | list[str] | None,
    ) -> str:
        """Normalize tags to comma-separated string."""
        if v is None:
            return ""
        if isinstance(v, list):
            return ",".join(v)
        return v

    @field_validator("external_tags", mode="before")
    @classmethod
    def _normalize_external_tags(
        cls,
        v: str | list[str] | None,
    ) -> str | None:
        """Normalize external_tags to comma-separated string."""
        if v is None:
            return None
        if isinstance(v, list):
            return ",".join(v)
        return v

    @field_validator("path")
    @classmethod
    def _validate_path_request(
        cls,
        v: str | None,
    ) -> str | None:
        """Validate path format if provided."""
        if v is None:
            return None
        return _validate_path_format(v)

    @field_validator("protocol_version")
    @classmethod
    def _validate_protocol_version_request(
        cls,
        v: str,
    ) -> str:
        """Validate protocol version format."""
        return _validate_protocol_version(v)

    @field_validator("supported_protocol")
    @classmethod
    def _validate_supported_protocol(
        cls,
        v: str,
    ) -> str:
        """Validate and normalize supported_protocol to lowercase."""
        v = v.lower()
        valid_values = ["a2a", "other"]
        if v not in valid_values:
            raise ValueError(f"supported_protocol must be one of: {', '.join(valid_values)}")
        return v

    @field_validator("visibility")
    @classmethod
    def _validate_visibility_request(
        cls,
        v: str,
    ) -> str:
        """Validate and normalize visibility value.

        Accepts "internal" as alias for "private" and "group" as alias
        for "group-restricted" for backward compatibility.
        """
        from registry.utils.visibility import validate_visibility

        return validate_visibility(v)

    @field_validator("trust_level")
    @classmethod
    def _validate_trust_level_request(
        cls,
        v: str,
    ) -> str:
        """Validate trust_level value."""
        valid_values = ["unverified", "community", "verified", "trusted"]
        if v not in valid_values:
            raise ValueError(f"trust_level must be one of: {', '.join(valid_values)}")
        return v

    @field_validator("allowed_groups", mode="before")
    @classmethod
    def _normalize_allowed_groups(
        cls,
        v: str | list[str] | None,
    ) -> list[str]:
        """Normalize allowed_groups from comma-separated string or list."""
        if v is None:
            return []
        if isinstance(v, str):
            return [g.strip() for g in v.split(",") if g.strip()]
        if isinstance(v, list):
            return [str(g).strip() for g in v if str(g).strip()]
        return []

    @field_validator("allowed_groups", mode="after")
    @classmethod
    def _validate_group_name_format(
        cls,
        v: list[str],
    ) -> list[str]:
        """Validate group name format (alphanumeric, hyphens, underscores, dots)."""
        for name in v:
            if not GROUP_NAME_PATTERN.match(name):
                raise ValueError(
                    f"Invalid group name '{name}'. "
                    "Group names may only contain letters, digits, hyphens, underscores, and dots."
                )
        return v

    @model_validator(mode="after")
    def _validate_group_restricted_groups(
        self,
    ) -> "AgentRegistrationRequest":
        """Validate group-restricted visibility has allowed groups."""
        if self.visibility == "group-restricted" and not self.allowed_groups:
            raise ValueError(
                "group-restricted visibility requires at least one allowed_group"
            )
        return self
