"""
Request models for PowerMem API
"""

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


class MemoryCreateRequest(BaseModel):
    """Request model for creating a memory"""
    
    content: str = Field(..., description="Memory content (string, dict, or list of dicts)")
    user_id: Optional[str] = Field(None, description="User identifier")
    agent_id: Optional[str] = Field(None, description="Agent identifier")
    run_id: Optional[str] = Field(None, description="Run/conversation identifier")
    metadata: Optional[Dict[str, Any]] = Field(None, description="Additional metadata")
    filters: Optional[Dict[str, Any]] = Field(None, description="Filter metadata for advanced filtering")
    scope: Optional[str] = Field(None, description="Memory scope (e.g., 'user', 'agent', 'session')")
    memory_type: Optional[str] = Field(None, description="Memory type classification")
    infer: bool = Field(True, description="Enable intelligent memory processing")


class MemoryItem(BaseModel):
    """Single memory item for batch creation"""
    
    content: str = Field(..., description="Memory content")
    metadata: Optional[Dict[str, Any]] = Field(None, description="Additional metadata for this memory")
    filters: Optional[Dict[str, Any]] = Field(None, description="Filter metadata for this memory")
    scope: Optional[str] = Field(None, description="Memory scope")
    memory_type: Optional[str] = Field(None, description="Memory type classification")


class MemoryBatchCreateRequest(BaseModel):
    """Request model for creating multiple memories in batch"""
    
    memories: List[MemoryItem] = Field(..., description="List of memories to create", min_length=1, max_length=100)
    user_id: Optional[str] = Field(None, description="User identifier (applied to all memories)")
    agent_id: Optional[str] = Field(None, description="Agent identifier (applied to all memories)")
    run_id: Optional[str] = Field(None, description="Run/conversation identifier (applied to all memories)")
    infer: bool = Field(True, description="Enable intelligent memory processing")


class CodingAgentObservation(BaseModel):
    """Structured coding-agent event payload."""

    model_config = ConfigDict(extra="allow")

    content: str = Field(..., description="Observation content to persist")
    observation_id: Optional[str] = Field(None, description="Client-provided observation ID")
    observation_kind: str = Field("event", description="Observation kind, such as command_result")
    observation_level: Optional[str] = Field(None, description="Observation level, such as info or error")
    observation_status: Optional[str] = Field(None, description="Observation status, such as succeeded or failed")
    source: str = Field("coding_agent", description="Observation source")
    repo: Optional[str] = Field(None, description="Repository name or slug")
    branch: Optional[str] = Field(None, description="Repository branch")
    commit_sha: Optional[str] = Field(None, description="Repository commit SHA")
    tool_name: Optional[str] = Field(None, description="Tool or command name")
    task_id: Optional[str] = Field(None, description="Task identifier")
    thread_id: Optional[str] = Field(None, description="Thread identifier")
    session_id: Optional[str] = Field(None, description="Session identifier")
    metadata: Optional[Dict[str, Any]] = Field(None, description="Additional observation metadata")

    @field_validator("content")
    @classmethod
    def validate_content(cls, value: str) -> str:
        """Reject empty observation content."""
        if not value or not value.strip():
            raise ValueError("content is required")
        return value.strip()


class ObservationIngestRequest(CodingAgentObservation):
    """Request model for ingesting a structured coding-agent observation."""

    user_id: Optional[str] = Field(None, description="User identifier")
    agent_id: Optional[str] = Field(None, description="Agent identifier")
    run_id: Optional[str] = Field(None, description="Run/conversation identifier")
    filters: Optional[Dict[str, Any]] = Field(None, description="Additional filters")
    scope: str = Field("coding_agent", description="Memory scope")
    memory_type: str = Field("coding_agent_observation", description="Memory type classification")
    save_raw: bool = Field(True, description="Persist the raw structured observation")
    infer: bool = Field(False, description="Also run intelligent memory extraction")
    dedupe: bool = Field(False, description="Reuse existing observation records with the same ID when available")

    @model_validator(mode="after")
    def validate_ingest_mode(self):
        """At least one ingest mode must be enabled."""
        if not self.save_raw and not self.infer:
            raise ValueError("save_raw=false and infer=false would not store anything")
        return self


class ObservationBatchIngestRequest(BaseModel):
    """Request model for ingesting multiple coding-agent observations."""

    observations: List[Any] = Field(
        ...,
        description="Observations to ingest",
        min_length=1,
        max_length=100,
    )


class MemoryUpdateRequest(BaseModel):
    """Request model for updating a memory"""
    
    content: Optional[str] = Field(None, description="New content for the memory")
    metadata: Optional[Dict[str, Any]] = Field(None, description="Updated metadata")


