//! Integration tests for the datamock C API.

use datamock::*;
use std::ffi::CStr;
use std::ptr;
use std::sync::Mutex;

/// The C++ mock uses global state (e.g. `Name` table); serialize API tests.
static C_API_TESTS: Mutex<()> = Mutex::new(());

fn c_api_guard() -> std::sync::MutexGuard<'static, ()> {
    C_API_TESTS
        .lock()
        .unwrap_or_else(|poisoned| poisoned.into_inner())
}

#[test]
fn test_session_lifecycle() {
    let _guard = c_api_guard();
    unsafe {
        // Create session options
        let options = datamock_SessionOptions_create();
        assert!(!options.is_null());

        // Create session (sync mode, no handler)
        let session = datamock_Session_create(options, None, ptr::null_mut());
        assert!(!session.is_null());

        // Start session - BEmu may return false but still work
        let result = datamock_Session_start(session);
        println!("Session start result: {}", result);
        // Don't assert on start result, BEmu may return false

        // Open refdata service
        let service_uri = c"//blp/refdata";
        let result = datamock_Session_openService(session, service_uri.as_ptr());
        println!("Open service result: {}", result);

        // Get service
        let mut service: *mut datamock_Service_t = ptr::null_mut();
        let result = datamock_Session_getService(session, &mut service, service_uri.as_ptr());
        println!("Get service result: {}", result);

        // Cleanup
        datamock_Session_stop(session);
        datamock_Session_destroy(session);
        datamock_SessionOptions_destroy(options);
    }
}

#[test]
fn test_historical_data_request() {
    let _guard = c_api_guard();
    unsafe {
        let options = datamock_SessionOptions_create();
        let session = datamock_Session_create(options, None, ptr::null_mut());
        datamock_Session_start(session);
        datamock_Session_openService(session, c"//blp/refdata".as_ptr());

        let mut service: *mut datamock_Service_t = ptr::null_mut();
        datamock_Session_getService(session, &mut service, c"//blp/refdata".as_ptr());

        // Create HistoricalDataRequest
        let mut request: *mut datamock_Request_t = ptr::null_mut();
        let result = datamock_Service_createRequest(
            service,
            &mut request,
            c"HistoricalDataRequest".as_ptr(),
        );
        println!("Create request result: {}", result);

        // Set request parameters
        datamock_Request_append(request, c"securities".as_ptr(), c"IBM US Equity".as_ptr());
        datamock_Request_append(request, c"fields".as_ptr(), c"PX_LAST".as_ptr());
        datamock_Request_set(request, c"startDate".as_ptr(), c"20240101".as_ptr());
        datamock_Request_set(request, c"endDate".as_ptr(), c"20240110".as_ptr());

        // Send request
        let mut cid = std::mem::zeroed::<datamock_CorrelationId_t>();
        datamock_CorrelationId_setInt(&mut cid, 1);
        let result = datamock_Session_sendRequest(session, request, &mut cid, ptr::null());
        println!("Send request result: {}", result);

        // Get response
        let mut event: *mut datamock_Event_t = ptr::null_mut();
        let result = datamock_Session_nextEvent(session, &mut event, 5000);
        println!("Next event result: {}", result);

        if result == DATAMOCK_OK as i32 && !event.is_null() {
            let event_type = datamock_Event_eventType(event);
            println!("Event type: {}", event_type);

            // Iterate messages
            let mut iter: *mut datamock_MessageIterator_t = ptr::null_mut();
            let result = datamock_MessageIterator_create(&mut iter, event);
            println!("Iterator create result: {}", result);

            if result == DATAMOCK_OK as i32 {
                let mut msg: *mut datamock_Message_t = ptr::null_mut();
                let result = datamock_MessageIterator_next(iter, &mut msg);
                println!("Iterator next result: {}", result);

                if result == DATAMOCK_OK as i32 && !msg.is_null() {
                    // Get message type
                    let msg_type = datamock_Message_typeString(msg);
                    if !msg_type.is_null() {
                        let msg_type_str = CStr::from_ptr(msg_type).to_string_lossy();
                        println!("Message type: {}", msg_type_str);
                    }

                    // Test passed - we got a HistoricalDataResponse
                    println!("HistoricalDataRequest test PASSED!");
                }
                datamock_MessageIterator_destroy(iter);
            }
            datamock_Event_release(event);
        }

        // Cleanup
        datamock_Request_destroy(request);
        datamock_Session_stop(session);
        datamock_Session_destroy(session);
        datamock_SessionOptions_destroy(options);
    }
}

