// tests/message_tests.rs

//! Integration tests for Message types
//!
//! These tests verify message serialization, deserialization,
//! and data structure validity.

use chrono::{Duration, Utc};
use qrusty::message::{Message, Priority, PriorityKind, PriorityOrdering, QueueConfig, QueueStats};

#[test]
fn test_message_creation() {
    let now = Utc::now();
    let message = Message {
        id: "test-id-123".to_string(),
        queue: "test_queue".to_string(),
        priority: Priority::Numeric(100),
        payload: r#"{"key": "value"}"#.to_string(),
        created_at: now,
        locked_until: None,
        locked_by: None,
        retry_count: 0,
        max_retries: 3,
        payload_ref: None,
        payload_hash: None,
    };

    assert_eq!(message.id, "test-id-123");
    assert_eq!(message.queue, "test_queue");
    assert_eq!(message.priority, Priority::Numeric(100));
    assert_eq!(message.payload, r#"{"key": "value"}"#);
    assert_eq!(message.created_at, now);
    assert!(message.locked_until.is_none());
    assert!(message.locked_by.is_none());
    assert_eq!(message.retry_count, 0);
    assert_eq!(message.max_retries, 3);
}

#[test]
fn test_message_serialization() {
    let now = Utc::now();
    let message = Message {
        id: "test-id-123".to_string(),
        queue: "test_queue".to_string(),
        priority: Priority::Numeric(100),
        payload: r#"{"key": "value"}"#.to_string(),
        created_at: now,
        locked_until: Some(now + Duration::seconds(30)),
        locked_by: Some("consumer-1".to_string()),
        retry_count: 1,
        max_retries: 3,
        payload_ref: None,
        payload_hash: None,
    };

    // Serialize to JSON
    let json = serde_json::to_string(&message).unwrap();
    assert!(json.contains("test-id-123"));
    assert!(json.contains("test_queue"));
    assert!(json.contains("consumer-1"));

    // Deserialize from JSON
    let deserialized: Message = serde_json::from_str(&json).unwrap();
    assert_eq!(deserialized.id, message.id);
    assert_eq!(deserialized.queue, message.queue);
    assert_eq!(deserialized.priority, message.priority);
    assert_eq!(deserialized.payload, message.payload);
    assert_eq!(deserialized.retry_count, message.retry_count);
    assert_eq!(deserialized.max_retries, message.max_retries);
    assert_eq!(deserialized.locked_by, message.locked_by);
}

#[test]
fn test_message_clone() {
    let now = Utc::now();
    let original = Message {
        id: "test-id-123".to_string(),
        queue: "test_queue".to_string(),
        priority: Priority::Numeric(100),
        payload: r#"{"key": "value"}"#.to_string(),
        created_at: now,
        locked_until: Some(now + Duration::seconds(30)),
        locked_by: Some("consumer-1".to_string()),
        retry_count: 1,
        max_retries: 3,
        payload_ref: None,
        payload_hash: None,
    };

    let cloned = original.clone();

    assert_eq!(cloned.id, original.id);
    assert_eq!(cloned.queue, original.queue);
    assert_eq!(cloned.priority, original.priority);
    assert_eq!(cloned.payload, original.payload);
    assert_eq!(cloned.created_at, original.created_at);
    assert_eq!(cloned.locked_until, original.locked_until);
    assert_eq!(cloned.locked_by, original.locked_by);
    assert_eq!(cloned.retry_count, original.retry_count);
    assert_eq!(cloned.max_retries, original.max_retries);
}

#[test]
fn test_message_debug() {
    let message = Message {
        id: "test-id-123".to_string(),
        queue: "test_queue".to_string(),
        priority: Priority::Numeric(100),
        payload: r#"{"key": "value"}"#.to_string(),
        created_at: Utc::now(),
        locked_until: None,
        locked_by: None,
        retry_count: 0,
        max_retries: 3,
        payload_ref: None,
        payload_hash: None,
    };

    let debug_str = format!("{:?}", message);
    assert!(debug_str.contains("Message"));
    assert!(debug_str.contains("test-id-123"));
    assert!(debug_str.contains("test_queue"));
    assert!(debug_str.contains("100"));
}

#[test]
fn test_queue_stats_creation() {
    let stats = QueueStats {
        name: "orders".to_string(),
        available: 150,
        locked: 25,
        total: 175,
        config: QueueConfig::default(),
    };

    assert_eq!(stats.name, "orders");
    assert_eq!(stats.available, 150);
    assert_eq!(stats.locked, 25);
    assert_eq!(stats.total, 175);
}

#[test]
fn test_queue_stats_serialization() {
    let stats = QueueStats {
        name: "orders".to_string(),
        available: 150,
        locked: 25,
        total: 175,
        config: QueueConfig::default(),
    };

    // Serialize to JSON
    let json = serde_json::to_string(&stats).unwrap();
    assert!(json.contains("orders"));
    assert!(json.contains("150"));
    assert!(json.contains("25"));
    assert!(json.contains("175"));

    // Deserialize from JSON
    let deserialized: QueueStats = serde_json::from_str(&json).unwrap();
    assert_eq!(deserialized.name, stats.name);
    assert_eq!(deserialized.available, stats.available);
    assert_eq!(deserialized.locked, stats.locked);
    assert_eq!(deserialized.total, stats.total);
}

#[test]
fn test_queue_stats_clone() {
    let original = QueueStats {
        name: "orders".to_string(),
        available: 150,
        locked: 25,
        total: 175,
        config: QueueConfig::default(),
    };

    let cloned = original.clone();

    assert_eq!(cloned.name, original.name);
    assert_eq!(cloned.available, original.available);
    assert_eq!(cloned.locked, original.locked);
    assert_eq!(cloned.total, original.total);
}

#[test]
fn test_queue_stats_debug() {
    let stats = QueueStats {
        name: "orders".to_string(),
        available: 150,
        locked: 25,
        total: 175,
        config: QueueConfig::default(),
    };

    let debug_str = format!("{:?}", stats);
    assert!(debug_str.contains("QueueStats"));
    assert!(debug_str.contains("orders"));
    assert!(debug_str.contains("150"));
    assert!(debug_str.contains("25"));
    assert!(debug_str.contains("175"));
}

#[test]
fn test_message_with_complex_payload() {
    let complex_payload = r#"{
        "order_id": 12345,
        "customer": {
            "name": "John Doe",
            "email": "john@example.com"
        },
        "items": [
            {"sku": "ABC123", "quantity": 2},
            {"sku": "XYZ789", "quantity": 1}
        ],
        "total": 99.99,
        "metadata": {
            "source": "web",
            "campaign": "summer2025"
        }
    }"#;

    let message = Message {
        id: uuid::Uuid::new_v4().to_string(),
        queue: "orders".to_string(),
        priority: Priority::Numeric(100),
        payload: complex_payload.to_string(),
        created_at: Utc::now(),
        locked_until: None,
        locked_by: None,
        retry_count: 0,
        max_retries: 3,
        payload_ref: None,
        payload_hash: None,
    };

    // Verify the payload is valid JSON
    let parsed: serde_json::Value = serde_json::from_str(&message.payload).unwrap();
    assert_eq!(parsed["order_id"], 12345);
    assert_eq!(parsed["customer"]["name"], "John Doe");
    assert_eq!(parsed["items"][0]["sku"], "ABC123");
    assert_eq!(parsed["total"], 99.99);

    // Verify the entire message can be serialized/deserialized
    let serialized = serde_json::to_string(&message).unwrap();
    let deserialized: Message = serde_json::from_str(&serialized).unwrap();
    assert_eq!(deserialized.payload, message.payload);
}

