//! API Request/Response Types
//!
//! All HTTP API request and response structures for the shodh-memory server.
//! Extracted from main.rs for better organization.

use serde::{Deserialize, Serialize};

// =============================================================================
// HEALTH & INFRASTRUCTURE
// =============================================================================

#[derive(Serialize)]
pub struct HealthResponse {
    pub status: String,
    pub version: String,
    pub uptime_seconds: u64,
    pub memory_mb: f64,
    pub active_users: usize,
}

// =============================================================================
// AUDIT & EVENTS
// =============================================================================

/// Audit event for history tracking
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuditEvent {
    pub timestamp: chrono::DateTime<chrono::Utc>,
    pub event_type: String,
    pub memory_id: String,
    pub details: String,
}

/// SSE Memory Event - lightweight event for real-time streaming
#[derive(Debug, Clone, Serialize)]
pub struct MemoryEvent {
    pub event_type: String,
    pub timestamp: chrono::DateTime<chrono::Utc>,
    pub user_id: String,
    pub memory_id: Option<String>,
    pub content_preview: Option<String>,
    pub memory_type: Option<String>,
    pub importance: Option<f32>,
    pub count: Option<usize>,
    /// Tags/entities for TUI display
    #[serde(skip_serializing_if = "Option::is_none")]
    pub entities: Option<Vec<String>>,
    /// Full command results for rich TUI display (recall, proactive_context)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub results: Option<serde_json::Value>,
}

/// Context status from Claude Code (MCP server reports this via status line)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ContextStatus {
    pub session_id: Option<String>,
    pub tokens_used: u64,
    pub tokens_budget: u64,
    pub percent_used: u8,
    pub current_task: Option<String>,
    pub model: Option<String>,
    pub updated_at: chrono::DateTime<chrono::Utc>,
}

// =============================================================================
// RECORD/REMEMBER API
// =============================================================================

#[derive(Deserialize)]
pub struct RecordRequest {
    pub user_id: String,
    pub content: String,
    #[serde(default)]
    pub experience_type: Option<String>,
    #[serde(default)]
    pub entities: Vec<String>,
}

#[derive(Serialize)]
pub struct RecordResponse {
    pub id: String,
    pub created_at: String,
}

// =============================================================================
// RECALL API
// =============================================================================

/// Simplified recall request - just query text
#[derive(Debug, Deserialize)]
pub struct RecallRequest {
    pub user_id: String,
    pub query: String,
    #[serde(default = "default_recall_limit")]
    pub limit: usize,
    /// Retrieval mode: "semantic", "associative", "temporal", "spatial", "mission", "action_outcome", or "hybrid" (default)
    #[serde(default = "default_recall_mode")]
    pub mode: String,
    /// Session ID for session-scoped retrieval (used with mode="temporal")
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub session_id: Option<String>,

    // === Robotics Filters ===
    /// Filter by robot/drone identifier
    #[serde(default)]
    pub robot_id: Option<String>,
    /// Filter by mission identifier
    #[serde(default)]
    pub mission_id: Option<String>,
    /// Spatial filter: center latitude (requires geo_lon and geo_radius_meters)
    #[serde(default)]
    pub geo_lat: Option<f64>,
    /// Spatial filter: center longitude
    #[serde(default)]
    pub geo_lon: Option<f64>,
    /// Spatial filter: search radius in meters
    #[serde(default)]
    pub geo_radius_meters: Option<f64>,
    /// Filter by action type
    #[serde(default)]
    pub action_type: Option<String>,
    /// Filter by minimum reward value
    #[serde(default)]
    pub reward_min: Option<f32>,
    /// Filter by maximum reward value
    #[serde(default)]
    pub reward_max: Option<f32>,
    /// Filter by outcome type: success, failure, partial, aborted, timeout
    #[serde(default)]
    pub outcome_type: Option<String>,
    /// Filter for failures only
    #[serde(default)]
    pub failures_only: Option<bool>,
    /// Filter by terrain type
    #[serde(default)]
    pub terrain_type: Option<String>,
    /// Filter by tags (any match)
    #[serde(default)]
    pub tags: Option<Vec<String>>,
}

pub fn default_recall_limit() -> usize {
    5
}

pub fn default_recall_mode() -> String {
    "hybrid".to_string()
}