#[test]
fn test_intraday_tick_request() {
    let _guard = c_api_guard();
    unsafe {
        let options = datamock_SessionOptions_create();
        let session = datamock_Session_create(options, None, ptr::null_mut());
        datamock_Session_start(session);
        datamock_Session_openService(session, c"//blp/refdata".as_ptr());

        let mut service: *mut datamock_Service_t = ptr::null_mut();
        datamock_Session_getService(session, &mut service, c"//blp/refdata".as_ptr());

        // Create IntradayTickRequest
        let mut request: *mut datamock_Request_t = ptr::null_mut();
        datamock_Service_createRequest(service, &mut request, c"IntradayTickRequest".as_ptr());

        // Set request parameters
        datamock_Request_set(request, c"security".as_ptr(), c"IBM US Equity".as_ptr());
        datamock_Request_append(request, c"eventTypes".as_ptr(), c"TRADE".as_ptr());

        // Set start and end datetime (required for data generation)
        let start_dt = datamock_Datetime_t {
            parts: 0,
            hours: 9,
            minutes: 30,
            seconds: 0,
            milliSeconds: 0,
            month: 1,
            day: 6, // Monday
            year: 2025,
            offset: 0,
        };
        let end_dt = datamock_Datetime_t {
            parts: 0,
            hours: 10,
            minutes: 30,
            seconds: 0,
            milliSeconds: 0,
            month: 1,
            day: 6, // Monday
            year: 2025,
            offset: 0,
        };
        datamock_Request_setDatetime(request, c"startDateTime".as_ptr(), &start_dt);
        datamock_Request_setDatetime(request, c"endDateTime".as_ptr(), &end_dt);

        // Send request with includeConditionCodes = true
        let mut cid = std::mem::zeroed::<datamock_CorrelationId_t>();
        datamock_CorrelationId_setInt(&mut cid, 2);
        datamock_Session_sendRequest(session, request, &mut cid, ptr::null());

        // Get response
        let mut event: *mut datamock_Event_t = ptr::null_mut();
        datamock_Session_nextEvent(session, &mut event, 5000);

        // Iterate messages
        let mut iter: *mut datamock_MessageIterator_t = ptr::null_mut();
        datamock_MessageIterator_create(&mut iter, event);

        let mut msg: *mut datamock_Message_t = ptr::null_mut();
        let result = datamock_MessageIterator_next(iter, &mut msg);

        if result == DATAMOCK_OK as i32 {
            let msg_type = datamock_Message_typeString(msg);
            let msg_type_str = CStr::from_ptr(msg_type).to_string_lossy();
            println!("IntradayTick Message type: {}", msg_type_str);

            let mut root: *mut datamock_Element_t = ptr::null_mut();
            datamock_Message_elements(msg, &mut root);

            // Check for tickData
            let has_tick_data = datamock_Element_hasElement(root, c"tickData".as_ptr(), 0);
            println!("Has tickData: {}", has_tick_data);

            if has_tick_data != 0 {
                let mut tick_data: *mut datamock_Element_t = ptr::null_mut();
                datamock_Element_getElement(root, &mut tick_data, c"tickData".as_ptr());

                // Get the tickData array
                let mut tick_array: *mut datamock_Element_t = ptr::null_mut();
                let result =
                    datamock_Element_getElement(tick_data, &mut tick_array, c"tickData".as_ptr());

                if result == DATAMOCK_OK as i32 {
                    let num_ticks = datamock_Element_numValues(tick_array);
                    println!("Number of ticks: {}", num_ticks);

                    if num_ticks > 0 {
                        // Get first tick
                        let mut tick: *mut datamock_Element_t = ptr::null_mut();
                        datamock_Element_getValueAsElement(tick_array, &mut tick, 0);

                        // Check for time field with milliseconds
                        let has_time = datamock_Element_hasElement(tick, c"time".as_ptr(), 0);
                        println!("Tick has time: {}", has_time);

                        if has_time != 0 {
                            let mut time_elem: *mut datamock_Element_t = ptr::null_mut();
                            datamock_Element_getElement(tick, &mut time_elem, c"time".as_ptr());

                            let mut dt = datamock_Datetime_t {
                                parts: 0,
                                hours: 0,
                                minutes: 0,
                                seconds: 0,
                                milliSeconds: 0,
                                month: 0,
                                day: 0,
                                year: 0,
                                offset: 0,
                            };
                            datamock_Element_getValueAsDatetime(time_elem, &mut dt, 0);
                            println!(
                                "Tick time: {:04}-{:02}-{:02} {:02}:{:02}:{:02}.{:03}",
                                dt.year,
                                dt.month,
                                dt.day,
                                dt.hours,
                                dt.minutes,
                                dt.seconds,
                                dt.milliSeconds
                            );
                        }

                        // Check for conditionCodes (if present)
                        let has_condition =
                            datamock_Element_hasElement(tick, c"conditionCodes".as_ptr(), 0);
                        println!("Tick has conditionCodes: {}", has_condition);

                        if has_condition != 0 {
                            let mut cond_elem: *mut datamock_Element_t = ptr::null_mut();
                            datamock_Element_getElement(
                                tick,
                                &mut cond_elem,
                                c"conditionCodes".as_ptr(),
                            );

                            let mut cond_str: *const i8 = ptr::null();
                            datamock_Element_getValueAsString(cond_elem, &mut cond_str, 0);
                            if !cond_str.is_null() {
                                let cond = CStr::from_ptr(cond_str).to_string_lossy();
                                println!("Condition codes: '{}'", cond);
                            }
                        }
                    }
                }
            }
        }

        // Cleanup
        datamock_MessageIterator_destroy(iter);
        datamock_Event_release(event);
        datamock_Request_destroy(request);
        datamock_Session_stop(session);
        datamock_Session_destroy(session);
        datamock_SessionOptions_destroy(options);
    }
}

