use dirigent_protocol::adapters::{OpenCodeAdapter, TranslationError};
use dirigent_protocol::Event;
use opencode_client::types as oc;

/// Test that duplicate MessageStarted events are filtered
#[test]
fn test_duplicate_message_started_filtered() {
    let adapter = OpenCodeAdapter::new();

    // First message.updated event (streaming)
    let oc_message = oc::Message::Assistant(oc::AssistantMessage {
        id: "msg_test1".to_string(),
        session_id: "ses_test".to_string(),
        time: oc::AssistantMessageTime {
            created: 1700000000000,
            completed: None,
        },
        error: None,
        system: vec![],
        parent_id: None,
        model_id: Some("gpt-4".to_string()),
        provider_id: Some("openai".to_string()),
        mode: None,
        path: None,
        summary: None,
        cost: 0.0,
        tokens: Default::default(),
    });

    let event1 = oc::Event::MessageUpdated {
        properties: oc::MessageEventInfo {
            info: oc_message.clone(),
        },
    };

    // First event should succeed
    let result1 = adapter.translate_event(event1);
    assert!(result1.is_ok());
    assert!(matches!(result1.unwrap(), Event::MessageStarted { .. }));

    // Second identical event should be filtered as duplicate
    let event2 = oc::Event::MessageUpdated {
        properties: oc::MessageEventInfo { info: oc_message },
    };

    let result2 = adapter.translate_event(event2);
    assert!(result2.is_err());
    assert!(matches!(result2.unwrap_err(), TranslationError::Duplicate));
}

/// Test that duplicate MessageCompleted events are filtered
#[test]
fn test_duplicate_message_completed_filtered() {
    let adapter = OpenCodeAdapter::new();

    // Completed message
    let oc_message = oc::Message::Assistant(oc::AssistantMessage {
        id: "msg_test2".to_string(),
        session_id: "ses_test".to_string(),
        time: oc::AssistantMessageTime {
            created: 1700000000000,
            completed: Some(1700000005000),
        },
        error: None,
        system: vec![],
        parent_id: None,
        model_id: Some("gpt-4".to_string()),
        provider_id: Some("openai".to_string()),
        mode: None,
        path: None,
        summary: None,
        cost: 0.0,
        tokens: Default::default(),
    });

    let event1 = oc::Event::MessageUpdated {
        properties: oc::MessageEventInfo {
            info: oc_message.clone(),
        },
    };

    // First completed event should succeed
    let result1 = adapter.translate_event(event1);
    assert!(result1.is_ok());
    assert!(matches!(result1.unwrap(), Event::MessageCompleted { .. }));

    // Second identical completed event should be filtered
    let event2 = oc::Event::MessageUpdated {
        properties: oc::MessageEventInfo { info: oc_message },
    };

    let result2 = adapter.translate_event(event2);
    assert!(result2.is_err());
    assert!(matches!(result2.unwrap_err(), TranslationError::Duplicate));
}

/// Test that part updates with delta: None are filtered after first occurrence
#[test]
fn test_duplicate_part_completion_filtered() {
    let adapter = OpenCodeAdapter::new();

    // First part update with delta (streaming)
    let part = oc::Part::Text(oc::TextPart {
        id: "prt_test1".to_string(),
        session_id: "ses_test".to_string(),
        message_id: "msg_test".to_string(),
        text: "Hello world".to_string(),
        synthetic: None,
        time: None,
    });

    let event1 = oc::Event::MessagePartUpdated {
        properties: oc::MessagePartEventInfo {
            part: part.clone(),
            delta: Some("Hello".to_string()),
        },
    };

    // First event with delta should succeed
    let result1 = adapter.translate_event(event1);
    assert!(result1.is_ok());
    assert!(matches!(result1.unwrap(), Event::SessionUpdate { .. }));

    // Second event for same part without delta (completion signal) should be filtered
    let event2 = oc::Event::MessagePartUpdated {
        properties: oc::MessagePartEventInfo {
            part,
            delta: None,
        },
    };

    let result2 = adapter.translate_event(event2);
    assert!(result2.is_err());
    assert!(matches!(result2.unwrap_err(), TranslationError::Duplicate));
}

