use dirigent_protocol::adapters::OpenCodeAdapter;
use dirigent_protocol::{Event, Message, MessagePart, MessageRole, MessageStatus, Session};
use opencode_client::types as oc;

#[test]
fn test_parse_opencode_events() {
    // Load sample events from fixture
    let fixture = include_str!("fixtures/sample_events.jsonl");

    for (idx, line) in fixture.lines().enumerate() {
        let result = serde_json::from_str::<oc::Event>(line);
        assert!(
            result.is_ok(),
            "Failed to parse OpenCode event at line {}: {:?}",
            idx + 1,
            result.err()
        );
    }
}

#[test]
fn test_translate_server_connected() {
    let adapter = OpenCodeAdapter::new();
    let oc_event = oc::Event::ServerConnected {
        properties: serde_json::json!({}),
    };

    let result = adapter.translate_event(oc_event);
    assert!(result.is_ok());
    assert!(matches!(result.unwrap(), Event::Connected));
}

#[test]
fn test_translate_session_created() {
    let adapter = OpenCodeAdapter::new();
    let oc_session = oc::Session {
        id: "ses_test123".to_string(),
        project_id: "test_project".to_string(),
        directory: "/test/path".to_string(),
        parent_id: None,
        summary: None,
        share: None,
        title: "Test Session".to_string(),
        version: "0.15.31".to_string(),
        time: oc::SessionTime {
            created: 1700000000000,
            updated: 1700000000000,
            compacting: None,
        },
        revert: None,
    };

    let oc_event = oc::Event::SessionCreated {
        properties: oc::SessionEventInfo {
            info: oc_session.clone(),
        },
    };

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

    match result.unwrap() {
        Event::SessionCreated {
            connector_id: _,
            session,
        } => {
            assert_eq!(session.id, "ses_test123");
            assert_eq!(session.title, "Test Session");
            assert_eq!(session.metadata.project_path, "/test/path");
        }
        _ => panic!("Expected SessionCreated event"),
    }
}

#[test]
fn test_translate_user_message() {
    let adapter = OpenCodeAdapter::new();
    let oc_message = oc::Message::User(oc::UserMessage {
        id: "msg_user1".to_string(),
        session_id: "ses_test123".to_string(),
        time: oc::MessageTime {
            created: 1700000001000,
        },
        summary: None,
    });

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

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

    match result.unwrap() {
        Event::MessageCompleted {
            connector_id: _,
            message,
        } => {
            assert_eq!(message.id, "msg_user1");
            assert_eq!(message.session_id, "ses_test123");
            assert!(matches!(message.role, MessageRole::User));
            assert!(matches!(message.status, MessageStatus::Completed));
        }
        _ => panic!("Expected MessageCompleted event for user message"),
    }
}

#[test]
fn test_translate_assistant_message_streaming() {
    let oc_message = oc::Message::Assistant(oc::AssistantMessage {
        id: "msg_asst1".to_string(),
        session_id: "ses_test123".to_string(),
        time: oc::AssistantMessageTime {
            created: 1700000002000,
            completed: None, // Still streaming
        },
        error: None,
        system: vec![],
        parent_id: Some("msg_user1".to_string()),
        model_id: Some("gpt-4".to_string()),
        provider_id: Some("openai".to_string()),
        mode: None,
        path: None,
        summary: None,
        cost: 0.05,
        tokens: Default::default(),
    });

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

    let adapter = OpenCodeAdapter::new();
    let result = adapter.translate_event(oc_event);
    assert!(result.is_ok());

    match result.unwrap() {
        Event::MessageStarted {
            connector_id: _,
            message,
        } => {
            assert_eq!(message.id, "msg_asst1");
            assert!(matches!(message.role, MessageRole::Assistant));
            assert!(matches!(message.status, MessageStatus::Streaming));
        }
        _ => panic!("Expected MessageStarted event for streaming message"),
    }
}