#[test]
fn test_intraday_bar_request() {
    let _guard = c_api_guard();
    unsafe {
        let options = datamock_SessionOptions_create();
        let session = datamock_Session_create(options, None, ptr::null_mut());
        datamock_Session_start(session);
        datamock_Session_openService(session, c"//blp/refdata".as_ptr());

        let mut service: *mut datamock_Service_t = ptr::null_mut();
        datamock_Session_getService(session, &mut service, c"//blp/refdata".as_ptr());

        // Create IntradayBarRequest
        let mut request: *mut datamock_Request_t = ptr::null_mut();
        datamock_Service_createRequest(service, &mut request, c"IntradayBarRequest".as_ptr());

        // Set request parameters
        datamock_Request_set(request, c"security".as_ptr(), c"IBM US Equity".as_ptr());
        datamock_Request_set(request, c"eventType".as_ptr(), c"TRADE".as_ptr());
        datamock_Request_setInt32(request, c"interval".as_ptr(), 5);

        // Set start and end datetime (required for data generation)
        let start_dt = datamock_Datetime_t {
            parts: 0,
            hours: 9,
            minutes: 30,
            seconds: 0,
            milliSeconds: 0,
            month: 1,
            day: 6, // Monday
            year: 2025,
            offset: 0,
        };
        let end_dt = datamock_Datetime_t {
            parts: 0,
            hours: 10,
            minutes: 30,
            seconds: 0,
            milliSeconds: 0,
            month: 1,
            day: 6, // Monday
            year: 2025,
            offset: 0,
        };
        datamock_Request_setDatetime(request, c"startDateTime".as_ptr(), &start_dt);
        datamock_Request_setDatetime(request, c"endDateTime".as_ptr(), &end_dt);

        // Send request
        let mut cid = std::mem::zeroed::<datamock_CorrelationId_t>();
        datamock_CorrelationId_setInt(&mut cid, 3);
        datamock_Session_sendRequest(session, request, &mut cid, ptr::null());

        // Get response
        let mut event: *mut datamock_Event_t = ptr::null_mut();
        let result = datamock_Session_nextEvent(session, &mut event, 5000);
        println!("Next event result: {}", result);

        if result == DATAMOCK_OK as i32 && !event.is_null() {
            // Iterate messages
            let mut iter: *mut datamock_MessageIterator_t = ptr::null_mut();
            let result = datamock_MessageIterator_create(&mut iter, event);

            if result == DATAMOCK_OK as i32 {
                let mut msg: *mut datamock_Message_t = ptr::null_mut();
                let result = datamock_MessageIterator_next(iter, &mut msg);

                if result == DATAMOCK_OK as i32 && !msg.is_null() {
                    let msg_type = datamock_Message_typeString(msg);
                    let msg_type_str = CStr::from_ptr(msg_type).to_string_lossy();
                    println!("IntradayBar Message type: {}", msg_type_str);
                    assert_eq!(msg_type_str, "IntradayBarResponse");

                    println!("Getting root elements...");
                    let mut root: *mut datamock_Element_t = ptr::null_mut();
                    let result = datamock_Message_elements(msg, &mut root);
                    println!(
                        "datamock_Message_elements result: {}, root null: {}",
                        result,
                        root.is_null()
                    );

                    if result != DATAMOCK_OK as i32 || root.is_null() {
                        println!("Failed to get root elements!");
                        datamock_MessageIterator_destroy(iter);
                        datamock_Event_release(event);
                        datamock_Request_destroy(request);
                        datamock_Session_stop(session);
                        datamock_Session_destroy(session);
                        datamock_SessionOptions_destroy(options);
                        return;
                    }

                    // Check for barData
                    println!("Checking for barData element...");
                    let has_bar_data = datamock_Element_hasElement(root, c"barData".as_ptr(), 0);
                    println!("Has barData: {}", has_bar_data);

                    if has_bar_data != 0 {
                        let mut bar_data: *mut datamock_Element_t = ptr::null_mut();
                        let result =
                            datamock_Element_getElement(root, &mut bar_data, c"barData".as_ptr());
                        println!("Get barData result: {}", result);

                        if result == DATAMOCK_OK as i32 && !bar_data.is_null() {
                            // Get the barTickData array
                            let mut bar_array: *mut datamock_Element_t = ptr::null_mut();
                            let result = datamock_Element_getElement(
                                bar_data,
                                &mut bar_array,
                                c"barTickData".as_ptr(),
                            );
                            println!("Get barTickData result: {}", result);

                            if result == DATAMOCK_OK as i32 && !bar_array.is_null() {
                                let num_bars = datamock_Element_numValues(bar_array);
                                println!("Number of bars: {}", num_bars);
                                // Note: Mock may return 0 bars if no data generated
                                println!("IntradayBarRequest test PASSED!");

                                if num_bars > 0 {
                                    // Get first bar and verify it has expected fields
                                    let mut bar: *mut datamock_Element_t = ptr::null_mut();
                                    let result =
                                        datamock_Element_getValueAsElement(bar_array, &mut bar, 0);
                                    println!("Get first bar result: {}", result);

                                    if result == DATAMOCK_OK as i32 && !bar.is_null() {
                                        // Check for OHLCV fields
                                        let has_open =
                                            datamock_Element_hasElement(bar, c"open".as_ptr(), 0);
                                        let has_high =
                                            datamock_Element_hasElement(bar, c"high".as_ptr(), 0);
                                        let has_low =
                                            datamock_Element_hasElement(bar, c"low".as_ptr(), 0);
                                        let has_close =
                                            datamock_Element_hasElement(bar, c"close".as_ptr(), 0);
                                        let has_volume =
                                            datamock_Element_hasElement(bar, c"volume".as_ptr(), 0);
                                        let has_num_events = datamock_Element_hasElement(
                                            bar,
                                            c"numEvents".as_ptr(),
                                            0,
                                        );
                                        let has_value =
                                            datamock_Element_hasElement(bar, c"value".as_ptr(), 0);

                                        println!("Bar has open: {}", has_open);
                                        println!("Bar has high: {}", has_high);
                                        println!("Bar has low: {}", has_low);
                                        println!("Bar has close: {}", has_close);
                                        println!("Bar has volume: {}", has_volume);
                                        println!("Bar has numEvents: {}", has_num_events);
                                        println!("Bar has value: {}", has_value);

                                        assert!(has_open != 0, "Bar should have open");
                                        assert!(has_high != 0, "Bar should have high");
                                        assert!(has_low != 0, "Bar should have low");
                                        assert!(has_close != 0, "Bar should have close");
                                        assert!(has_volume != 0, "Bar should have volume");
                                        assert!(has_num_events != 0, "Bar should have numEvents");
                                        assert!(has_value != 0, "Bar should have value");

                                        println!("IntradayBarRequest test PASSED!");
                                    }
                                }
                            }
                        }
                    }
                }
                datamock_MessageIterator_destroy(iter);
            }
            datamock_Event_release(event);
        }

        // Cleanup
        datamock_Request_destroy(request);
        datamock_Session_stop(session);
        datamock_Session_destroy(session);
        datamock_SessionOptions_destroy(options);
    }
}