#[test]
fn test_message_priority_extremes() {
    // Test minimum priority
    let min_priority_msg = Message {
        id: "min-test".to_string(),
        queue: "test".to_string(),
        priority: Priority::Numeric(0),
        payload: "min priority".to_string(),
        created_at: Utc::now(),
        locked_until: None,
        locked_by: None,
        retry_count: 0,
        max_retries: 3,
        payload_ref: None,
        payload_hash: None,
    };

    // Test maximum priority
    let max_priority_msg = Message {
        id: "max-test".to_string(),
        queue: "test".to_string(),
        priority: Priority::Numeric(u64::MAX),
        payload: "max priority".to_string(),
        created_at: Utc::now(),
        locked_until: None,
        locked_by: None,
        retry_count: 0,
        max_retries: 3,
        payload_ref: None,
        payload_hash: None,
    };

    assert_eq!(min_priority_msg.priority, Priority::Numeric(0));
    assert_eq!(
        max_priority_msg.priority,
        Priority::Numeric(18_446_744_073_709_551_615)
    );

    // Both should serialize/deserialize correctly
    let min_json = serde_json::to_string(&min_priority_msg).unwrap();
    let max_json = serde_json::to_string(&max_priority_msg).unwrap();

    let min_deserialized: Message = serde_json::from_str(&min_json).unwrap();
    let max_deserialized: Message = serde_json::from_str(&max_json).unwrap();

    assert_eq!(min_deserialized.priority, Priority::Numeric(0));
    assert_eq!(max_deserialized.priority, Priority::Numeric(u64::MAX));
}