/// Test that different parts with same message are NOT filtered
#[test]
fn test_different_parts_not_filtered() {
    let adapter = OpenCodeAdapter::new();

    // Reasoning part
    let reasoning_part = oc::Part::Reasoning(oc::ReasoningPart {
        id: "prt_reasoning".to_string(),
        session_id: "ses_test".to_string(),
        message_id: "msg_test".to_string(),
        text: "Let me think...".to_string(),
        time: None,
    });

    let event1 = oc::Event::MessagePartUpdated {
        properties: oc::MessagePartEventInfo {
            part: reasoning_part,
            delta: Some("Let me".to_string()),
        },
    };

    let result1 = adapter.translate_event(event1);
    assert!(result1.is_ok());
    assert!(matches!(result1.unwrap(), Event::SessionUpdate { .. }));

    // Text part (different part, same message)
    let text_part = oc::Part::Text(oc::TextPart {
        id: "prt_text".to_string(),
        session_id: "ses_test".to_string(),
        message_id: "msg_test".to_string(),
        text: "Answer".to_string(),
        synthetic: None,
        time: None,
    });

    let event2 = oc::Event::MessagePartUpdated {
        properties: oc::MessagePartEventInfo {
            part: text_part,
            delta: Some("Answer".to_string()),
        },
    };

    // Different part should not be filtered
    let result2 = adapter.translate_event(event2);
    assert!(result2.is_ok());
    assert!(matches!(result2.unwrap(), Event::SessionUpdate { .. }));
}

/// Test streaming part updates are not filtered (same part_id, different delta)
#[test]
fn test_streaming_part_updates_not_filtered() {
    let adapter = OpenCodeAdapter::new();

    // First update
    let part1 = oc::Part::Text(oc::TextPart {
        id: "prt_streaming".to_string(),
        session_id: "ses_test".to_string(),
        message_id: "msg_test".to_string(),
        text: "Hello".to_string(),
        synthetic: None,
        time: None,
    });

    let event1 = oc::Event::MessagePartUpdated {
        properties: oc::MessagePartEventInfo {
            part: part1,
            delta: Some("Hello".to_string()),
        },
    };

    let result1 = adapter.translate_event(event1);
    assert!(result1.is_ok());

    // Second update with more text
    let part2 = oc::Part::Text(oc::TextPart {
        id: "prt_streaming".to_string(),
        session_id: "ses_test".to_string(),
        message_id: "msg_test".to_string(),
        text: "Hello world".to_string(),
        synthetic: None,
        time: None,
    });

    let event2 = oc::Event::MessagePartUpdated {
        properties: oc::MessagePartEventInfo {
            part: part2,
            delta: Some(" world".to_string()),
        },
    };

    // Second update with delta should NOT be filtered (streaming update)
    let result2 = adapter.translate_event(event2);
    assert!(result2.is_ok());
    assert!(matches!(result2.unwrap(), Event::SessionUpdate { .. }));
}