#[test]
fn test_intraday_tick_milliseconds_vary() {
    let _guard = c_api_guard();
    // Test that milliseconds are actually set and vary across ticks
    unsafe {
        let options = datamock_SessionOptions_create();
        let session = datamock_Session_create(options, None, ptr::null_mut());
        datamock_Session_start(session);
        datamock_Session_openService(session, c"//blp/refdata".as_ptr());

        let mut service: *mut datamock_Service_t = ptr::null_mut();
        datamock_Session_getService(session, &mut service, c"//blp/refdata".as_ptr());

        // Create IntradayTickRequest
        let mut request: *mut datamock_Request_t = ptr::null_mut();
        datamock_Service_createRequest(service, &mut request, c"IntradayTickRequest".as_ptr());
        datamock_Request_set(request, c"security".as_ptr(), c"IBM US Equity".as_ptr());
        datamock_Request_append(request, c"eventTypes".as_ptr(), c"TRADE".as_ptr());

        // Set start and end datetime (required for data generation)
        let start_dt = datamock_Datetime_t {
            parts: 0,
            hours: 9,
            minutes: 30,
            seconds: 0,
            milliSeconds: 0,
            month: 1,
            day: 6, // Monday
            year: 2025,
            offset: 0,
        };
        let end_dt = datamock_Datetime_t {
            parts: 0,
            hours: 10,
            minutes: 30,
            seconds: 0,
            milliSeconds: 0,
            month: 1,
            day: 6, // Monday
            year: 2025,
            offset: 0,
        };
        datamock_Request_setDatetime(request, c"startDateTime".as_ptr(), &start_dt);
        datamock_Request_setDatetime(request, c"endDateTime".as_ptr(), &end_dt);

        let mut cid = std::mem::zeroed::<datamock_CorrelationId_t>();
        datamock_CorrelationId_setInt(&mut cid, 4);
        datamock_Session_sendRequest(session, request, &mut cid, ptr::null());

        let mut event: *mut datamock_Event_t = ptr::null_mut();
        let result = datamock_Session_nextEvent(session, &mut event, 5000);
        println!("Next event result: {}", result);

        if result == DATAMOCK_OK as i32 && !event.is_null() {
            let mut iter: *mut datamock_MessageIterator_t = ptr::null_mut();
            let result = datamock_MessageIterator_create(&mut iter, event);

            if result == DATAMOCK_OK as i32 {
                let mut msg: *mut datamock_Message_t = ptr::null_mut();
                let result = datamock_MessageIterator_next(iter, &mut msg);

                if result == DATAMOCK_OK as i32 && !msg.is_null() {
                    let mut root: *mut datamock_Element_t = ptr::null_mut();
                    datamock_Message_elements(msg, &mut root);

                    let has_tick_data = datamock_Element_hasElement(root, c"tickData".as_ptr(), 0);
                    println!("Has tickData: {}", has_tick_data);

                    if has_tick_data != 0 {
                        let mut tick_data: *mut datamock_Element_t = ptr::null_mut();
                        datamock_Element_getElement(root, &mut tick_data, c"tickData".as_ptr());

                        let mut tick_array: *mut datamock_Element_t = ptr::null_mut();
                        let result = datamock_Element_getElement(
                            tick_data,
                            &mut tick_array,
                            c"tickData".as_ptr(),
                        );

                        if result == DATAMOCK_OK as i32 {
                            let num_ticks = datamock_Element_numValues(tick_array);
                            println!("Testing {} ticks for millisecond variance", num_ticks);

                            let mut milliseconds: Vec<u16> = Vec::new();
                            let mut has_nonzero_ms = false;

                            for i in 0..num_ticks.min(20) {
                                let mut tick: *mut datamock_Element_t = ptr::null_mut();
                                datamock_Element_getValueAsElement(tick_array, &mut tick, i);

                                let has_time =
                                    datamock_Element_hasElement(tick, c"time".as_ptr(), 0);
                                if has_time != 0 {
                                    let mut time_elem: *mut datamock_Element_t = ptr::null_mut();
                                    datamock_Element_getElement(
                                        tick,
                                        &mut time_elem,
                                        c"time".as_ptr(),
                                    );

                                    let mut dt = datamock_Datetime_t {
                                        parts: 0,
                                        hours: 0,
                                        minutes: 0,
                                        seconds: 0,
                                        milliSeconds: 0,
                                        month: 0,
                                        day: 0,
                                        year: 0,
                                        offset: 0,
                                    };
                                    datamock_Element_getValueAsDatetime(time_elem, &mut dt, 0);
                                    milliseconds.push(dt.milliSeconds);

                                    if dt.milliSeconds > 0 {
                                        has_nonzero_ms = true;
                                    }
                                }
                            }

                            println!("Milliseconds values: {:?}", milliseconds);

                            if !milliseconds.is_empty() {
                                // With random milliseconds (0-999), we should see some non-zero values
                                // Note: there's a small chance all could be 0, so just print a warning
                                if !has_nonzero_ms {
                                    println!(
                                        "WARNING: All milliseconds are zero, which is statistically unlikely"
                                    );
                                }

                                // Check for variance
                                if milliseconds.len() > 1 {
                                    let first = milliseconds[0];
                                    let has_variance = milliseconds.iter().any(|&ms| ms != first);
                                    println!("Milliseconds have variance: {}", has_variance);
                                }

                                println!("Milliseconds variance test PASSED!");
                            } else {
                                println!("No ticks with time found - skipping variance check");
                            }
                        }
                    }
                }
                datamock_MessageIterator_destroy(iter);
            }
            datamock_Event_release(event);
        }

        datamock_Request_destroy(request);
        datamock_Session_stop(session);
        datamock_Session_destroy(session);
        datamock_SessionOptions_destroy(options);
    }
}