/// Simplified recall response - returns just text snippets
#[derive(Serialize)]
pub struct RecallResponse {
    pub memories: Vec<RecallMemory>,
    pub count: usize,
    /// Retrieval statistics (for observability)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub retrieval_stats: Option<crate::memory::types::RetrievalStats>,
    /// Related todos found via semantic search
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub todos: Vec<RecallTodo>,
    /// Number of todos found
    #[serde(skip_serializing_if = "Option::is_none")]
    pub todo_count: Option<usize>,
    /// Related semantic facts extracted from memories
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub facts: Vec<RecallFact>,
    /// Number of facts found
    #[serde(skip_serializing_if = "Option::is_none")]
    pub fact_count: Option<usize>,
    /// Triggered reminders (future intentions that influenced retrieval)
    /// These are prospective tasks whose context matched the query
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub triggered_reminders: Vec<RecallReminder>,
    /// Number of triggered reminders
    #[serde(skip_serializing_if = "Option::is_none")]
    pub reminder_count: Option<usize>,
    /// Causal lineage edges connecting recalled memories
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub lineage: Vec<RecallLineageEdge>,
    /// Number of lineage edges found
    #[serde(skip_serializing_if = "Option::is_none")]
    pub lineage_count: Option<usize>,
}

/// Causal lineage edge returned in recall results
#[derive(Serialize)]
pub struct RecallLineageEdge {
    pub from: String,
    pub to: String,
    pub relation: String,
    pub confidence: f32,
}

/// Reminder/prospective task returned in recall results
#[derive(Serialize)]
pub struct RecallReminder {
    pub id: String,
    pub content: String,
    /// Keywords that triggered this reminder (from OnContext trigger)
    pub keywords: Vec<String>,
    /// How the reminder was matched (keyword_match, semantic_match)
    pub match_type: String,
    pub priority: u8,
    pub created_at: String,
}

/// Semantic fact returned in recall results
#[derive(Serialize)]
pub struct RecallFact {
    pub id: String,
    pub fact: String,
    pub confidence: f32,
    pub support_count: usize,
    pub related_entities: Vec<String>,
}

/// Todo returned in recall results
#[derive(Serialize)]
pub struct RecallTodo {
    pub id: String,
    pub short_id: String,
    pub content: String,
    pub status: String,
    pub priority: String,
    pub project: Option<String>,
    pub due_date: Option<String>,
    pub score: f32,
}

#[derive(Serialize)]
pub struct RecallMemory {
    pub id: String,
    pub experience: RecallExperience,
    pub importance: f32,
    pub created_at: String,
    pub score: f32,
    pub tier: String,
}

#[derive(Serialize)]
pub struct RecallExperience {
    pub content: String,
    pub memory_type: Option<String>,
    pub tags: Vec<String>,
}

// =============================================================================
// UPSERT API
// =============================================================================

/// Upsert request - create or update memory with external linking
#[derive(Deserialize)]
pub struct UpsertRequest {
    pub user_id: String,
    pub external_id: String,
    pub content: String,
    #[serde(default)]
    pub memory_type: Option<String>,
    #[serde(default)]
    pub tags: Vec<String>,
    #[serde(default = "default_change_type")]
    pub change_type: String,
    #[serde(default)]
    pub changed_by: Option<String>,
    #[serde(default)]
    pub change_reason: Option<String>,
    /// Optional importance override (0.0-1.0). When provided, bypasses auto-calculation.
    #[serde(default)]
    pub importance: Option<f32>,
}

fn default_change_type() -> String {
    "content_updated".to_string()
}

#[derive(Serialize)]
pub struct UpsertResponse {
    pub id: String,
    pub external_id: String,
    pub created: bool,
    pub updated: bool,
    pub revision: usize,
}

// =============================================================================
// MEMORY HISTORY API
// =============================================================================

/// Request to get memory history (audit trail)
#[derive(Deserialize)]
pub struct MemoryHistoryRequest {
    pub user_id: String,
    pub memory_id: String,
}

/// Response with memory revision history
#[derive(Serialize)]
pub struct MemoryHistoryResponse {
    pub memory_id: String,
    pub external_id: Option<String>,
    pub current_content: String,
    pub revision_count: usize,
    pub revisions: Vec<MemoryRevisionInfo>,
}

#[derive(Serialize)]
pub struct MemoryRevisionInfo {
    pub revision: usize,
    pub content: String,
    pub changed_at: String,
    pub change_type: String,
    pub changed_by: Option<String>,
}

// =============================================================================
// RETRIEVAL RESPONSE
// =============================================================================

