"""
API Request/Response Models
===========================
Pydantic models with comprehensive input validation and Field validators.
"""

from typing import Optional, Dict, Any, List
from pydantic import BaseModel, Field, field_validator, model_validator
import re


class StoreRequest(BaseModel):
    """Request model for storing a memory."""
    content: str = Field(
        ...,
        max_length=100_000,
        description="The content to store as a memory",
        examples=["This is a sample memory content"]
    )
    metadata: Optional[Dict[str, Any]] = Field(
        default=None,
        description="Optional metadata associated with the memory"
    )
    agent_id: Optional[str] = Field(
        default=None,
        max_length=256,
        description="Optional agent identifier"
    )
    ttl: Optional[int] = Field(
        default=None,
        ge=1,
        le=86400 * 365,  # Max 1 year TTL
        description="Time-to-live in seconds (1 to 31536000)"
    )

    @field_validator('content')
    @classmethod
    def validate_content(cls, v: str) -> str:
        """Ensure content is not empty or whitespace only."""
        if not v or not v.strip():
            raise ValueError('Content cannot be empty or whitespace only')
        return v

    @field_validator('metadata')
    @classmethod
    def check_metadata_size(cls, v: Optional[Dict[str, Any]]) -> Optional[Dict[str, Any]]:
        """Validate metadata constraints."""
        if v is None:
            return v
        if len(v) > 50:
            raise ValueError('Too many metadata keys (max 50)')
        for key, value in v.items():
            if len(key) > 64:
                raise ValueError(f'Metadata key "{key[:20]}..." too long (max 64 chars)')
            if not re.match(r'^[a-zA-Z0-9_\-\.]+$', key):
                raise ValueError(f'Metadata key "{key}" contains invalid characters (only alphanumeric, underscore, hyphen, dot allowed)')
            # Metadata values can be Any, but limit strings
            if isinstance(value, str) and len(value) > 1000:
                raise ValueError(f'Metadata value for "{key}" too long (max 1000 chars)')
            # Limit nested structures
            if isinstance(value, (dict, list)):
                raise ValueError(f'Metadata value for "{key}" must be a primitive type (str, int, float, bool, null)')
        return v

    @field_validator('agent_id')
    @classmethod
    def validate_agent_id(cls, v: Optional[str]) -> Optional[str]:
        """Validate agent_id format."""
        if v is None:
            return v
        if not re.match(r'^[a-zA-Z0-9_\-\:]+$', v):
            raise ValueError('Agent ID contains invalid characters')
        return v


class QueryRequest(BaseModel):
    """Request model for querying memories."""
    query: str = Field(
        ...,
        max_length=10000,
        description="The search query string",
        examples=["sample search query"]
    )
    top_k: int = Field(
        default=5,
        ge=1,
        le=100,
        description="Maximum number of results to return (1-100)"
    )
    agent_id: Optional[str] = Field(
        default=None,
        max_length=256,
        description="Optional agent identifier to filter by"
    )

    @field_validator('query')
    @classmethod
    def validate_query(cls, v: str) -> str:
        """Ensure query is not empty or whitespace only."""
        if not v or not v.strip():
            raise ValueError('Query cannot be empty or whitespace only')
        return v


class ConceptRequest(BaseModel):
    """Request model for defining a concept."""
    name: str = Field(
        ...,
        max_length=256,
        description="Name of the concept",
        examples=["animal"]
    )
    attributes: Dict[str, str] = Field(
        ...,
        description="Key-value attributes for the concept"
    )

    @field_validator('name')
    @classmethod
    def validate_name(cls, v: str) -> str:
        """Validate concept name."""
        if not v or not v.strip():
            raise ValueError('Concept name cannot be empty')
        if not re.match(r'^[a-zA-Z0-9_\-\s]+$', v):
            raise ValueError('Concept name contains invalid characters')
        return v.strip()

    @field_validator('attributes')
    @classmethod
    def check_attributes_size(cls, v: Dict[str, str]) -> Dict[str, str]:
        """Validate attributes constraints."""
        if len(v) == 0:
            raise ValueError('At least one attribute is required')
        if len(v) > 50:
            raise ValueError('Too many attributes (max 50)')
        for key, value in v.items():
            if len(key) > 64:
                raise ValueError(f'Attribute key "{key[:20]}..." too long (max 64 chars)')
            if not re.match(r'^[a-zA-Z0-9_\-\.]+$', key):
                raise ValueError(f'Attribute key "{key}" contains invalid characters')
            if len(value) > 1000:
                raise ValueError(f'Attribute value for "{key}" too long (max 1000 chars)')
        return v


class AnalogyRequest(BaseModel):
    """Request model for solving analogies."""
    source_concept: str = Field(
        ...,
        max_length=256,
        description="The source concept in the analogy"
    )
    source_value: str = Field(
        ...,
        max_length=1000,
        description="The value associated with the source concept"
    )
    target_concept: str = Field(
        ...,
        max_length=256,
        description="The target concept in the analogy"
    )

    @field_validator('source_concept', 'target_concept')
    @classmethod
    def validate_concept(cls, v: str) -> str:
        """Validate concept names."""
        if not v or not v.strip():
            raise ValueError('Concept cannot be empty')
        return v.strip()

    @field_validator('source_value')
    @classmethod
    def validate_value(cls, v: str) -> str:
        """Validate source value."""
        if not v or not v.strip():
            raise ValueError('Source value cannot be empty')
        return v.strip()


class MemoryResponse(BaseModel):
    """Response model for memory retrieval."""
    id: str
    content: str
    metadata: Dict[str, Any]
    created_at: str
    epistemic_value: float = 0.0
    ltp_strength: float = 0.0
    tier: str = "unknown"


class QueryResult(BaseModel):
    """Single result from a query."""
    id: str
    content: str
    score: float
    metadata: Dict[str, Any]
    tier: str


class QueryResponse(BaseModel):
    """Response model for query results."""
    ok: bool = True
    query: str
    results: List[QueryResult]


class StoreResponse(BaseModel):
    """Response model for store operation."""
    ok: bool = True
    memory_id: str
    message: str


class DeleteResponse(BaseModel):
    """Response model for delete operation."""
    ok: bool = True
    deleted: str


class ConceptResponse(BaseModel):
    """Response model for concept definition."""
    ok: bool = True
    concept: str


class AnalogyResult(BaseModel):
    """Single result from an analogy query."""
    value: str
    score: float


class AnalogyResponse(BaseModel):
    """Response model for analogy query."""
    ok: bool = True
    analogy: str
    results: List[AnalogyResult]


class ErrorResponse(BaseModel):
    """Error response model."""
    detail: str
    error_type: Optional[str] = None


class HealthResponse(BaseModel):
    """Health check response model."""
    status: str
    redis_connected: bool
    storage_circuit_breaker: str
    qdrant_circuit_breaker: str
    engine_ready: bool
    timestamp: str


class RootResponse(BaseModel):
    """Root endpoint response model."""
    status: str
    service: str
    version: str
    phase: str
    timestamp: str
