use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum SignalType {
    Interrupt,
    Query,
    Patch,
    Fork,
}

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
#[serde(tag = "$type")]
pub enum SignalContent {
    #[serde(rename = "interrupt", rename_all = "camelCase")]
    Interrupt {
        lens: String,
        body: String,
        scope: Vec<String>,
    },
    #[serde(rename = "query", rename_all = "camelCase")]
    Query { question: String, context: String },
    #[serde(rename = "patch", rename_all = "camelCase")]
    Patch {
        proposal: String,
        alternatives: Vec<String>,
        #[serde(default, skip_serializing_if = "Option::is_none")]
        trade_offs: Option<Vec<String>>,
    },
    #[serde(rename = "fork", rename_all = "camelCase")]
    Fork {
        sources: Vec<String>,
        integration: String,
        #[serde(default, skip_serializing_if = "Option::is_none")]
        unresolved: Option<Vec<String>>,
    },
}

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct Signal {
    pub signal_type: SignalType,
    pub socket: String,
    pub content: SignalContent,
    pub created_at: DateTime<Utc>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub author_did: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub uri: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub priority: Option<i32>,
}

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct Ack {
    pub subject: String,
    pub body: String,
    pub created_at: DateTime<Utc>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub author_did: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub uri: Option<String>,
    pub socket: String,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct SocketContext {
    pub name: String,
    pub description: String,
    pub daemons: Vec<String>,
    pub recent_signals: Vec<Signal>,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct HookContextBundle {
    pub sockets: HashMap<String, SocketBundle>,
    pub communion_updates: Vec<Signal>,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct SocketBundle {
    pub description: String,
    pub new_signals: Vec<Signal>,
}

#[derive(Debug, Clone)]
pub struct ScryFilter {
    pub socket: String,
    pub limit: u32,
    pub since: Option<DateTime<Utc>>,
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_signal_roundtrip() {
        let signal = Signal {
            signal_type: SignalType::Interrupt,
            socket: "arena".to_string(),
            content: SignalContent::Interrupt {
                lens: "test".to_string(),
                body: "hello".to_string(),
                scope: vec!["general".to_string()],
            },
            created_at: Utc::now(),
            author_did: Some("did:plc:test".to_string()),
            uri: Some("at://did:plc:test/system.egregore.signal/abc".to_string()),
            priority: Some(5),
        };
        let json = serde_json::to_string(&signal).unwrap();
        let parsed: Signal = serde_json::from_str(&json).unwrap();
        assert_eq!(parsed.socket, "arena");
        assert_eq!(parsed.signal_type, SignalType::Interrupt);
    }

    #[test]
    fn test_signal_content_discriminated_union() {
        // Test that $type tag works correctly
        let json = r#"{"$type":"query","question":"hello?","context":"test"}"#;
        let content: SignalContent = serde_json::from_str(json).unwrap();
        match content {
            SignalContent::Query { question, .. } => assert_eq!(question, "hello?"),
            _ => panic!("Wrong variant"),
        }
    }
}