#[test]
fn test_translate_assistant_message_completed() {
    let oc_message = oc::Message::Assistant(oc::AssistantMessage {
        id: "msg_asst1".to_string(),
        session_id: "ses_test123".to_string(),
        time: oc::AssistantMessageTime {
            created: 1700000002000,
            completed: Some(1700000005000), // Completed
        },
        error: None,
        system: vec![],
        parent_id: Some("msg_user1".to_string()),
        model_id: Some("gpt-4".to_string()),
        provider_id: Some("openai".to_string()),
        mode: None,
        path: None,
        summary: None,
        cost: 0.05,
        tokens: Default::default(),
    });

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

    let adapter = OpenCodeAdapter::new();
    let result = adapter.translate_event(oc_event);
    assert!(result.is_ok());

    match result.unwrap() {
        Event::MessageCompleted {
            connector_id: _,
            message,
        } => {
            assert_eq!(message.id, "msg_asst1");
            assert!(matches!(message.role, MessageRole::Assistant));
            assert!(matches!(message.status, MessageStatus::Completed));
        }
        _ => panic!("Expected MessageCompleted event"),
    }
}

#[test]
fn test_translate_text_part() {
    let oc_part = oc::Part::Text(oc::TextPart {
        id: "prt_text1".to_string(),
        session_id: "ses_test123".to_string(),
        message_id: "msg_user1".to_string(),
        text: "Hello, can you help me?".to_string(),
        synthetic: Some(false),
        time: None,
    });

    let oc_event = oc::Event::MessagePartUpdated {
        properties: oc::MessagePartEventInfo {
            part: oc_part,
            delta: None,
        },
    };

    let adapter = OpenCodeAdapter::new();
    let result = adapter.translate_event(oc_event);
    assert!(result.is_ok());

    match result.unwrap() {
        Event::SessionUpdate {
            connector_id: _,
            session_id,
            update,
        } => {
            assert_eq!(session_id, "ses_test123");
            match update {
                dirigent_protocol::SessionUpdate::AgentMessageChunk {
                    message_id,
                    content,
                    ..
                } => {
                    assert_eq!(message_id, "msg_user1");
                    match content {
                        dirigent_protocol::ContentBlock::Text { text } => {
                            assert_eq!(text, "Hello, can you help me?");
                        }
                        _ => panic!("Expected Text content block"),
                    }
                }
                _ => panic!("Expected AgentMessageChunk"),
            }
        }
        _ => panic!("Expected SessionUpdate event"),
    }
}

#[test]
fn test_translate_reasoning_part() {
    let oc_part = oc::Part::Reasoning(oc::ReasoningPart {
        id: "prt_reasoning1".to_string(),
        session_id: "ses_test123".to_string(),
        message_id: "msg_asst1".to_string(),
        text: "Let me think about this...".to_string(),
        time: None,
    });

    let oc_event = oc::Event::MessagePartUpdated {
        properties: oc::MessagePartEventInfo {
            part: oc_part,
            delta: Some(" more".to_string()),
        },
    };

    let adapter = OpenCodeAdapter::new();
    let result = adapter.translate_event(oc_event);
    assert!(result.is_ok());

    match result.unwrap() {
        Event::SessionUpdate {
            connector_id: _,
            session_id,
            update,
        } => {
            assert_eq!(session_id, "ses_test123");
            match update {
                dirigent_protocol::SessionUpdate::AgentThoughtChunk {
                    message_id,
                    content,
                    ..
                } => {
                    assert_eq!(message_id, "msg_asst1");
                    match content {
                        dirigent_protocol::ContentBlock::Text { text } => {
                            // Should use delta, not full text
                            assert_eq!(text, " more");
                        }
                        _ => panic!("Expected Text content block"),
                    }
                }
                _ => panic!("Expected AgentThoughtChunk"),
            }
        }
        _ => panic!("Expected SessionUpdate event"),
    }
}