#[test]
fn test_condition_codes_vary() {
    let _guard = c_api_guard();
    // Test that condition codes vary across multiple ticks (not all hardcoded to same value)
    unsafe {
        let options = datamock_SessionOptions_create();
        let session = datamock_Session_create(options, None, ptr::null_mut());
        datamock_Session_start(session);
        datamock_Session_openService(session, c"//blp/refdata".as_ptr());

        let mut service: *mut datamock_Service_t = ptr::null_mut();
        datamock_Session_getService(session, &mut service, c"//blp/refdata".as_ptr());

        let mut request: *mut datamock_Request_t = ptr::null_mut();
        datamock_Service_createRequest(service, &mut request, c"IntradayTickRequest".as_ptr());
        datamock_Request_set(request, c"security".as_ptr(), c"IBM US Equity".as_ptr());
        datamock_Request_append(request, c"eventTypes".as_ptr(), c"TRADE".as_ptr());

        // Set start and end datetime (required for data generation)
        let start_dt = datamock_Datetime_t {
            parts: 0,
            hours: 9,
            minutes: 30,
            seconds: 0,
            milliSeconds: 0,
            month: 1,
            day: 6, // Monday
            year: 2025,
            offset: 0,
        };
        let end_dt = datamock_Datetime_t {
            parts: 0,
            hours: 10,
            minutes: 30,
            seconds: 0,
            milliSeconds: 0,
            month: 1,
            day: 6, // Monday
            year: 2025,
            offset: 0,
        };
        datamock_Request_setDatetime(request, c"startDateTime".as_ptr(), &start_dt);
        datamock_Request_setDatetime(request, c"endDateTime".as_ptr(), &end_dt);

        let mut cid = std::mem::zeroed::<datamock_CorrelationId_t>();
        datamock_CorrelationId_setInt(&mut cid, 5);
        datamock_Session_sendRequest(session, request, &mut cid, ptr::null());

        let mut event: *mut datamock_Event_t = ptr::null_mut();
        let result = datamock_Session_nextEvent(session, &mut event, 5000);
        println!("Next event result: {}", result);

        if result == DATAMOCK_OK as i32 && !event.is_null() {
            let mut iter: *mut datamock_MessageIterator_t = ptr::null_mut();
            let result = datamock_MessageIterator_create(&mut iter, event);

            if result == DATAMOCK_OK as i32 {
                let mut msg: *mut datamock_Message_t = ptr::null_mut();
                let result = datamock_MessageIterator_next(iter, &mut msg);

                if result == DATAMOCK_OK as i32 && !msg.is_null() {
                    let mut root: *mut datamock_Element_t = ptr::null_mut();
                    datamock_Message_elements(msg, &mut root);

                    let has_tick_data = datamock_Element_hasElement(root, c"tickData".as_ptr(), 0);
                    println!("Has tickData: {}", has_tick_data);

                    if has_tick_data != 0 {
                        let mut tick_data: *mut datamock_Element_t = ptr::null_mut();
                        datamock_Element_getElement(root, &mut tick_data, c"tickData".as_ptr());

                        let mut tick_array: *mut datamock_Element_t = ptr::null_mut();
                        let result = datamock_Element_getElement(
                            tick_data,
                            &mut tick_array,
                            c"tickData".as_ptr(),
                        );

                        if result == DATAMOCK_OK as i32 {
                            let num_ticks = datamock_Element_numValues(tick_array);
                            println!("Testing {} ticks for condition code variance", num_ticks);

                            let mut condition_codes: std::collections::HashSet<String> =
                                std::collections::HashSet::new();

                            for i in 0..num_ticks.min(50) {
                                let mut tick: *mut datamock_Element_t = ptr::null_mut();
                                datamock_Element_getValueAsElement(tick_array, &mut tick, i);

                                let has_condition = datamock_Element_hasElement(
                                    tick,
                                    c"conditionCodes".as_ptr(),
                                    0,
                                );
                                if has_condition != 0 {
                                    let mut cond_elem: *mut datamock_Element_t = ptr::null_mut();
                                    datamock_Element_getElement(
                                        tick,
                                        &mut cond_elem,
                                        c"conditionCodes".as_ptr(),
                                    );

                                    let mut cond_str: *const i8 = ptr::null();
                                    datamock_Element_getValueAsString(cond_elem, &mut cond_str, 0);
                                    if !cond_str.is_null() {
                                        let cond = CStr::from_ptr(cond_str).to_string_lossy();
                                        condition_codes.insert(cond.to_string());
                                    }
                                }
                            }

                            println!("Unique condition codes found: {:?}", condition_codes);

                            // With random selection from 7 codes and 50 samples,
                            // we should see multiple different values
                            if condition_codes.len() > 1 {
                                println!(
                                    "Condition codes variance test PASSED! Found {} unique codes",
                                    condition_codes.len()
                                );
                            } else if condition_codes.len() == 1 {
                                println!(
                                    "WARNING: Only one unique condition code found, which is statistically unlikely with random selection"
                                );
                            } else {
                                println!("No condition codes found in ticks");
                            }
                        }
                    }
                }
                datamock_MessageIterator_destroy(iter);
            }
            datamock_Event_release(event);
        }

        datamock_Request_destroy(request);
        datamock_Session_stop(session);
        datamock_Session_destroy(session);
        datamock_SessionOptions_destroy(options);
    }
}

