#[cfg(test)]
mod tests {
    use regenerator2000::mcp::handler::handle_request;
    use regenerator2000::mcp::types::McpRequest;
    use regenerator2000::state::AppState;
    use regenerator2000::theme::Theme;
    use regenerator2000::ui_state::UIState;
    use serde_json::json;
    use tokio::sync::oneshot;

    fn call_tool(
        app_state: &mut AppState,
        ui_state: &mut UIState,
        name: &str,
        args: serde_json::Value,
    ) -> serde_json::Value {
        let (tx, _rx) = oneshot::channel();
        let req = McpRequest {
            method: "tools/call".to_string(),
            params: json!({
                "name": name,
                "arguments": args
            }),
            response_sender: tx,
        };

        let response = handle_request(&req, app_state, ui_state);
        let result = response
            .result
            .unwrap_or_else(|| panic!("Tool {} failed: {:?}", name, response.error));
        let content = result["content"].as_array().unwrap();
        let text = content[0]["text"].as_str().unwrap();
        serde_json::from_str(text).unwrap()
    }

    #[test]
    fn test_block_details() {
        let mut app_state = AppState::default();
        let origin = 0x1000;
        // LDA #$00 (A9 00)
        // STA $D020 (8D 20 D0)
        // JMP $1000 (4C 00 10)
        let data = vec![0xA9, 0x00, 0x8D, 0x20, 0xD0, 0x4C, 0x00, 0x10];
        app_state.load_binary(origin, data).unwrap();

        // Add some metadata
        app_state.labels.insert(
            0x1000,
            vec![regenerator2000::state::Label {
                name: "start".to_string(),
                kind: regenerator2000::state::LabelKind::User,
                label_type: regenerator2000::state::LabelType::UserDefined,
            }],
        );
        app_state
            .user_side_comments
            .insert(0x1000, "Start of loop".to_string());

        let mut ui_state = UIState::new(Theme::default());

        // Test 1: Code block at $1002 (STA $D020)
        let details = call_tool(
            &mut app_state,
            &mut ui_state,
            "r2000_get_address_details",
            json!({ "address": 0x1002 }),
        );
        println!(
            "Details 1002: {}",
            serde_json::to_string_pretty(&details).unwrap()
        );

        assert_eq!(details["address"], 0x1002);
        assert_eq!(details["type"], "Code");
        assert_eq!(details["instruction"]["mnemonic"], "STA");
        assert_eq!(details["metadata"]["referenced_address"], 0xD020);

        // Test 2: Code block at $1005 (JMP $1000)
        let details = call_tool(
            &mut app_state,
            &mut ui_state,
            "r2000_get_address_details",
            json!({ "address": 0x1005 }),
        );
        println!(
            "Details 1005: {}",
            serde_json::to_string_pretty(&details).unwrap()
        );

        assert_eq!(details["address"], 0x1005);
        assert_eq!(details["instruction"]["mnemonic"], "JMP");
        assert_eq!(details["metadata"]["target_address"], 0x1000);

        // Test 3: Metadata at $1000
        let details = call_tool(
            &mut app_state,
            &mut ui_state,
            "r2000_get_address_details",
            json!({ "address": 0x1000 }),
        );
        println!(
            "Details 1000: {}",
            serde_json::to_string_pretty(&details).unwrap()
        );

        assert_eq!(details["address"], 0x1000);
        assert_eq!(details["instruction"]["mnemonic"], "LDA");

        let labels = details["metadata"]["labels"].as_array().unwrap();
        assert!(labels.iter().any(|v| v.as_str() == Some("start")));

        let comments = details["metadata"]["comments"].as_array().unwrap();
        assert!(
            comments
                .iter()
                .any(|v| v.as_str().unwrap().contains("Start of loop"))
        );

        // Check Incoming Refs (JMP $1000 should create a ref to $1000)
        // Wait, cross_refs are generated by analyzer or disassembler?
        // In load_binary, we called disassemble() but maybe not full analysis unless auto_analyse is on?
        // AppState::load_binary calls disassemble() twice and perform_analysis() if config.auto_analyze.
        // Default config might not have auto_analyze?
        // Let's force analysis or just check if it was generated.
        // references are usually generated during analysis phase.

        // Let's manually trigger analysis if needed, but AppState::default() might not have it.
        // Actually, let's just assert that if refs are present, they are correct.
        if let Some(refs) = details["metadata"]["cross_refs_in"].as_array() {
            assert!(refs.iter().any(|v| v.as_u64() == Some(0x1005)));
        } else {
            println!("No cross refs generated (expected if analysis didn't run)");
            // Force analysis to verification
            app_state.perform_analysis();
            let details_after = call_tool(
                &mut app_state,
                &mut ui_state,
                "r2000_get_address_details",
                json!({ "address": 0x1000 }),
            );
            let refs = details_after["metadata"]["cross_refs_in"]
                .as_array()
                .expect("Cross refs should exist after analysis");
            assert!(refs.iter().any(|v| v.as_u64() == Some(0x1005)));
        }
    }
}