#[test]
fn test_translate_tool_part_completed() {
    let oc_part = oc::Part::Tool(oc::ToolPart {
        id: "prt_tool1".to_string(),
        session_id: "ses_test123".to_string(),
        message_id: "msg_asst1".to_string(),
        call_id: "call_123".to_string(),
        tool: "Read".to_string(),
        state: oc::ToolState::Completed {
            input: serde_json::json!({"file_path": "/test/file.txt"}),
            output: "File contents here".to_string(),
            title: "Reading file".to_string(),
            metadata: serde_json::json!({}),
            time: oc::PartTime {
                start: 1700000003000,
                end: Some(1700000004000),
            },
            attachments: None,
        },
        metadata: None,
    });

    let oc_event = oc::Event::MessagePartUpdated {
        properties: oc::MessagePartEventInfo {
            part: oc_part,
            delta: None,
        },
    };

    let adapter = OpenCodeAdapter::new();
    let result = adapter.translate_event(oc_event);
    assert!(result.is_ok());

    match result.unwrap() {
        Event::SessionUpdate {
            connector_id: _,
            session_id,
            update,
        } => {
            assert_eq!(session_id, "ses_test123");
            match update {
                dirigent_protocol::SessionUpdate::ToolCall {
                    message_id,
                    tool_call,
                    ..
                } => {
                    assert_eq!(message_id, "msg_asst1");
                    assert_eq!(tool_call.tool_name, "Read");
                    assert_eq!(
                        tool_call.status,
                        dirigent_protocol::ToolCallStatus::Completed
                    );
                    assert!(tool_call.raw_input.is_some());
                    assert_eq!(
                        tool_call.raw_input.unwrap().get("file_path").unwrap(),
                        "/test/file.txt"
                    );
                    assert!(tool_call.raw_output.is_some());
                    assert_eq!(
                        tool_call.raw_output.unwrap().as_str().unwrap(),
                        "File contents here"
                    );
                }
                _ => panic!("Expected ToolCall"),
            }
        }
        _ => panic!("Expected SessionUpdate event"),
    }
}

#[test]
fn test_full_event_stream() {
    let fixture = include_str!("fixtures/sample_events.jsonl");
    let adapter = OpenCodeAdapter::new(); // One adapter for entire stream
    let mut session_created = false;
    let mut message_count = 0;
    let mut part_count = 0;

    for line in fixture.lines() {
        let oc_event: oc::Event =
            serde_json::from_str(line).expect("Failed to parse OpenCode event");
        let result = adapter.translate_event(oc_event);

        match result {
            Ok(Event::SessionCreated { .. }) => session_created = true,
            Ok(Event::MessageStarted { .. }) | Ok(Event::MessageCompleted { .. }) => {
                message_count += 1
            }
            Ok(Event::SessionUpdate { .. }) => part_count += 1,
            Err(_) => {} // Some events might not translate (e.g., unknown types) or duplicates
            _ => {}
        }
    }

    assert!(session_created, "Session should be created");
    assert!(message_count > 0, "Should have messages");
    assert!(part_count > 0, "Should have message parts");
}

#[test]
fn test_dirigent_protocol_serialization() {
    // Test that Dirigent protocol types can be serialized and deserialized
    let session = Session {
        id: "ses_test".to_string(),
        title: "Test".to_string(),
        created_at: chrono::Utc::now(),

        updated_at: chrono::Utc::now(),

        metadata: dirigent_protocol::SessionMetadata {
            project_path: "/test".to_string(),

            model: Some("gpt-4".to_string()),

            total_messages: 5,

            system_message: None,

            current_mode_id: None,

            _meta: None,

            project_id: None,
        },
        cwd: None,
        models: None,
        modes: None,
        config_options: None,
        acp_client_id: None,
    };

    let json = serde_json::to_string(&session).expect("Failed to serialize");
    let deserialized: Session = serde_json::from_str(&json).expect("Failed to deserialize");

    assert_eq!(session, deserialized);
}

#[test]

fn test_message_protocol_serialization() {
    let message = Message {
        id: "msg_test".to_string(),

        session_id: "ses_test".to_string(),

        role: MessageRole::Assistant,

        created_at: chrono::Utc::now(),

        content: vec![
            MessagePart::Text {
                text: "Hello".to_string(),
            },
            MessagePart::Tool {
                tool: "Read".to_string(),
                tool_call_id: None,
                input: serde_json::json!({"file": "test.txt"}),

                output: Some(serde_json::json!("content")),
            },
        ],

        status: MessageStatus::Completed,

        metadata: None,
    };

    let json = serde_json::to_string(&message).expect("Failed to serialize");

    let deserialized: Message = serde_json::from_str(&json).expect("Failed to deserialize");

    assert_eq!(message, deserialized);
}