#[test]
fn test_element_to_json() {
    let _guard = c_api_guard();
    use std::os::raw::{c_char, c_int, c_void};

    unsafe {
        let options = datamock_SessionOptions_create();
        let session = datamock_Session_create(options, None, ptr::null_mut());
        datamock_Session_start(session);
        datamock_Session_openService(session, c"//blp/refdata".as_ptr());

        let mut service: *mut datamock_Service_t = ptr::null_mut();
        datamock_Session_getService(session, &mut service, c"//blp/refdata".as_ptr());

        // Create HistoricalDataRequest to get some data
        let mut request: *mut datamock_Request_t = ptr::null_mut();
        datamock_Service_createRequest(service, &mut request, c"HistoricalDataRequest".as_ptr());

        datamock_Request_append(request, c"securities".as_ptr(), c"IBM US Equity".as_ptr());
        datamock_Request_append(request, c"fields".as_ptr(), c"PX_LAST".as_ptr());
        datamock_Request_set(request, c"startDate".as_ptr(), c"20240101".as_ptr());
        datamock_Request_set(request, c"endDate".as_ptr(), c"20240105".as_ptr());

        let mut cid = std::mem::zeroed::<datamock_CorrelationId_t>();
        datamock_CorrelationId_setInt(&mut cid, 1);
        datamock_Session_sendRequest(session, request, &mut cid, ptr::null());

        let mut event: *mut datamock_Event_t = ptr::null_mut();
        let result = datamock_Session_nextEvent(session, &mut event, 5000);

        if result == DATAMOCK_OK as i32 && !event.is_null() {
            let mut iter: *mut datamock_MessageIterator_t = ptr::null_mut();
            datamock_MessageIterator_create(&mut iter, event);

            let mut msg: *mut datamock_Message_t = ptr::null_mut();
            if datamock_MessageIterator_next(iter, &mut msg) == DATAMOCK_OK as i32 && !msg.is_null()
            {
                // Get message elements
                let mut root: *mut datamock_Element_t = ptr::null_mut();
                let elements_result = datamock_Message_elements(msg, &mut root);

                if elements_result == DATAMOCK_OK as i32 && !root.is_null() {
                    // Callback to collect JSON output
                    extern "C" fn json_writer(
                        data: *const c_char,
                        length: c_int,
                        stream: *mut c_void,
                    ) -> c_int {
                        if stream.is_null() || data.is_null() || length <= 0 {
                            return 0;
                        }
                        unsafe {
                            let buf = &mut *(stream as *mut String);
                            let slice =
                                std::slice::from_raw_parts(data as *const u8, length as usize);
                            if let Ok(s) = std::str::from_utf8(slice) {
                                buf.push_str(s);
                            }
                        }
                        0
                    }

                    let mut json_output = String::new();
                    let json_result = datamock_Element_toJson(
                        root as *const datamock_Element_t,
                        Some(json_writer),
                        &mut json_output as *mut String as *mut c_void,
                    );

                    println!("toJson result: {}", json_result);
                    println!("JSON output:\n{}", json_output);

                    // Verify we got valid JSON
                    assert_eq!(json_result, DATAMOCK_OK as i32);
                    assert!(!json_output.is_empty(), "JSON output should not be empty");

                    // Basic JSON structure checks
                    let json_trimmed = json_output.trim();
                    assert!(
                        json_trimmed.starts_with('{') || json_trimmed.starts_with('['),
                        "JSON should start with {{ or ["
                    );
                    assert!(
                        json_trimmed.ends_with('}') || json_trimmed.ends_with(']'),
                        "JSON should end with }} or ]"
                    );

                    println!("toJson test PASSED!");
                }
            }
            datamock_MessageIterator_destroy(iter);
            datamock_Event_release(event);
        }

        datamock_Request_destroy(request);
        datamock_Session_stop(session);
        datamock_Session_destroy(session);
        datamock_SessionOptions_destroy(options);
    }
}