/// Response for list/search operations returning multiple memories
#[derive(Serialize)]
pub struct RetrieveResponse {
    pub memories: Vec<serde_json::Value>,
    pub count: usize,
}

// =============================================================================
// TRACKED RETRIEVAL & FEEDBACK
// =============================================================================

/// Request for tracked retrieval (returns tracking ID for later feedback)
#[derive(Debug, Deserialize)]
pub struct TrackedRetrieveRequest {
    pub user_id: String,
    pub query: String,
    #[serde(default = "default_recall_limit")]
    pub limit: usize,
    #[serde(default = "default_recall_mode")]
    pub mode: String,
}

/// Response with tracking ID for feedback
#[derive(Serialize)]
pub struct TrackedRetrieveResponse {
    pub tracking_id: String,
    pub ids: Vec<String>,
    pub memories: Vec<RecallMemory>,
}

/// Request to provide feedback on retrieval outcome
#[derive(Debug, Deserialize)]
pub struct ReinforceFeedbackRequest {
    pub user_id: String,
    /// Memory IDs to reinforce
    pub ids: Vec<String>,
    /// "helpful", "misleading", or "neutral"
    pub outcome: String,
}

// =============================================================================
// CONSOLIDATION
// =============================================================================

/// Request to trigger consolidation
#[derive(Debug, Deserialize)]
pub struct ConsolidateRequest {
    pub user_id: String,
    /// Minimum supporting memories required for fact extraction
    #[serde(default = "default_min_support")]
    pub min_support: usize,
    /// Minimum age in days for memories to be considered for consolidation
    #[serde(default = "default_min_age_days")]
    pub min_age_days: i64,
}

fn default_min_support() -> usize {
    2
}

fn default_min_age_days() -> i64 {
    1
}

/// Response from consolidation
#[derive(Serialize)]
pub struct ConsolidateResponse {
    pub memories_analyzed: usize,
    pub facts_extracted: usize,
    pub facts_reinforced: usize,
    pub fact_ids: Vec<String>,
    pub memories_replayed: usize,
    pub edges_strengthened: usize,
    pub entity_edges_strengthened: usize,
    pub memories_decayed: usize,
    #[serde(skip_serializing_if = "Vec::is_empty")]
    pub warnings: Vec<String>,
}

// =============================================================================
// INDEX MAINTENANCE
// =============================================================================

#[derive(Deserialize)]
pub struct VerifyIndexRequest {
    pub user_id: String,
}

#[derive(Deserialize)]
pub struct RepairIndexRequest {
    pub user_id: String,
}

#[derive(Serialize)]
pub struct RepairIndexResponse {
    pub success: bool,
    pub total_storage: usize,
    pub total_indexed: usize,
    pub repaired: usize,
    pub failed: usize,
    pub is_healthy: bool,
}

#[derive(Deserialize)]
pub struct CleanupCorruptedRequest {
    pub user_id: String,
}

#[derive(Serialize)]
pub struct CleanupCorruptedResponse {
    pub success: bool,
    pub deleted_count: usize,
}

#[derive(Deserialize)]
pub struct MigrateLegacyRequest {
    pub user_id: String,
}

#[derive(Serialize)]
pub struct MigrateLegacyResponse {
    pub success: bool,
    pub migrated_count: usize,
    pub already_current_count: usize,
    pub failed_count: usize,
}

#[derive(Deserialize)]
pub struct RebuildIndexRequest {
    pub user_id: String,
}

#[derive(Serialize)]
pub struct RebuildIndexResponse {
    pub success: bool,
    pub storage_count: usize,
    pub indexed_count: usize,
    pub is_healthy: bool,
}

// =============================================================================
// BACKUP & RESTORE
// =============================================================================

#[derive(Deserialize)]
pub struct CreateBackupRequest {
    pub user_id: String,
}

#[derive(Serialize)]
pub struct BackupResponse {
    pub success: bool,
    pub backup: Option<crate::backup::BackupMetadata>,
    pub message: String,
}

#[derive(Deserialize)]
pub struct ListBackupsRequest {
    pub user_id: String,
}

#[derive(Serialize)]
pub struct ListBackupsResponse {
    pub success: bool,
    pub backups: Vec<crate::backup::BackupMetadata>,
    pub count: usize,
}

#[derive(Deserialize)]
pub struct VerifyBackupRequest {
    pub user_id: String,
    pub backup_id: u32,
}