#[test]
fn test_message_retry_count_extremes() {
    // Test with high retry counts
    let high_retry_msg = Message {
        id: "retry-test".to_string(),
        queue: "test".to_string(),
        priority: Priority::Numeric(100),
        payload: "retry test".to_string(),
        created_at: Utc::now(),
        locked_until: None,
        locked_by: None,
        retry_count: u32::MAX,
        max_retries: u32::MAX,
        payload_ref: None,
        payload_hash: None,
    };

    assert_eq!(high_retry_msg.retry_count, u32::MAX);
    assert_eq!(high_retry_msg.max_retries, u32::MAX);

    // Should serialize/deserialize correctly
    let json = serde_json::to_string(&high_retry_msg).unwrap();
    let deserialized: Message = serde_json::from_str(&json).unwrap();

    assert_eq!(deserialized.retry_count, u32::MAX);
    assert_eq!(deserialized.max_retries, u32::MAX);
}

#[test]
fn test_priority_ordering_enum() {
    // Test enum values
    let max_first = PriorityOrdering::MaxFirst;
    let min_first = PriorityOrdering::MinFirst;

    assert_ne!(max_first, min_first);

    // Test serialization
    let max_json = serde_json::to_string(&max_first).unwrap();
    let min_json = serde_json::to_string(&min_first).unwrap();

    assert_eq!(max_json, "\"MaxFirst\"");
    assert_eq!(min_json, "\"MinFirst\"");

    // Test deserialization
    let max_deserialized: PriorityOrdering = serde_json::from_str(&max_json).unwrap();
    let min_deserialized: PriorityOrdering = serde_json::from_str(&min_json).unwrap();

    assert_eq!(max_deserialized, PriorityOrdering::MaxFirst);
    assert_eq!(min_deserialized, PriorityOrdering::MinFirst);
}

#[test]
fn test_priority_ordering_default() {
    let default_ordering = PriorityOrdering::default();
    assert_eq!(default_ordering, PriorityOrdering::MaxFirst);
}

#[test]
fn test_priority_ordering_debug_clone() {
    let ordering = PriorityOrdering::MinFirst;
    let cloned = ordering;
    assert_eq!(ordering, cloned);

    let debug_str = format!("{:?}", ordering);
    assert!(debug_str.contains("MinFirst"));
}

#[test]
fn test_queue_config_creation() {
    let max_config = QueueConfig {
        ordering: PriorityOrdering::MaxFirst,
        ..Default::default()
    };
    let min_config = QueueConfig {
        ordering: PriorityOrdering::MinFirst,
        ..Default::default()
    };

    assert_eq!(max_config.ordering, PriorityOrdering::MaxFirst);
    assert_eq!(min_config.ordering, PriorityOrdering::MinFirst);
}

#[test]
fn test_queue_config_default() {
    let default_config = QueueConfig::default();
    assert_eq!(default_config.ordering, PriorityOrdering::MaxFirst);
}