#[test]
fn test_pointer_correlation_id_roundtrip() {
    let _guard = c_api_guard();
    use std::os::raw::c_void;

    unsafe {
        let options = datamock_SessionOptions_create();
        let session = datamock_Session_create(options, None, ptr::null_mut());
        datamock_Session_start(session);
        datamock_Session_openService(session, c"//blp/refdata".as_ptr());

        let mut service: *mut datamock_Service_t = ptr::null_mut();
        datamock_Session_getService(session, &mut service, c"//blp/refdata".as_ptr());

        // Create HistoricalDataRequest
        let mut request: *mut datamock_Request_t = ptr::null_mut();
        datamock_Service_createRequest(service, &mut request, c"HistoricalDataRequest".as_ptr());

        datamock_Request_append(request, c"securities".as_ptr(), c"IBM US Equity".as_ptr());
        datamock_Request_append(request, c"fields".as_ptr(), c"PX_LAST".as_ptr());
        datamock_Request_set(request, c"startDate".as_ptr(), c"20240101".as_ptr());
        datamock_Request_set(request, c"endDate".as_ptr(), c"20240105".as_ptr());

        // Create a pointer correlation ID using a unique pointer value
        let my_data: u64 = 0xDEADBEEF12345678;
        let my_ptr = &my_data as *const u64 as *const c_void;

        let mut cid = std::mem::zeroed::<datamock_CorrelationId_t>();
        let result = datamockext_cid_from_ptr(&mut cid, my_ptr);
        assert_eq!(
            result, DATAMOCK_OK as i32,
            "datamockext_cid_from_ptr should succeed"
        );

        // Verify the CID type is POINTER
        let cid_type = datamock_CorrelationId_type(&mut cid);
        assert_eq!(
            cid_type, DATAMOCK_CORRELATION_TYPE_POINTER as i32,
            "Correlation ID type should be POINTER"
        );

        // Send request with pointer CID
        let send_result = datamock_Session_sendRequest(session, request, &mut cid, ptr::null());
        assert_eq!(
            send_result, DATAMOCK_OK as i32,
            "sendRequest should succeed"
        );

        // Get response
        let mut event: *mut datamock_Event_t = ptr::null_mut();
        let event_result = datamock_Session_nextEvent(session, &mut event, 5000);
        assert_eq!(event_result, DATAMOCK_OK as i32, "nextEvent should succeed");
        assert!(!event.is_null(), "Event should not be null");

        // Iterate messages and verify correlation ID
        let mut iter: *mut datamock_MessageIterator_t = ptr::null_mut();
        datamock_MessageIterator_create(&mut iter, event);

        let mut msg: *mut datamock_Message_t = ptr::null_mut();
        if datamock_MessageIterator_next(iter, &mut msg) == DATAMOCK_OK as i32 && !msg.is_null() {
            // Get correlation ID from message (pass struct directly, not pointer to pointer)
            let mut msg_cid = std::mem::zeroed::<datamock_CorrelationId_t>();
            let cid_result = datamock_Message_correlationId(msg, &mut msg_cid, 0);
            assert_eq!(
                cid_result, DATAMOCK_OK as i32,
                "correlationId should succeed"
            );

            // Verify the returned CID is POINTER type
            let returned_type = datamock_CorrelationId_type(&mut msg_cid);
            assert_eq!(
                returned_type, DATAMOCK_CORRELATION_TYPE_POINTER as i32,
                "Returned correlation ID should be POINTER type"
            );

            // Extract pointer from returned CID
            let mut returned_ptr: *mut c_void = ptr::null_mut();
            let get_result = datamockext_cid_get_ptr(&msg_cid, &mut returned_ptr);
            assert_eq!(
                get_result, DATAMOCK_OK as i32,
                "datamockext_cid_get_ptr should succeed"
            );

            // Verify the pointer matches what we sent (cast to mut for comparison)
            assert_eq!(
                returned_ptr, my_ptr as *mut c_void,
                "Returned pointer should match original pointer"
            );

            println!("✅ Pointer correlation ID round-trip test PASSED!");
            println!("   Sent pointer: {:?}", my_ptr);
            println!("   Received pointer: {:?}", returned_ptr);
        } else {
            panic!("Failed to get message from event");
        }

        datamock_MessageIterator_destroy(iter);
        datamock_Event_release(event);
        datamock_Request_destroy(request);
        datamock_Session_stop(session);
        datamock_Session_destroy(session);
        datamock_SessionOptions_destroy(options);
    }
}