#[derive(Serialize)]
pub struct VerifyBackupResponse {
    pub success: bool,
    pub is_valid: bool,
    pub message: String,
}

#[derive(Deserialize)]
pub struct PurgeBackupsRequest {
    pub user_id: String,
    pub keep_count: usize,
}

#[derive(Serialize)]
pub struct PurgeBackupsResponse {
    pub success: bool,
    pub purged_count: usize,
}

#[derive(Deserialize)]
pub struct RestoreBackupRequest {
    pub user_id: String,
    #[serde(default)]
    pub backup_id: Option<u32>,
}

#[derive(Serialize)]
pub struct RestoreBackupResponse {
    pub success: bool,
    pub message: String,
    pub restored_stores: Vec<String>,
}

// =============================================================================
// CONTEXT STATUS
// =============================================================================

#[derive(Deserialize)]
pub struct ContextStatusRequest {
    pub session_id: String,
    pub tokens_used: u64,
    pub tokens_limit: u64,
    #[serde(default)]
    pub current_task: Option<String>,
    #[serde(default)]
    pub model: Option<String>,
}

// =============================================================================
// DELETE / FORGET APIs
// =============================================================================

#[derive(Deserialize)]
pub struct ForgetByAgeRequest {
    pub user_id: String,
    pub older_than_days: i64,
}

#[derive(Deserialize)]
pub struct ForgetByImportanceRequest {
    pub user_id: String,
    pub below_importance: f32,
    #[serde(default)]
    pub older_than_days: Option<i64>,
}

#[derive(Deserialize)]
pub struct ForgetByPatternRequest {
    pub user_id: String,
    pub pattern: String,
}

#[derive(Deserialize)]
pub struct BulkDeleteRequest {
    pub user_id: String,
    pub memory_ids: Vec<String>,
    /// Optional: require all deletes to succeed (default: false, best-effort)
    #[serde(default)]
    pub atomic: bool,
}

#[derive(Deserialize)]
pub struct ClearAllRequest {
    pub user_id: String,
    /// Must be "DELETE_ALL_MEMORIES" to confirm
    pub confirm: String,
}

// =============================================================================
// RECALL BY TAGS/DATE
// =============================================================================

#[derive(Deserialize)]
pub struct RecallByTagsRequest {
    pub user_id: String,
    pub tags: Vec<String>,
    #[serde(default)]
    pub limit: Option<usize>,
}

#[derive(Deserialize)]
pub struct RecallByDateRequest {
    pub user_id: String,
    pub start: chrono::DateTime<chrono::Utc>,
    pub end: chrono::DateTime<chrono::Utc>,
    #[serde(default)]
    pub limit: Option<usize>,
}

#[derive(Deserialize)]
pub struct ForgetByTagsRequest {
    pub user_id: String,
    pub tags: Vec<String>,
}

#[derive(Deserialize)]
pub struct ForgetByDateRequest {
    pub user_id: String,
    pub start: chrono::DateTime<chrono::Utc>,
    pub end: chrono::DateTime<chrono::Utc>,
}

// =============================================================================
// PATCH MEMORY
// =============================================================================

#[derive(Deserialize)]
pub struct PatchMemoryRequest {
    pub user_id: String,
    pub memory_id: String,
    #[serde(default)]
    pub content: Option<String>,
    #[serde(default)]
    pub tags: Option<Vec<String>>,
    #[serde(default)]
    pub importance: Option<f32>,
}

// =============================================================================
// MULTIMODAL SEARCH
// =============================================================================

#[derive(Debug, Deserialize)]
pub struct MultiModalSearchRequest {
    pub user_id: String,
    pub query_text: String,
    pub mode: String, // "similarity", "temporal", "causal", "associative", "hybrid"
    pub limit: Option<usize>,
}

// =============================================================================
// ROBOTICS SEARCH
// =============================================================================

#[derive(Debug, Deserialize)]
pub struct RoboticsSearchRequest {
    pub user_id: String,
    pub query_text: String,
    #[serde(default)]
    pub robot_id: Option<String>,
    #[serde(default)]
    pub mission_id: Option<String>,
    #[serde(default)]
    pub location: Option<String>,
    #[serde(default)]
    pub time_range_start: Option<chrono::DateTime<chrono::Utc>>,
    #[serde(default)]
    pub time_range_end: Option<chrono::DateTime<chrono::Utc>>,
    #[serde(default)]
    pub include_spatial: bool,
    #[serde(default)]
    pub include_mission: bool,
    #[serde(default)]
    pub include_actions: bool,
    #[serde(default = "default_robotics_limit")]
    pub limit: usize,
}