#[test]
fn test_queue_config_serialization() {
    let config = QueueConfig {
        ordering: PriorityOrdering::MinFirst,
        ..Default::default()
    };

    // Serialize to JSON
    let json = serde_json::to_string(&config).unwrap();
    assert!(json.contains("MinFirst"));

    // Deserialize from JSON
    let deserialized: QueueConfig = serde_json::from_str(&json).unwrap();
    assert_eq!(deserialized.ordering, PriorityOrdering::MinFirst);
}

#[test]
fn test_queue_config_clone_debug() {
    let original = QueueConfig {
        ordering: PriorityOrdering::MaxFirst,
        ..Default::default()
    };

    let cloned = original.clone();
    assert_eq!(cloned.ordering, original.ordering);

    let debug_str = format!("{:?}", original);
    assert!(debug_str.contains("QueueConfig"));
    assert!(debug_str.contains("MaxFirst"));
}

#[test]
fn test_queue_config_with_different_orderings() {
    let configs = vec![
        QueueConfig {
            ordering: PriorityOrdering::MaxFirst,
            ..Default::default()
        },
        QueueConfig {
            ordering: PriorityOrdering::MinFirst,
            ..Default::default()
        },
    ];

    // Test that different configs are indeed different
    assert_ne!(configs[0].ordering, configs[1].ordering);

    // Test serialization/deserialization round trip
    for config in configs {
        let json = serde_json::to_string(&config).unwrap();
        let deserialized: QueueConfig = serde_json::from_str(&json).unwrap();
        assert_eq!(config.ordering, deserialized.ordering);
    }
}

#[test]
fn test_priority_display() {
    assert_eq!(format!("{}", Priority::Numeric(42)), "42");
    assert_eq!(format!("{}", Priority::Text("high".to_string())), "high");
}

#[test]
fn test_priority_kind() {
    assert_eq!(Priority::Numeric(0).kind(), PriorityKind::Numeric);
    assert_eq!(Priority::Text("a".to_string()).kind(), PriorityKind::Text);
}

#[test]
fn test_priority_kind_default() {
    assert_eq!(PriorityKind::default(), PriorityKind::Numeric);
}

#[test]
fn test_priority_default() {
    assert_eq!(Priority::default(), Priority::Numeric(0));
}

#[test]
fn test_payload_ref_and_hash_serde_roundtrip() {
    use qrusty::message::PayloadRef;

    let msg = Message {
        id: "serde-test".to_string(),
        queue: "q".to_string(),
        priority: Priority::Numeric(1),
        payload: String::new(),
        created_at: Utc::now(),
        locked_until: None,
        locked_by: None,
        retry_count: 0,
        max_retries: 3,
        payload_ref: Some(PayloadRef {
            file_id: 1,
            offset: 100,
            length: 50,
        }),
        payload_hash: Some([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]),
    };

    let json = serde_json::to_string(&msg).unwrap();
    assert!(json.contains("payload_ref"));
    assert!(json.contains("payload_hash"));

    let deserialized: Message = serde_json::from_str(&json).unwrap();
    assert_eq!(deserialized.payload_ref, msg.payload_ref);
    assert_eq!(deserialized.payload_hash, msg.payload_hash);
}

#[test]
fn test_payload_ref_and_hash_omitted_when_none() {
    let msg = Message {
        id: "no-ref".to_string(),
        queue: "q".to_string(),
        priority: Priority::Numeric(1),
        payload: "inline".to_string(),
        created_at: Utc::now(),
        locked_until: None,
        locked_by: None,
        retry_count: 0,
        max_retries: 3,
        payload_ref: None,
        payload_hash: None,
    };

    let json = serde_json::to_string(&msg).unwrap();
    // Fields should be omitted entirely when None (skip_serializing_if).
    assert!(!json.contains("payload_ref"));
    assert!(!json.contains("payload_hash"));

    // Deserializing without the fields should produce None.
    let deserialized: Message = serde_json::from_str(&json).unwrap();
    assert!(deserialized.payload_ref.is_none());
    assert!(deserialized.payload_hash.is_none());
}