/// Test full tit-tat flow with proper deduplication
#[test]
fn test_tit_tat_flow() {
    let adapter = OpenCodeAdapter::new();

    // 1. User message arrives (completed)
    let user_msg = oc::Message::User(oc::UserMessage {
        id: "msg_user".to_string(),
        session_id: "ses_test".to_string(),
        time: oc::MessageTime {
            created: 1700000000000,
        },
        summary: None,
    });

    let user_event = oc::Event::MessageUpdated {
        properties: oc::MessageEventInfo { info: user_msg },
    };

    let result = adapter.translate_event(user_event);
    assert!(result.is_ok());
    assert!(matches!(result.unwrap(), Event::MessageCompleted { .. }));

    // 2. Assistant message starts streaming
    let asst_msg_streaming = oc::Message::Assistant(oc::AssistantMessage {
        id: "msg_asst".to_string(),
        session_id: "ses_test".to_string(),
        time: oc::AssistantMessageTime {
            created: 1700000001000,
            completed: None,
        },
        error: None,
        system: vec![],
        parent_id: Some("msg_user".to_string()),
        model_id: Some("grok-code".to_string()),
        provider_id: Some("opencode".to_string()),
        mode: None,
        path: None,
        summary: None,
        cost: 0.0,
        tokens: Default::default(),
    });

    let asst_start_event = oc::Event::MessageUpdated {
        properties: oc::MessageEventInfo {
            info: asst_msg_streaming,
        },
    };

    let result = adapter.translate_event(asst_start_event);
    assert!(result.is_ok());
    assert!(matches!(result.unwrap(), Event::MessageStarted { .. }));

    // 3. Reasoning part streams
    let reasoning_part_1 = oc::Part::Reasoning(oc::ReasoningPart {
        id: "prt_reasoning".to_string(),
        session_id: "ses_test".to_string(),
        message_id: "msg_asst".to_string(),
        text: "First".to_string(),
        time: None,
    });

    let reasoning_event_1 = oc::Event::MessagePartUpdated {
        properties: oc::MessagePartEventInfo {
            part: reasoning_part_1,
            delta: Some("First".to_string()),
        },
    };

    let result = adapter.translate_event(reasoning_event_1);
    assert!(result.is_ok());

    // More reasoning updates...
    let reasoning_part_2 = oc::Part::Reasoning(oc::ReasoningPart {
        id: "prt_reasoning".to_string(),
        session_id: "ses_test".to_string(),
        message_id: "msg_asst".to_string(),
        text: "First, the user is saying \"tit?\"".to_string(),
        time: None,
    });

    let reasoning_event_2 = oc::Event::MessagePartUpdated {
        properties: oc::MessagePartEventInfo {
            part: reasoning_part_2,
            delta: Some(", the user is saying \"tit?\"".to_string()),
        },
    };

    let result = adapter.translate_event(reasoning_event_2);
    assert!(result.is_ok());

    // 4. Reasoning completes (delta: None) - should be filtered
    let reasoning_part_complete = oc::Part::Reasoning(oc::ReasoningPart {
        id: "prt_reasoning".to_string(),
        session_id: "ses_test".to_string(),
        message_id: "msg_asst".to_string(),
        text: "First, the user is saying \"tit?\" which seems like they're continuing the game.".to_string(),
        time: None,
    });

    let reasoning_complete_event = oc::Event::MessagePartUpdated {
        properties: oc::MessagePartEventInfo {
            part: reasoning_part_complete,
            delta: None, // Completion signal
        },
    };

    let result = adapter.translate_event(reasoning_complete_event);
    assert!(result.is_err()); // Should be filtered as duplicate
    assert!(matches!(result.unwrap_err(), TranslationError::Duplicate));

    // 5. Text part starts streaming
    let text_part_1 = oc::Part::Text(oc::TextPart {
        id: "prt_text".to_string(),
        session_id: "ses_test".to_string(),
        message_id: "msg_asst".to_string(),
        text: "tat".to_string(),
        synthetic: None,
        time: None,
    });

    let text_event_1 = oc::Event::MessagePartUpdated {
        properties: oc::MessagePartEventInfo {
            part: text_part_1,
            delta: Some("tat".to_string()),
        },
    };

    let result = adapter.translate_event(text_event_1);
    assert!(result.is_ok());

    // 6. Text completes (delta: None) - should be filtered
    let text_part_complete = oc::Part::Text(oc::TextPart {
        id: "prt_text".to_string(),
        session_id: "ses_test".to_string(),
        message_id: "msg_asst".to_string(),
        text: "tat".to_string(),
        synthetic: None,
        time: None,
    });

    let text_complete_event = oc::Event::MessagePartUpdated {
        properties: oc::MessagePartEventInfo {
            part: text_part_complete,
            delta: None, // Completion signal
        },
    };

    let result = adapter.translate_event(text_complete_event);
    assert!(result.is_err()); // Should be filtered as duplicate
    assert!(matches!(result.unwrap_err(), TranslationError::Duplicate));

    // 7. Message completes
    let asst_msg_complete = oc::Message::Assistant(oc::AssistantMessage {
        id: "msg_asst".to_string(),
        session_id: "ses_test".to_string(),
        time: oc::AssistantMessageTime {
            created: 1700000001000,
            completed: Some(1700000010000),
        },
        error: None,
        system: vec![],
        parent_id: Some("msg_user".to_string()),
        model_id: Some("grok-code".to_string()),
        provider_id: Some("opencode".to_string()),
        mode: None,
        path: None,
        summary: None,
        cost: 0.0,
        tokens: Default::default(),
    });

    let asst_complete_event = oc::Event::MessageUpdated {
        properties: oc::MessageEventInfo {
            info: asst_msg_complete,
        },
    };

    let result = adapter.translate_event(asst_complete_event);
    assert!(result.is_ok());
    assert!(matches!(result.unwrap(), Event::MessageCompleted { .. }));
}

/// Test adapter state is independent across instances
#[test]
fn test_adapter_state_independence() {
    let adapter1 = OpenCodeAdapter::new();
    let adapter2 = OpenCodeAdapter::new();

    let oc_message = oc::Message::Assistant(oc::AssistantMessage {
        id: "msg_test".to_string(),
        session_id: "ses_test".to_string(),
        time: oc::AssistantMessageTime {
            created: 1700000000000,
            completed: None,
        },
        error: None,
        system: vec![],
        parent_id: None,
        model_id: Some("gpt-4".to_string()),
        provider_id: Some("openai".to_string()),
        mode: None,
        path: None,
        summary: None,
        cost: 0.0,
        tokens: Default::default(),
    });

    let event1 = oc::Event::MessageUpdated {
        properties: oc::MessageEventInfo {
            info: oc_message.clone(),
        },
    };

    let event2 = oc::Event::MessageUpdated {
        properties: oc::MessageEventInfo { info: oc_message },
    };

    // First adapter processes event
    let result1 = adapter1.translate_event(event1);
    assert!(result1.is_ok());

    // Second adapter (with independent state) should also process successfully
    let result2 = adapter2.translate_event(event2);
    assert!(result2.is_ok());
}