class MemoryUpdateItem(BaseModel):
    """Single memory update item for batch update"""
    
    memory_id: int = Field(..., description="Memory ID to update")
    content: Optional[str] = Field(None, description="New content for the memory (optional)")
    metadata: Optional[Dict[str, Any]] = Field(None, description="Updated metadata (optional)")


class MemoryBatchUpdateRequest(BaseModel):
    """Request model for updating multiple memories in batch"""
    
    updates: List[MemoryUpdateItem] = Field(..., description="List of memory updates", min_length=1, max_length=100)
    user_id: Optional[str] = Field(None, description="User ID for access control")
    agent_id: Optional[str] = Field(None, description="Agent ID for access control")


class SearchRequest(BaseModel):
    """Request model for searching memories"""
    
    query: str = Field(..., description="Search query")
    user_id: Optional[str] = Field(None, description="Filter by user ID")
    agent_id: Optional[str] = Field(None, description="Filter by agent ID")
    run_id: Optional[str] = Field(None, description="Filter by run ID")
    filters: Optional[Dict[str, Any]] = Field(None, description="Additional filters")
    limit: int = Field(default=30, ge=1, le=100, description="Maximum number of results")
    threshold: Optional[float] = Field(
        None,
        ge=0.0,
        le=1.0,
        description="Minimum similarity score threshold",
    )
    time_range: Optional[str] = Field(
        default=None,
        description="Preset time window: '7d', '30d', '90d', or 'all'. When set, only memories created within the window are returned.",
    )
    sort_by: Optional[str] = Field(
        default=None,
        description="Field to sort results by: 'score' (default — by relevance), 'created_at', or 'updated_at'",
    )
    order: str = Field(
        default="desc",
        description="Sort order: 'desc' (default) or 'asc'",
    )


class UserProfileAddRequest(BaseModel):
    """Request model for adding messages and extracting user profile"""
    
    messages: Any = Field(..., description="Conversation messages (str, dict, or list[dict])")
    agent_id: Optional[str] = Field(None, description="Agent identifier")
    run_id: Optional[str] = Field(None, description="Run/session identifier")
    metadata: Optional[Dict[str, Any]] = Field(None, description="Additional metadata")
    filters: Optional[Dict[str, Any]] = Field(None, description="Filter metadata for advanced filtering")
    scope: Optional[str] = Field(None, description="Memory scope")
    memory_type: Optional[str] = Field(None, description="Memory type classification")
    prompt: Optional[str] = Field(None, description="Custom prompt for intelligent processing")
    infer: bool = Field(True, description="Enable intelligent memory processing")
    profile_type: str = Field("content", description="Profile extraction type: 'content' or 'topics'")
    custom_topics: Optional[str] = Field(None, description="Custom topics JSON string for structured extraction (only used when profile_type='topics')")
    strict_mode: bool = Field(False, description="Only output topics from provided list (only used when profile_type='topics')")
    include_roles: Optional[List[str]] = Field(["user"], description="Roles to include when filtering messages. Default: ['user']. Set to None or [] to disable.")
    exclude_roles: Optional[List[str]] = Field(["assistant"], description="Roles to exclude when filtering messages. Default: ['assistant']. Set to None or [] to disable.")
    native_language: Optional[str] = Field(None, description="ISO 639-1 language code (e.g., 'zh', 'en') for profile extraction. If specified, profile will be extracted in this language.")


class UserProfileUpdateRequest(BaseModel):
    """Request model for updating a user memory"""
    
    content: str = Field(..., description="New content for the memory")
    agent_id: Optional[str] = Field(None, description="Agent identifier for access control")
    metadata: Optional[Dict[str, Any]] = Field(None, description="Updated metadata")


class AgentMemoryCreateRequest(BaseModel):
    """Request model for creating agent memory"""
    
    content: str = Field(..., description="Memory content")
    user_id: Optional[str] = Field(None, description="User ID")
    run_id: Optional[str] = Field(None, description="Run ID")


class AgentMemoryShareRequest(BaseModel):
    """Request model for sharing memories between agents"""
    
    target_agent_id: str = Field(..., description="Target agent ID to share with")
    memory_ids: Optional[List[int]] = Field(None, description="Specific memory IDs to share (None for all)")


class BulkDeleteRequest(BaseModel):
    """Request model for bulk deleting memories"""
    
    memory_ids: List[int] = Field(..., description="List of memory IDs to delete", min_length=1, max_length=100)
    user_id: Optional[str] = Field(None, description="User ID for access control")
    agent_id: Optional[str] = Field(None, description="Agent ID for access control")