fn default_robotics_limit() -> usize {
    10
}

// =============================================================================
// GRAPH API
// =============================================================================

#[derive(Deserialize)]
pub struct GetUncompressedRequest {
    pub user_id: String,
    pub memory_id: String,
}

#[derive(Deserialize)]
pub struct AddEntityRequest {
    pub user_id: String,
    pub name: String,
    pub label: String,
    #[serde(default)]
    pub summary: Option<String>,
}

#[derive(Deserialize)]
pub struct AddRelationshipRequest {
    pub user_id: String,
    pub from_entity: String,
    pub to_entity: String,
    pub relation_type: String,
    #[serde(default)]
    pub strength: Option<f32>,
    #[serde(default)]
    pub context: Option<String>,
}

#[derive(Deserialize)]
pub struct GetAllEntitiesRequest {
    pub user_id: String,
}

// =============================================================================
// VISUALIZATION
// =============================================================================

#[derive(Serialize)]
pub struct BrainStateResponse {
    pub user_id: String,
    pub neurons: Vec<MemoryNeuron>,
    pub connections: Vec<(String, String, f32)>,
    pub stats: BrainStats,
}

#[derive(Serialize)]
pub struct MemoryNeuron {
    pub id: String,
    pub content_preview: String,
    pub memory_type: String,
    pub importance: f32,
    pub activation: f32,
    pub tier: String,
    pub created_at: String,
}

#[derive(Serialize)]
pub struct BrainStats {
    pub total_neurons: usize,
    pub total_connections: usize,
    pub avg_importance: f32,
    pub memory_by_type: std::collections::HashMap<String, usize>,
}

#[derive(Deserialize)]
pub struct BuildVisualizationRequest {
    pub user_id: String,
}

#[cfg(test)]
mod tests {
    use super::super::remember::{BatchRememberOptions, BatchRememberRequest, RememberRequest};
    use super::*;
    use serde_json::json;

    #[test]
    fn test_health_response_serialize() {
        let resp = HealthResponse {
            status: "ok".to_string(),
            version: "0.1.0".to_string(),
            uptime_seconds: 1000,
            memory_mb: 256.5,
            active_users: 10,
        };
        let json = serde_json::to_string(&resp).unwrap();
        assert!(json.contains("ok"));
        assert!(json.contains("0.1.0"));
    }

    #[test]
    fn test_record_request_deserialize() {
        let json = json!({
            "user_id": "test-user",
            "content": "test content"
        });
        let req: RecordRequest = serde_json::from_value(json).unwrap();
        assert_eq!(req.user_id, "test-user");
        assert_eq!(req.content, "test content");
        assert!(req.entities.is_empty());
    }

    #[test]
    fn test_record_request_with_entities() {
        let json = json!({
            "user_id": "test-user",
            "content": "test content",
            "experience_type": "Decision",
            "entities": ["entity1", "entity2"]
        });
        let req: RecordRequest = serde_json::from_value(json).unwrap();
        assert_eq!(req.entities.len(), 2);
        assert_eq!(req.experience_type, Some("Decision".to_string()));
    }

    #[test]
    fn test_remember_request_defaults() {
        let json = json!({
            "user_id": "test-user",
            "content": "test content"
        });
        let req: RememberRequest = serde_json::from_value(json).unwrap();
        assert_eq!(req.user_id, "test-user");
        assert!(req.memory_type.is_none());
        assert!(req.tags.is_empty());
        assert!(req.emotional_valence.is_none());
    }

    #[test]
    fn test_remember_request_full() {
        let json = json!({
            "user_id": "test-user",
            "content": "test content",
            "memory_type": "Learning",
            "tags": ["rust", "memory"],
            "emotional_valence": 0.5,
            "emotional_arousal": 0.7,
            "emotion": "joy",
            "source_type": "user",
            "credibility": 0.9,
            "episode_id": "ep-123",
            "sequence_number": 5
        });
        let req: RememberRequest = serde_json::from_value(json).unwrap();
        assert_eq!(req.memory_type, Some("Learning".to_string()));
        assert_eq!(req.tags.len(), 2);
        assert_eq!(req.emotional_valence, Some(0.5));
        assert_eq!(req.episode_id, Some("ep-123".to_string()));
    }

    #[test]
    fn test_recall_request_defaults() {
        let json = json!({
            "user_id": "test-user",
            "query": "search query"
        });
        let req: RecallRequest = serde_json::from_value(json).unwrap();
        assert_eq!(req.user_id, "test-user");
        assert_eq!(req.query, "search query");
        assert_eq!(req.limit, 5); // default
        assert_eq!(req.mode, "hybrid"); // default
    }

    #[test]
    fn test_recall_request_custom() {
        let json = json!({
            "user_id": "test-user",
            "query": "search query",
            "limit": 10,
            "mode": "semantic"
        });
        let req: RecallRequest = serde_json::from_value(json).unwrap();
        assert_eq!(req.limit, 10);
        assert_eq!(req.mode, "semantic");
    }

    #[test]
    fn test_recall_response_serialize() {
        let resp = RecallResponse {
            memories: vec![],
            count: 0,
            retrieval_stats: None,
            todos: vec![],
            todo_count: None,
            facts: vec![],
            fact_count: None,
            triggered_reminders: vec![],
            reminder_count: None,
            lineage: vec![],
            lineage_count: None,
        };
        let json = serde_json::to_string(&resp).unwrap();
        assert!(json.contains("memories"));
        assert!(json.contains("count"));
    }

    #[test]
    fn test_batch_remember_options_default() {
        let opts = BatchRememberOptions::default();
        assert!(opts.extract_entities);
        assert!(opts.create_edges);
    }

    #[test]
    fn test_batch_remember_request() {
        let json = json!({
            "user_id": "test-user",
            "memories": [
                {"content": "memory 1"},
                {"content": "memory 2", "tags": ["tag1"]}
            ]
        });
        let req: BatchRememberRequest = serde_json::from_value(json).unwrap();
        assert_eq!(req.user_id, "test-user");
        assert_eq!(req.memories.len(), 2);
    }

    #[test]
    fn test_upsert_request() {
        let json = json!({
            "user_id": "test-user",
            "external_id": "linear:SHO-123",
            "content": "issue content"
        });
        let req: UpsertRequest = serde_json::from_value(json).unwrap();
        assert_eq!(req.external_id, "linear:SHO-123");
        assert_eq!(req.change_type, "content_updated"); // default
    }

    #[test]
    fn test_consolidate_request_defaults() {
        let json = json!({
            "user_id": "test-user"
        });
        let req: ConsolidateRequest = serde_json::from_value(json).unwrap();
        assert_eq!(req.min_support, 2); // default
        assert_eq!(req.min_age_days, 1); // default
    }

    #[test]
    fn test_multimodal_search_request() {
        let json = json!({
            "user_id": "test-user",
            "query_text": "search query",
            "mode": "hybrid"
        });
        let req: MultiModalSearchRequest = serde_json::from_value(json).unwrap();
        assert_eq!(req.user_id, "test-user");
        assert_eq!(req.mode, "hybrid");
    }

    #[test]
    fn test_robotics_search_request_defaults() {
        let json = json!({
            "user_id": "test-user",
            "query_text": "search query"
        });
        let req: RoboticsSearchRequest = serde_json::from_value(json).unwrap();
        assert_eq!(req.limit, 10); // default
        assert!(!req.include_spatial);
    }

    #[test]
    fn test_audit_event_serialize() {
        let event = AuditEvent {
            timestamp: chrono::Utc::now(),
            event_type: "remember".to_string(),
            memory_id: "mem-123".to_string(),
            details: "test details".to_string(),
        };
        let json = serde_json::to_string(&event).unwrap();
        assert!(json.contains("remember"));
    }

    #[test]
    fn test_memory_event_serialize() {
        let event = MemoryEvent {
            event_type: "created".to_string(),
            timestamp: chrono::Utc::now(),
            user_id: "user-123".to_string(),
            memory_id: Some("mem-123".to_string()),
            content_preview: Some("preview...".to_string()),
            memory_type: Some("Observation".to_string()),
            importance: Some(0.8),
            count: None,
            entities: None,
            results: None,
        };
        let json = serde_json::to_string(&event).unwrap();
        assert!(json.contains("created"));
    }

    #[test]
    fn test_brain_stats_serialize() {
        let stats = BrainStats {
            total_neurons: 100,
            total_connections: 500,
            avg_importance: 0.65,
            memory_by_type: std::collections::HashMap::new(),
        };
        let json = serde_json::to_string(&stats).unwrap();
        assert!(json.contains("total_neurons"));
    }
}
