#[cfg(test)]
pub mod mocks {
    use byteorder::{BigEndian, ReadBytesExt};
    use std::io::{Cursor, Read, Write};
    use std::net::{TcpListener, TcpStream};
    use std::sync::{Arc, Mutex};
    use std::thread;
    use time_tz::timezones;

    use crate::messages::{encode_length, OutgoingMessages};

    #[derive(Debug, Clone)]
    struct Interaction {
        request: OutgoingMessages,
        responses: Vec<String>,
    }

    pub struct MockGateway {
        handle: Option<thread::JoinHandle<()>>,
        requests: Arc<Mutex<Vec<String>>>,
        interactions: Vec<Interaction>,
        server_version: i32,
        address: Option<String>,
    }

    impl MockGateway {
        pub fn new(server_version: i32) -> Self {
            MockGateway {
                handle: None,
                requests: Arc::new(Mutex::new(Vec::new())),
                interactions: Vec::new(),
                server_version,
                address: None,
            }
        }

        pub fn requests(&self) -> Vec<String> {
            self.requests.lock().unwrap().clone()
        }

        pub fn address(&self) -> String {
            self.address.clone().unwrap_or_default()
        }

        pub fn add_interaction(&mut self, request: OutgoingMessages, responses: Vec<String>) {
            self.interactions.push(Interaction { request, responses });
        }

        pub fn server_version(&self) -> i32 {
            self.server_version
        }

        pub fn time_zone(&self) -> Option<&time_tz::Tz> {
            Some(timezones::db::EST)
        }

        pub fn start(&mut self) -> Result<(), anyhow::Error> {
            let listener = TcpListener::bind("127.0.0.1:0")?;
            let address = listener.local_addr()?;

            let requests = Arc::clone(&self.requests);
            let interactions = self.interactions.clone();
            let server_version = self.server_version;

            let handle = thread::spawn(move || {
                // Handle single request and exit
                let stream = match listener.accept() {
                    Ok((stream, addr)) => {
                        println!("MockGateway: Accepted connection from {}", addr);
                        stream
                    }
                    Err(e) => {
                        eprintln!("MockGateway: Failed to accept connection: {}", e);
                        return;
                    }
                };

                let mut handler = ConnectionHandler::new(server_version, requests.clone(), interactions.clone());
                if let Err(err) = handler.handle(stream) {
                    // Error handling connection
                    eprintln!("MockGateway: Error handling connection: {}", err);
                }
            });

            self.handle = Some(handle);
            self.address = Some(address.to_string());

            Ok(())
        }
    }

    impl Drop for MockGateway {
        fn drop(&mut self) {
            if let Some(handle) = self.handle.take() {
                handle.join().expect("Failed to join mock gateway thread");
            }
        }
    }

    struct ConnectionHandler {
        requests: Arc<Mutex<Vec<String>>>,
        interactions: Vec<Interaction>,
        current_interaction: usize,
        server_version: i32,
    }

    impl ConnectionHandler {
        pub fn new(server_version: i32, requests: Arc<Mutex<Vec<String>>>, interactions: Vec<Interaction>) -> Self {
            ConnectionHandler {
                requests,
                interactions,
                current_interaction: 0,
                server_version,
            }
        }

        pub fn handshake_response(&self) -> String {
            format!("{}\020240120 12:00:00 EST\0", self.server_version)
        }

        pub fn read_message(&mut self, stream: &mut TcpStream) -> Result<String, std::io::Error> {
            let size = self.read_size(stream)?;
            let mut buf = vec![0u8; size];
            stream.read_exact(&mut buf)?;
            Ok(String::from_utf8_lossy(&buf).into_owned())
        }

        pub fn write_message(&mut self, stream: &mut TcpStream, message: String) -> Result<(), std::io::Error> {
            let packet = encode_length(&message);
            stream.write_all(&packet)?;
            Ok(())
        }

        pub fn handle(&mut self, mut stream: TcpStream) -> Result<(), std::io::Error> {
            self.handle_startup(&mut stream)?;

            if self.interactions.is_empty() {
                self.send_shutdown(&mut stream)?;
                return Ok(());
            }

            // Set a read timeout so we don't wait forever for requests
            stream.set_read_timeout(Some(std::time::Duration::from_millis(500)))?;

            while let Ok(request) = self.read_message(&mut stream) {
                self.add_request(request.clone());

                // Check if we have a matching interaction
                if self.current_interaction < self.interactions.len() {
                    let interaction = self.interactions[self.current_interaction].clone();
                    if request.starts_with(&format!("{}\0", interaction.request)) {
                        println!("MockGateway: Sending {} responses", interaction.responses.len());
                        for (i, response) in interaction.responses.iter().enumerate() {
                            let msg_type = response.split('\0').next().unwrap_or("unknown");
                            println!("MockGateway: Sending response {} (type {})", i + 1, msg_type);
                            self.write_message(&mut stream, response.clone())?;
                            stream.flush()?;
                            // Small delay between messages to ensure proper delivery
                            std::thread::sleep(std::time::Duration::from_millis(10));
                        }
                        self.current_interaction += 1;
                    } else {
                        break;
                    }
                } else {
                    break;
                }
            }

            // Keep the connection alive for a bit to allow client to read all messages
            std::thread::sleep(std::time::Duration::from_millis(500));

            self.send_shutdown(&mut stream)?;
            println!("MockGateway: Shutdown sent, closing connection");

            // Give the client a moment to read the shutdown message
            std::thread::sleep(std::time::Duration::from_millis(50));

            Ok(())
        }

        pub fn handle_startup(&mut self, stream: &mut TcpStream) -> Result<(), std::io::Error> {
            let magic_token = self.read_magic_token(stream)?;
            assert_eq!(magic_token, "API\0");

            // Supported server versions
            let supported_versions = self.read_message(stream)?;
            println!("Supported server versions: {}", supported_versions);

            // Send handshake response
            self.write_message(stream, self.handshake_response())?;

            // Start API
            let message = self.read_message(stream)?;
            // For server versions > 72 (OPTIONAL_CAPABILITIES), expect an extra empty field
            if self.server_version > 72 {
                assert_eq!(message, "71\02\0100\0\0");
            } else {
                assert_eq!(message, "71\02\0100\0");
            }

            // next valid order id
            self.write_message(stream, "9\01\090\0".to_string())?;

            // managed accounts
            self.write_message(stream, "15\01\02334\0".to_string())?;

            Ok(())
        }

        pub fn send_shutdown(&mut self, stream: &mut TcpStream) -> Result<(), std::io::Error> {
            // signal shutdown
            println!("Sending shutdown message");
            self.write_message(stream, "-2\01\0".to_string())?;

            // Flush to ensure the message is sent
            stream.flush()?;

            Ok(())
        }

        pub fn add_request(&mut self, request: String) {
            let mut requests = self.requests.lock().unwrap();
            requests.push(request);
        }

        pub fn read_magic_token(&mut self, stream: &mut TcpStream) -> Result<String, std::io::Error> {
            let mut buf = [0u8; 4];
            stream.read_exact(&mut buf)?;
            Ok(String::from_utf8_lossy(&buf).into_owned())
        }

        fn read_size(&mut self, stream: &mut TcpStream) -> Result<usize, std::io::Error> {
            let buffer = &mut [0_u8; 4];
            stream.read_exact(buffer)?;
            let mut reader = Cursor::new(buffer);
            let count = reader.read_u32::<BigEndian>()?;
            Ok(count as usize)
        }
    }
}

#[cfg(test)]
pub mod tests {
    use time::OffsetDateTime;

    use crate::{client::common::mocks::MockGateway, messages::OutgoingMessages, server_versions};

    pub fn setup_connect() -> MockGateway {
        let mut gateway = MockGateway::new(server_versions::IPO_PRICES);
        gateway.start().expect("Failed to start mock gateway");
        gateway
    }

    pub struct ServerTimeExpectations {
        pub server_time: OffsetDateTime,
    }

    pub fn setup_server_time() -> (MockGateway, ServerTimeExpectations) {
        let mut gateway = MockGateway::new(server_versions::IPO_PRICES);

        let server_time = OffsetDateTime::now_utc().replace_nanosecond(0).unwrap();

        gateway.add_interaction(
            OutgoingMessages::RequestCurrentTime,
            vec![format!("49\01\0{}\0", server_time.unix_timestamp())],
        );

        gateway.start().expect("Failed to start mock gateway");

        (gateway, ServerTimeExpectations { server_time })
    }

    pub struct ManagedAccountsExpectations {
        pub accounts: Vec<String>,
    }

    pub fn setup_managed_accounts() -> (MockGateway, ManagedAccountsExpectations) {
        let mut gateway = MockGateway::new(server_versions::IPO_PRICES);
        let expected_accounts = vec!["DU1234567".to_string(), "DU1234568".to_string()];

        gateway.add_interaction(
            OutgoingMessages::RequestManagedAccounts,
            vec![format!("15\01\0{}\0", expected_accounts.join(","))],
        );

        gateway.start().expect("Failed to start mock gateway");

        (gateway, ManagedAccountsExpectations { accounts: expected_accounts })
    }

    pub struct NextValidOrderIdExpectations {
        pub next_valid_order_id: i32,
    }

    pub fn setup_next_valid_order_id() -> (MockGateway, NextValidOrderIdExpectations) {
        let mut gateway = MockGateway::new(server_versions::IPO_PRICES);
        let expected_order_id = 12345;

        gateway.add_interaction(OutgoingMessages::RequestIds, vec![format!("9\01\0{}\0", expected_order_id)]);

        gateway.start().expect("Failed to start mock gateway");

        (
            gateway,
            NextValidOrderIdExpectations {
                next_valid_order_id: expected_order_id,
            },
        )
    }

    pub fn setup_positions() -> MockGateway {
        let mut gateway = MockGateway::new(server_versions::IPO_PRICES);

        gateway.add_interaction(
            OutgoingMessages::RequestPositions,
            vec![
                "61\03\0DU1234567\012345\0AAPL\0STK\0\00.0\0\0\0SMART\0USD\0AAPL\0AAPL\0500.0\0150.25\0".to_string(),
                "62\01\0".to_string(),
            ],
        );

        gateway.start().expect("Failed to start mock gateway");
        gateway
    }

    pub fn setup_positions_multi() -> MockGateway {
        let mut gateway = MockGateway::new(server_versions::IPO_PRICES);

        gateway.add_interaction(
            OutgoingMessages::RequestPositionsMulti,
            vec![
                "71\03\09000\0DU1234567\012345\0AAPL\0STK\0\00.0\0\0\0SMART\0USD\0AAPL\0AAPL\0500.0\0150.25\0MODEL1\0".to_string(),
                "71\03\09000\0DU1234568\067890\0GOOGL\0STK\0\00.0\0\0\0SMART\0USD\0GOOGL\0GOOGL\0200.0\02500.00\0MODEL1\0".to_string(),
                "72\01\09000\0".to_string(),
            ],
        );

        gateway.start().expect("Failed to start mock gateway");
        gateway
    }

    pub fn setup_account_summary() -> MockGateway {
        let mut gateway = MockGateway::new(server_versions::IPO_PRICES);

        gateway.add_interaction(
            OutgoingMessages::RequestAccountSummary,
            vec![
                "63\01\09000\0DU1234567\0NetLiquidation\025000.00\0USD\0".to_string(),
                "63\01\09000\0DU1234567\0TotalCashValue\015000.00\0USD\0".to_string(),
                "64\01\09000\0".to_string(),
            ],
        );

        gateway.start().expect("Failed to start mock gateway");
        gateway
    }

    pub fn setup_pnl() -> MockGateway {
        let mut gateway = MockGateway::new(server_versions::IPO_PRICES);

        gateway.add_interaction(OutgoingMessages::RequestPnL, vec!["94\09000\0250.50\01500.00\0750.00\0".to_string()]);

        gateway.start().expect("Failed to start mock gateway");
        gateway
    }

    pub fn setup_pnl_single() -> MockGateway {
        let mut gateway = MockGateway::new(server_versions::IPO_PRICES);

        // Response format: message_type\0request_id\0position\0daily_pnl\0unrealized_pnl\0realized_pnl\0value\0
        gateway.add_interaction(
            OutgoingMessages::RequestPnLSingle,
            vec!["95\09000\0100.0\0150.25\0500.00\0250.00\01000.00\0".to_string()],
        );

        gateway.start().expect("Failed to start mock gateway");
        gateway
    }

    pub fn setup_family_codes() -> MockGateway {
        let mut gateway = MockGateway::new(server_versions::IPO_PRICES);

        // Response format: message_type\0count\0account_id\0family_code\0...
        gateway.add_interaction(
            OutgoingMessages::RequestFamilyCodes,
            vec!["78\02\0DU1234567\0FAM001\0DU1234568\0FAM002\0".to_string()],
        );

        gateway.start().expect("Failed to start mock gateway");
        gateway
    }

    pub fn setup_account_updates() -> MockGateway {
        let mut gateway = MockGateway::new(server_versions::IPO_PRICES);

        gateway.add_interaction(
            OutgoingMessages::RequestAccountData,
            vec![
                "6\02\0NetLiquidation\025000.00\0USD\0DU1234567\0".to_string(),
                // PortfolioValue v4: type(7), version(4), symbol, sec_type, expiry, strike, right,
                // currency, local_symbol, position, market_price, market_value, avg_cost, unrealized_pnl, realized_pnl, account
                // (NO contract_id, multiplier, primary_exchange, trading_class in v4!)
                "7\04\0AAPL\0STK\0\00.0\0\0USD\0AAPL\0500.0\0151.50\075750.00\0150.25\0375.00\0125.00\0DU1234567\0".to_string(),
                // AccountUpdateTime: type(8), version(ignored), timestamp
                "8\01\020240122 15:30:00\0".to_string(),
                "54\01\0DU1234567\0".to_string(),
            ],
        );

        gateway.start().expect("Failed to start mock gateway");
        gateway
    }

    pub fn setup_account_updates_multi() -> MockGateway {
        let mut gateway = MockGateway::new(server_versions::IPO_PRICES);

        gateway.add_interaction(
            OutgoingMessages::RequestAccountUpdatesMulti,
            vec![
                // AccountUpdateMulti: type(73), version(1), request_id, account, model_code, key, value, currency
                "73\01\09000\0DU1234567\0\0CashBalance\094629.71\0USD\0".to_string(),
                "73\01\09000\0DU1234567\0\0Currency\0USD\0USD\0".to_string(),
                "73\01\09000\0DU1234567\0\0StockMarketValue\00.00\0BASE\0".to_string(),
                // AccountUpdateMultiEnd: type(74), version(1), request_id
                "74\01\09000\0".to_string(),
            ],
        );

        gateway.start().expect("Failed to start mock gateway");
        gateway
    }

    pub fn setup_contract_details() -> MockGateway {
        let mut gateway = MockGateway::new(server_versions::IPO_PRICES);

        gateway.add_interaction(
            OutgoingMessages::RequestContractData,
            vec![
                // ContractData: type(10), symbol, sec_type, last_trade_date, strike, right, exchange, currency, 
                // local_symbol, market_name, trading_class, contract_id, min_tick, multiplier,
                // order_types, valid_exchanges, price_magnifier, under_contract_id, long_name, primary_exchange,
                // contract_month, industry, category, subcategory, time_zone_id, trading_hours, liquid_hours,
                // ev_rule, ev_multiplier, sec_id_list_count, agg_group, under_symbol, under_security_type,
                // market_rule_ids, real_expiration_date, stock_type, min_size, size_increment, suggested_size_increment
                "10\09000\0AAPL\0STK\0\00.0\0\0NASDAQ\0USD\0AAPL\0NMS\0AAPL\0265598\00.01\0\0ACTIVETIM,AD,ADJUST,ALERT,ALGO,ALLOC,AON,AVGCOST,BASKET,BENCHPX,CASHQTY,COND,CONDORDER,DARKONLY,DARKPOLL,DAY,DEACT,DEACTDIS,DEACTPX,DIS,FOK,GAT,GTC,GTD,GTT,HID,IBKRATS,ICE,IMB,IOC,LIT,LMT,LOC,MIDPX,MIT,MKT,MOC,MTL,NGCOMB,NODARK,NONALGO,OCA,OPG,OPGREROUT,PEGBENCH,PEGMID,PEGMKT,PEGPRI,PEGSTK,POSTONLY,PREOPGRTH,PRICEIMP,REL,RPI,RTH,SCALE,SCALEODD,SCALERST,SIZEPRIO,SNAPMID,SNAPMKT,SNAPREL,STP,STPLMT,SWEEP,TRAIL,TRAILLIT,TRAILLMT,TRAILMIT,WHATIF\0SMART,AMEX,NYSE,CBOE,PHLX,ISE,CHX,ARCA,ISLAND,DRCTEDGE,BEX,BATS,EDGEA,CSFBALGO,JEFFALGO,BYX,IEX,EDGX,FOXRIVER,PEARL,NYSENAT,LTSE,MEMX,TPLUS1,PSX\00\00\0Apple Inc\0NASDAQ\0\0Technology\0Computers\0Computers\0US/Eastern\020240122:0930-1600;20240123:0930-1600\020240122:0930-1600;20240123:0930-1600\0\00\00\00\0\0\0Consolidated\0\0NMS\01\01\01\0".to_string(),
                // ContractDataEnd: type(52), version(1), request_id
                "52\01\09000\0".to_string(),
            ],
        );

        gateway.start().expect("Failed to start mock gateway");
        gateway
    }

    pub fn setup_matching_symbols() -> MockGateway {
        let mut gateway = MockGateway::new(server_versions::BOND_ISSUERID);

        gateway.add_interaction(
            OutgoingMessages::RequestMatchingSymbols,
            vec![
                // SymbolSamples: type(79), request_id, count, 
                // (contract_id, symbol, security_type, primary_exchange, currency, deriv_sec_types_count, [deriv_types], description, issuer_id)*
                // Format based on the decoder test
                "79\09000\02\0265598\0AAPL\0STK\0NASDAQ\0USD\02\0OPT\0WAR\0Apple Inc.\0AAPL123\0276821\0MSFT\0STK\0NASDAQ\0USD\01\0OPT\0Microsoft Corporation\0MSFT456\0".to_string(),
            ],
        );

        gateway.start().expect("Failed to start mock gateway");
        gateway
    }

    pub fn setup_market_rule() -> MockGateway {
        let mut gateway = MockGateway::new(server_versions::MARKET_RULES);

        gateway.add_interaction(
            OutgoingMessages::RequestMarketRule,
            vec![
                // MarketRule: type(93), market_rule_id, price_increments_count, (low_edge, increment)*
                // Example with 3 price increments for market rule ID 26
                "93\026\03\00.0\00.01\0100.0\00.05\01000.0\00.10\0".to_string(),
            ],
        );

        gateway.start().expect("Failed to start mock gateway");
        gateway
    }

    pub fn setup_calculate_option_price() -> MockGateway {
        let mut gateway = MockGateway::new(server_versions::PRICE_BASED_VOLATILITY);

        gateway.add_interaction(
            OutgoingMessages::ReqCalcImpliedVolat,
            vec![
                // TickOptionComputation: type(21), request_id, tick_type, tick_attribute,
                // implied_volatility, delta, option_price, pv_dividend, gamma, vega, theta, underlying_price
                // tick_type=13 (ModelOption), tick_attribute=0
                "21\09000\013\00\00.25\00.5\012.75\00.0\00.05\00.02\0-0.01\0100.0\0".to_string(),
            ],
        );

        gateway.start().expect("Failed to start mock gateway");
        gateway
    }

    pub fn setup_calculate_implied_volatility() -> MockGateway {
        let mut gateway = MockGateway::new(server_versions::PRICE_BASED_VOLATILITY);

        gateway.add_interaction(
            OutgoingMessages::ReqCalcImpliedVolat,
            vec![
                // TickOptionComputation: type(21), request_id, tick_type, tick_attribute,
                // implied_volatility, delta, option_price, pv_dividend, gamma, vega, theta, underlying_price
                // tick_type=13 (ModelOption), tick_attribute=1 (price-based)
                // When calculating IV from price, we get back the computed IV (0.35 in this example)
                "21\09000\013\01\00.35\00.45\015.50\00.0\00.04\00.03\0-0.02\0105.0\0".to_string(),
            ],
        );

        gateway.start().expect("Failed to start mock gateway");
        gateway
    }

    pub fn setup_option_chain() -> MockGateway {
        let mut gateway = MockGateway::new(server_versions::SEC_DEF_OPT_PARAMS_REQ);

        gateway.add_interaction(
            OutgoingMessages::RequestSecurityDefinitionOptionalParameters,
            vec![
                // SecurityDefinitionOptionParameter: type(75), request_id, exchange, underlying_contract_id,
                // trading_class, multiplier, expirations_count, expirations, strikes_count, strikes
                "75\09000\0SMART\0265598\0AAPL\0100\03\020250117\020250221\020250321\05\090.0\095.0\0100.0\0105.0\0110.0\0".to_string(),
                // Multiple exchanges can be returned
                "75\09000\0CBOE\0265598\0AAPL\0100\02\020250117\020250221\04\095.0\0100.0\0105.0\0110.0\0".to_string(),
                // SecurityDefinitionOptionParameterEnd: type(76), request_id
                "76\09000\0".to_string(),
            ],
        );

        gateway.start().expect("Failed to start mock gateway");
        gateway
    }

    pub fn setup_open_orders() -> MockGateway {
        let mut gateway = MockGateway::new(server_versions::IPO_PRICES);

        gateway.add_interaction(
            OutgoingMessages::RequestOpenOrders,
            vec![
                // OpenOrder message - Order 1: Copy exact format from place_order test
                "5\01001\0265598\0AAPL\0STK\0\00\0?\0\0SMART\0USD\0AAPL\0NMS\0BUY\0100\0MKT\00.0\00.0\0DAY\0\0DU1236109\0\00\0\0100\01377295418\00\00\00\0\01377295418.0/DU1236109/100\0\0\0\0\0\0\0\0\0\00\0\0-1\00\0\0\0\0\0\02147483647\00\00\00\0\03\00\00\0\00\00\0\00\0None\0\00\0\0\0\0?\00\00\0\00\00\0\0\0\0\0\00\00\00\02147483647\02147483647\0\0\00\0\0IB\00\00\0\00\00\0PreSubmitted\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\0\0\0\0\0\00\00\00\0None\01.7976931348623157E308\02.0\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\00\0\0\0\00\01\00\00\00\0\0\00\0\0\0\0\0\0".to_string(),
                // OpenOrder message - Order 2: Same format but changed order_id, symbol, action, quantity, order_type, limit_price
                "5\01002\0276821\0MSFT\0STK\0\00\0?\0\0SMART\0USD\0MSFT\0NMS\0SELL\050\0LMT\0350.0\00.0\0DAY\0\0DU1236109\0\00\0\0100\01377295419\00\00\00\0\01377295419.0/DU1236109/100\0\0\0\0\0\0\0\0\0\00\0\0-1\00\0\0\0\0\0\02147483647\00\00\00\0\03\00\00\0\00\00\0\00\0None\0\00\0\0\0\0?\00\00\0\00\00\0\0\0\0\0\00\00\00\02147483647\02147483647\0\0\00\0\0IB\00\00\0\00\00\0Submitted\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\0\0\0\0\0\00\00\00\0None\01.7976931348623157E308\02.0\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\00\0\0\0\00\01\00\00\00\0\0\00\0\0\0\0\0\0".to_string(),
                // OpenOrderEnd message: type(53), version(1)
                "53\01\0".to_string(),
            ],
        );

        gateway.start().expect("Failed to start mock gateway");
        gateway
    }

    pub fn setup_all_open_orders() -> MockGateway {
        let mut gateway = MockGateway::new(server_versions::IPO_PRICES);

        gateway.add_interaction(
            OutgoingMessages::RequestAllOpenOrders,
            vec![
                // OpenOrder message - Order 1: TSLA order from different client
                "5\02001\076792\0TSLA\0STK\0\00\0?\0\0SMART\0USD\0TSLA\0NMS\0BUY\010\0LMT\0420.0\00.0\0GTC\0\0DU1236110\0\00\0\0101\01377295500\00\00\00\0\01377295500.0/DU1236110/101\0\0\0\0\0\0\0\0\0\00\0\0-1\00\0\0\0\0\0\02147483647\00\00\00\0\03\00\00\0\00\00\0\00\0None\0\00\0\0\0\0?\00\00\0\00\00\0\0\0\0\0\00\00\00\02147483647\02147483647\0\0\00\0\0IB\00\00\0\00\00\0Submitted\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\0\0\0\0\0\00\00\00\0None\01.7976931348623157E308\02.0\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\00\0\0\0\00\01\00\00\00\0\0\00\0\0\0\0\0\0".to_string(),
                // OpenOrder message - Order 2: AMZN order from different client  
                "5\02002\03691\0AMZN\0STK\0\00\0?\0\0SMART\0USD\0AMZN\0NMS\0SELL\05\0MKT\00.0\00.0\0DAY\0\0DU1236111\0\00\0\0102\01377295501\00\00\00\0\01377295501.0/DU1236111/102\0\0\0\0\0\0\0\0\0\00\0\0-1\00\0\0\0\0\0\02147483647\00\00\00\0\03\00\00\0\00\00\0\00\0None\0\00\0\0\0\0?\00\00\0\00\00\0\0\0\0\0\00\00\00\02147483647\02147483647\0\0\00\0\0IB\00\00\0\00\00\0PreSubmitted\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\0\0\0\0\0\00\00\00\0None\01.7976931348623157E308\02.0\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\00\0\0\0\00\01\00\00\00\0\0\00\0\0\0\0\0\0".to_string(),
                // OpenOrder message - Order 3: GOOGL order from current client
                "5\01003\026578\0GOOGL\0STK\0\00\0?\0\0SMART\0USD\0GOOGL\0NMS\0BUY\020\0LMT\02800.0\00.0\0DAY\0\0DU1236109\0\00\0\0100\01377295502\00\00\00\0\01377295502.0/DU1236109/100\0\0\0\0\0\0\0\0\0\00\0\0-1\00\0\0\0\0\0\02147483647\00\00\00\0\03\00\00\0\00\00\0\00\0None\0\00\0\0\0\0?\00\00\0\00\00\0\0\0\0\0\00\00\00\02147483647\02147483647\0\0\00\0\0IB\00\00\0\00\00\0Submitted\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\0\0\0\0\0\00\00\00\0None\01.7976931348623157E308\02.0\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\00\0\0\0\00\01\00\00\00\0\0\00\0\0\0\0\0\0".to_string(),
                // OpenOrderEnd message
                "53\01\0".to_string(),
            ],
        );

        gateway.start().expect("Failed to start mock gateway");
        gateway
    }

    pub fn setup_auto_open_orders() -> MockGateway {
        let mut gateway = MockGateway::new(server_versions::IPO_PRICES);

        gateway.add_interaction(
            OutgoingMessages::RequestAutoOpenOrders,
            vec![
                // OrderStatus message - Order 3001 status update
                "3\03001\0PreSubmitted\00\0100\00\0123456\00\00\0100\0\00\0".to_string(),
                // OpenOrder message - Order 3001: FB order from TWS
                "5\03001\013407\0FB\0STK\0\00\0?\0\0SMART\0USD\0FB\0NMS\0BUY\050\0MKT\00.0\00.0\0DAY\0\0TWS\0\00\0\00\01377295600\00\00\00\0\01377295600.0/TWS/0\0\0\0\0\0\0\0\0\0\00\0\0-1\00\0\0\0\0\0\02147483647\00\00\00\0\03\00\00\0\00\00\0\00\0None\0\00\0\0\0\0?\00\00\0\00\00\0\0\0\0\0\00\00\00\02147483647\02147483647\0\0\00\0\0IB\00\00\0\00\00\0PreSubmitted\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\0\0\0\0\0\00\00\00\0None\01.7976931348623157E308\02.0\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\00\0\0\0\00\01\00\00\00\0\0\00\0\0\0\0\0\0".to_string(),
                // OrderStatus message - Order 3001 submitted
                "3\03001\0Submitted\00\0100\00\0123456\00\00\0100\0\00\0".to_string(),
                // OpenOrderEnd message
                "53\01\0".to_string(),
            ],
        );

        gateway.start().expect("Failed to start mock gateway");
        gateway
    }

    pub fn setup_completed_orders() -> MockGateway {
        let mut gateway = MockGateway::new(server_versions::COMPLETED_ORDERS);

        // Real CompletedOrder message captured from IB Gateway
        // This is a cancelled ES futures order
        let msg1 = "101\0637533641\0ES\0FUT\020250919\00\0?\050\0CME\0USD\0ESU5\0ES\0BUY\01\0LMT\01000.0\00.0\0GTC\0\0DU1236109\0\00\0\0616088517\00\00\00\0\0\0\0\0\0\0\0\0\0\00\0\0-1\0\0\0\0\0\02147483647\00\00\0\03\00\0\00\0None\0\00\00\00\0\00\00\0\0\0\00\00\00\02147483647\02147483647\0\0\0\0IB\00\00\0\00\0Cancelled\00\00\00\01001.0\01.7976931348623157E308\00\01\00\0\00\02147483647\00\0Not an insider or substantial shareholder\00\00\09223372036854775807\020250810 09:07:39 America/Los_Angeles\0Cancelled by Trader\0\0\0\0\0\0".to_string();

        // Create a second message for a filled stock order (AAPL)
        let msg2 = "101\0265598\0AAPL\0STK\0\00\0\0\0SMART\0USD\0AAPL\0NMS\0BUY\0100\0MKT\00.0\00.0\0DAY\0\0DU1236109\0\00\0\01377295418\00\00\00\0\0\0\0\0\0\0\0\0\0\00\0\0-1\0\0\0\0\0\02147483647\00\00\0\03\00\0\00\0None\0\00\00\00\0\00\00\0\0\0\00\00\00\02147483647\02147483647\0\0\0\0IB\00\00\0\00\0Filled\0100\00\00\0150.25\01.7976931348623157E308\00\01\00\0\00\02147483647\00\0Not an insider or substantial shareholder\00\00\09223372036854775807\020231122 10:30:00 America/Los_Angeles\0Filled\0\0\0\0\0\0".to_string();

        gateway.add_interaction(
            OutgoingMessages::RequestCompletedOrders,
            vec![
                msg1,
                msg2,
                // CompletedOrdersEnd message - captured from real IB Gateway
                "102\0".to_string(),
            ],
        );

        gateway.start().expect("Failed to start mock gateway");
        gateway
    }

    pub fn setup_place_order() -> MockGateway {
        let mut gateway = MockGateway::new(server_versions::IPO_PRICES);

        gateway.add_interaction(
            OutgoingMessages::PlaceOrder,
            vec![
                // Real OrderStatus message captured from TWS
                "3\01001\0PreSubmitted\00\0100\00\0123456\00\00\0100\0\00\0".to_string(),
                // Another OrderStatus showing submitted
                "3\01001\0Submitted\00\0100\00\0123456\00\00\0100\0\00\0".to_string(),
                // OrderStatus update when filled
                "3\01001\0Filled\0100\00\0150.25\0123456\00\0150.25\0100\0\00\0".to_string(),
                // OpenOrder message - captured from real TWS, with order_id changed to 1001
                "5\01001\0265598\0AAPL\0STK\0\00\0?\0\0SMART\0USD\0AAPL\0NMS\0BUY\0100\0LMT\01.0\00.0\0DAY\0\0DU1236109\0\00\0\0100\01377295418\00\00\00\0\01377295418.0/DU1236109/100\0\0\0\0\0\0\0\0\0\00\0\0-1\00\0\0\0\0\0\02147483647\00\00\00\0\03\00\00\0\00\00\0\00\0None\0\00\0\0\0\0?\00\00\0\00\00\0\0\0\0\0\00\00\00\02147483647\02147483647\0\0\00\0\0IB\00\00\0\00\00\0PreSubmitted\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\0\0\0\0\0\00\00\00\0None\01.7976931348623157E308\02.0\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\00\0\0\0\00\01\00\00\00\0\0\00\0\0\0\0\0\0".to_string(),
                // ExecutionData message - complete format with all required fields
                // Fields: type(11), request_id, order_id, contract_id, symbol, sec_type, 
                // last_trade_date, strike, right, multiplier, exchange, currency, local_symbol, trading_class,
                // execution_id, time, account, exchange, side, shares, price, perm_id, client_id,
                // liquidation, cum_qty, avg_price, order_ref, ev_rule, ev_multiplier, model_code, last_liquidity
                "11\0-1\01001\0265598\0AAPL\0STK\0\00.0\0\0\0SMART\0USD\0AAPL\0AAPL\0000e1a2b.67890abc.01.01\020240125 10:30:00\0DU1234567\0SMART\0BOT\0100\0150.25\0123456\0100\00\0100\0150.25\0\0\00.0\0\00\0".to_string(),
                // CommissionReport message
                // type(59), version(1), execution_id, commission, currency, realized_pnl, yield, yield_redemption_date
                "59\01\0000e1a2b.67890abc.01.01\01.25\0USD\00.0\00.0\00\0".to_string(),
            ],
        );

        gateway.start().expect("Failed to start mock gateway");
        gateway
    }

    pub fn setup_cancel_order() -> MockGateway {
        let mut gateway = MockGateway::new(server_versions::IPO_PRICES);

        // Add interaction for cancel_order
        gateway.add_interaction(
            OutgoingMessages::CancelOrder,
            vec![
                // OrderStatus message showing order was cancelled
                // Fields: type(3), order_id, status, filled, remaining, avg_fill_price, perm_id, parent_id, last_fill_price, client_id, why_held, mkt_cap_price
                "3\01001\0Cancelled\00\0100\00.0\0123456\00\00.0\0100\0\00\0".to_string(),
                // Error message confirming cancellation (optional but common)
                // Fields: type(4), version(2), id(order_id), error_code(202), error_string
                "4\02\01001\0202\0Order Cancelled - reason:User requested order cancellation\0".to_string(),
            ],
        );

        gateway.start().expect("Failed to start mock gateway");
        gateway
    }

    pub fn setup_global_cancel() -> MockGateway {
        let mut gateway = MockGateway::new(server_versions::IPO_PRICES);

        // Add interaction for global_cancel - simulates cancelling two open orders
        gateway.add_interaction(
            OutgoingMessages::RequestGlobalCancel,
            vec![
                // OrderStatus for first cancelled order
                // Fields: type(3), order_id, status, filled, remaining, avg_fill_price, perm_id, parent_id, last_fill_price, client_id, why_held, mkt_cap_price
                "3\01033\0Cancelled\00\0100\00\0137729541\00\00\0100\0\00\0".to_string(),
                // Error message for first order
                // Fields: type(4), version(2), id(order_id), error_code(202), error_string, advanced_order_reject_json
                "4\02\01033\0202\0Order Canceled - reason:\0\0".to_string(),
                // OrderStatus for second cancelled order
                "3\01034\0Cancelled\00\050\00\0137729542\00\00\0100\0\00\0".to_string(),
                // Error message for second order
                "4\02\01034\0202\0Order Canceled - reason:\0\0".to_string(),
            ],
        );

        gateway.start().expect("Failed to start mock gateway");
        gateway
    }

    pub fn setup_executions() -> MockGateway {
        let mut gateway = MockGateway::new(server_versions::IPO_PRICES);

        // Add interaction for RequestExecutions message
        gateway.add_interaction(
            OutgoingMessages::RequestExecutions,
            vec![
                // ExecutionData message - stock execution (AAPL)
                // Fields: type(11), request_id, order_id, contract_id, symbol, sec_type, 
                // last_trade_date, strike, right, multiplier, exchange, currency, local_symbol, trading_class,
                // execution_id, time, account, exchange, side, shares, price, perm_id, client_id,
                // liquidation, cum_qty, avg_price, order_ref, ev_rule, ev_multiplier, model_code, last_liquidity
                "11\09000\01001\0265598\0AAPL\0STK\0\00.0\0\0\0SMART\0USD\0AAPL\0AAPL\0000e1a2b.67890abc.01.01\020240125 10:30:00\0DU1234567\0SMART\0BOT\0100\0150.25\0123456\0100\00\0100\0150.25\0\0\00.0\0\00\0".to_string(),
                // CommissionReport message for first execution
                // Fields: type(59), version(1), execution_id, commission, currency, realized_pnl, yield, yield_redemption_date
                "59\01\0000e1a2b.67890abc.01.01\01.25\0USD\00.0\00.0\00\0".to_string(),
                // ExecutionData message - futures execution (ES)
                "11\09000\01002\0637533641\0ES\0FUT\020250919\00.0\0\050\0CME\0USD\0ESU5\0ES\0000e1a2b.67890def.02.01\020240125 10:31:00\0DU1234567\0CME\0SLD\05\05050.25\0123457\0100\00\05\05050.25\0\0\00.0\0\00\0".to_string(),
                // CommissionReport message for second execution
                "59\01\0000e1a2b.67890def.02.01\02.50\0USD\0125.50\00.0\00\0".to_string(),
                // ExecutionData message - options execution (SPY)
                "11\09000\01003\0123456789\0SPY\0OPT\020240126\0450.0\0C\0100\0CBOE\0USD\0SPY240126C00450000\0SPY\0000e1a2b.67890ghi.03.01\020240125 10:32:00\0DU1234567\0CBOE\0BOT\010\02.50\0123458\0100\00\010\02.50\0\0\00.0\0\00\0".to_string(),
                // CommissionReport message for third execution
                "59\01\0000e1a2b.67890ghi.03.01\00.65\0USD\0250.00\00.0\00\0".to_string(),
                // ExecutionDataEnd message
                "55\01\09000\0".to_string(),
            ],
        );

        gateway.start().expect("Failed to start mock gateway");
        gateway
    }

    pub fn setup_exercise_options() -> MockGateway {
        let mut gateway = MockGateway::new(server_versions::IPO_PRICES);

        // Add interaction for ExerciseOptions message
        // Using order_id 90 to match the next_order_id from client
        gateway.add_interaction(
            OutgoingMessages::ExerciseOptions,
            vec![
                // OrderStatus message - Option exercise submitted
                // Fields: type(3), order_id, status, filled, remaining, avg_fill_price, perm_id, parent_id, last_fill_price, client_id, why_held, mkt_cap_price
                "3\090\0PreSubmitted\00\010\00.0\0123456\00\00.0\0100\0\00\0".to_string(),
                // OpenOrder message - Option exercise order
                // Fields: Similar to place_order but for an option contract being exercised
                // contract_id=123456789 for SPY option, security_type=OPT, right=C (Call), strike=450.0
                "5\090\0123456789\0SPY\0OPT\020240126\0450.0\0C\0100\0CBOE\0USD\0SPY240126C00450000\0SPY\0BUY\010\0EXERCISE\00.0\00.0\0DAY\0\0DU1234567\0\00\0\0100\01377295700\00\00\00\0\01377295700.0/DU1234567/100\0\0\0\0\0\0\0\0\0\00\0\0-1\00\0\0\0\0\0\02147483647\00\00\00\0\03\00\00\0\00\00\0\00\0None\0\00\0\0\0\0?\00\00\0\00\00\0\0\0\0\0\00\00\00\02147483647\02147483647\0\0\00\0\0IB\00\00\0\00\00\0PreSubmitted\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\0\0\0\0\0\00\00\00\0None\01.7976931348623157E308\02.0\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\01.7976931348623157E308\00\0\0\0\00\01\00\00\00\0\0\00\0\0\0\0\0\0".to_string(),
                // OrderStatus message - Option exercise in progress
                "3\090\0Submitted\00\010\00.0\0123456\00\00.0\0100\0\00\0".to_string(),
                // OrderStatus message - Option exercise filled
                "3\090\0Filled\010\00\00.0\0123456\00\00.0\0100\0\00\0".to_string(),
            ],
        );

        gateway.start().expect("Failed to start mock gateway");
        gateway
    }

    // === Market Data Setup Functions ===

    pub fn setup_market_data() -> MockGateway {
        let mut gateway = MockGateway::new(server_versions::IPO_PRICES);

        gateway.add_interaction(
            OutgoingMessages::RequestMarketData,
            vec![
                // TickPrice - Bid price
                // type(1), version(6), request_id, tick_type(1=bid), price, size(deprecated), attrib_mask
                "1\06\09000\01\0150.50\00\01\0".to_string(),
                // TickSize - Bid size
                // type(2), version(6), request_id, tick_type(0=bid_size), size
                "2\06\09000\00\0100\0".to_string(),
                // TickPrice - Ask price
                // type(1), version(6), request_id, tick_type(2=ask), price, size(deprecated), attrib_mask
                "1\06\09000\02\0151.00\00\00\0".to_string(),
                // TickSize - Ask size
                // type(2), version(6), request_id, tick_type(3=ask_size), size
                "2\06\09000\03\0200\0".to_string(),
                // TickPrice - Last price
                // type(1), version(6), request_id, tick_type(4=last), price, size(deprecated), attrib_mask
                "1\06\09000\04\0150.75\00\00\0".to_string(),
                // TickSize - Last size
                // type(2), version(6), request_id, tick_type(5=last_size), size
                "2\06\09000\05\050\0".to_string(),
                // TickString - Last timestamp
                // type(46), version(6), request_id, tick_type(45=last_timestamp), value
                "46\06\09000\045\01705500000\0".to_string(),
                // TickGeneric - Volume
                // type(45), version(6), request_id, tick_type(8=volume), value
                "45\06\09000\08\01500000\0".to_string(),
                // TickSnapshotEnd - for snapshot requests
                // type(57), version(1), request_id
                "57\01\09000\0".to_string(),
            ],
        );

        gateway.start().expect("Failed to start mock gateway");
        gateway
    }

    pub fn setup_realtime_bars() -> MockGateway {
        let mut gateway = MockGateway::new(server_versions::IPO_PRICES);

        gateway.add_interaction(
            OutgoingMessages::RequestRealTimeBars,
            vec![
                // RealTimeBars message - 5 second bar
                // type(50), version(3), request_id, time, open, high, low, close, volume, wap, count
                "50\03\09000\01705500000\0150.25\0150.75\0150.00\0150.50\01000\0150.40\025\0".to_string(),
                // Another bar after 5 seconds
                "50\03\09000\01705500005\0150.50\0151.00\0150.40\0150.90\01200\0150.70\030\0".to_string(),
                // Third bar
                "50\03\09000\01705500010\0150.90\0151.25\0150.85\0151.20\0800\0151.05\020\0".to_string(),
            ],
        );

        gateway.start().expect("Failed to start mock gateway");
        gateway
    }

    pub fn setup_tick_by_tick_last() -> MockGateway {
        let mut gateway = MockGateway::new(server_versions::TICK_BY_TICK);

        gateway.add_interaction(
            OutgoingMessages::RequestTickByTickData,
            vec![
                // TickByTick message - Last trade
                // type(99), request_id, tick_type(1=Last, 2=AllLast), time, price, size,
                // tick_attrib_last(mask), exchange, special_conditions
                "99\09000\01\01705500000\0150.75\0100\00\0NASDAQ\0\0".to_string(),
                // Another Last trade
                "99\09000\01\01705500001\0150.80\050\02\0NYSE\0\0".to_string(),
                // Third Last trade
                "99\09000\01\01705500002\0150.70\0150\00\0NASDAQ\0\0".to_string(),
            ],
        );

        gateway.start().expect("Failed to start mock gateway");
        gateway
    }

    pub fn setup_tick_by_tick_all_last() -> MockGateway {
        let mut gateway = MockGateway::new(server_versions::TICK_BY_TICK);

        gateway.add_interaction(
            OutgoingMessages::RequestTickByTickData,
            vec![
                // TickByTick message - AllLast trade (includes all trades)
                // type(99), request_id, tick_type(2=AllLast), time, price, size,
                // tick_attrib_last(mask), exchange, special_conditions
                "99\09000\02\01705500000\0150.75\0100\00\0NASDAQ\0\0".to_string(),
                // Another AllLast trade (unreported trade)
                "99\09000\02\01705500001\0150.80\050\02\0DARK\0ISO\0".to_string(),
                // Third AllLast trade
                "99\09000\02\01705500002\0150.70\0150\00\0NYSE\0\0".to_string(),
            ],
        );

        gateway.start().expect("Failed to start mock gateway");
        gateway
    }

    pub fn setup_tick_by_tick_bid_ask() -> MockGateway {
        let mut gateway = MockGateway::new(server_versions::TICK_BY_TICK);

        gateway.add_interaction(
            OutgoingMessages::RequestTickByTickData,
            vec![
                // TickByTick message - BidAsk
                // type(99), request_id, tick_type(3=BidAsk), time, bid_price, ask_price,
                // bid_size, ask_size, tick_attrib_bid_ask(mask)
                "99\09000\03\01705500000\0150.50\0150.55\0100\0200\00\0".to_string(),
                // BidAsk update - bid past low
                "99\09000\03\01705500001\0150.45\0150.55\0150\0200\01\0".to_string(),
                // BidAsk update - ask past high
                "99\09000\03\01705500002\0150.45\0150.60\0150\0100\02\0".to_string(),
            ],
        );

        gateway.start().expect("Failed to start mock gateway");
        gateway
    }

    pub fn setup_tick_by_tick_midpoint() -> MockGateway {
        let mut gateway = MockGateway::new(server_versions::TICK_BY_TICK);

        gateway.add_interaction(
            OutgoingMessages::RequestTickByTickData,
            vec![
                // TickByTick message - MidPoint
                // type(99), request_id, tick_type(4=MidPoint), time, midpoint
                "99\09000\04\01705500000\0150.525\0".to_string(),
                // MidPoint update
                "99\09000\04\01705500001\0150.50\0".to_string(),
                // Another MidPoint update
                "99\09000\04\01705500002\0150.525\0".to_string(),
            ],
        );

        gateway.start().expect("Failed to start mock gateway");
        gateway
    }

    pub fn setup_market_depth() -> MockGateway {
        let mut gateway = MockGateway::new(server_versions::IPO_PRICES);

        gateway.add_interaction(
            OutgoingMessages::RequestMarketDepth,
            vec![
                // MarketDepth message type 12 version 1 (for is_smart_depth=false)
                // type(12), version(1), request_id(9000), position(0),
                // operation(0=insert), side(1=bid), price, size
                "12\01\09000\00\00\01\0150.50\0100\0".to_string(),
                // MarketDepth v1 - Ask insert at position 0
                "12\01\09000\00\00\00\0150.55\0200\0".to_string(),
                // MarketDepth v1 - Bid update at position 0
                "12\01\09000\00\01\01\0150.49\0150\0".to_string(),
                // MarketDepth v1 - Ask delete at position 0
                "12\01\09000\00\02\00\0150.55\00\0".to_string(),
            ],
        );

        gateway.start().expect("Failed to start mock gateway");
        gateway
    }

    pub fn setup_market_depth_exchanges() -> MockGateway {
        let mut gateway = MockGateway::new(server_versions::SERVICE_DATA_TYPE);

        gateway.add_interaction(
            OutgoingMessages::RequestMktDepthExchanges,
            vec![
                // MktDepthExchanges message
                // type(80), number_of_descriptions(3), then for each:
                // exchange, sec_type, listing_exchange, service_data_type, agg_group
                "80\03\0ISLAND\0STK\0NASDAQ\0Deep2\01\0NYSE\0STK\0NYSE\0Deep\02\0ARCA\0STK\0NYSE\0Deep\02\0".to_string(),
            ],
        );

        gateway.start().expect("Failed to start mock gateway");
        gateway
    }

    pub fn setup_switch_market_data_type() -> MockGateway {
        let mut gateway = MockGateway::new(server_versions::REQ_MARKET_DATA_TYPE);

        // Note: switch_market_data_type doesn't return a response, it's a fire-and-forget request
        // We just need to ensure the gateway accepts the request without error
        gateway.add_interaction(
            OutgoingMessages::RequestMarketDataType,
            vec![], // No response expected
        );

        gateway.start().expect("Failed to start mock gateway");
        gateway
    }

    // === Historical Data Test Setup Functions ===

    pub fn setup_head_timestamp() -> MockGateway {
        let mut gateway = MockGateway::new(server_versions::PEGBEST_PEGMID_OFFSETS);

        // Add response for head timestamp request
        gateway.add_interaction(
            OutgoingMessages::RequestHeadTimestamp,
            vec![
                "88\09000\01705311000\0".to_string(), // HeadTimestamp message: type=88, request_id=9000, timestamp=2024-01-15 09:30:00 UTC
            ],
        );

        gateway.start().expect("Failed to start mock gateway");
        gateway
    }

    pub fn setup_historical_data() -> MockGateway {
        let mut gateway = MockGateway::new(server_versions::IPO_PRICES);

        // Add response for historical data request
        gateway.add_interaction(
            OutgoingMessages::RequestHistoricalData,
            vec![
                // Message type=17, request_id=9000, start_date, end_date, bar_count=3, then bars
                "17\09000\020240122  09:30:00\020240122  09:45:00\03\01705398600\0150.25\0150.75\0150.00\0150.50\01000\0150.40\025\01705398900\0150.50\0151.00\0150.40\0150.90\01200\0150.70\030\01705399200\0150.90\0151.25\0150.85\0151.20\01500\0151.05\035\0".to_string(),
            ],
        );

        gateway.start().expect("Failed to start mock gateway");
        gateway
    }

    pub fn setup_historical_schedules() -> MockGateway {
        let mut gateway = MockGateway::new(server_versions::IPO_PRICES);

        // Add response for historical schedules request
        gateway.add_interaction(
            OutgoingMessages::RequestHistoricalData,
            vec![
                // Message type=106 (HistoricalSchedule), request_id=9000, start, end, timezone, session_count=1, then session data
                "106\09000\020240122-09:30:00\020240122-16:00:00\0US/Eastern\01\020240122-09:30:00\020240122-16:00:00\020240122\0".to_string(),
            ],
        );

        gateway.start().expect("Failed to start mock gateway");
        gateway
    }

    pub fn setup_historical_ticks_bid_ask() -> MockGateway {
        let mut gateway = MockGateway::new(server_versions::IPO_PRICES);

        // Add response for historical ticks bid/ask request
        gateway.add_interaction(
            OutgoingMessages::RequestHistoricalTicks,
            vec![
                // Message type=97 (HistoricalTickBidAsk), request_id=9000, number_of_ticks=3, then for each tick: timestamp, mask, priceBid, priceAsk, sizeBid, sizeAsk, then done flag
                "97\09000\03\01705920600\00\0150.25\0150.50\0100\0200\01705920605\00\0150.30\0150.55\0150\0250\01705920610\00\0150.35\0150.60\0200\0300\01\0".to_string(),
            ],
        );

        gateway.start().expect("Failed to start mock gateway");
        gateway
    }

    pub fn setup_historical_ticks_mid_point() -> MockGateway {
        let mut gateway = MockGateway::new(server_versions::IPO_PRICES);

        // Add response for historical ticks midpoint request
        gateway.add_interaction(
            OutgoingMessages::RequestHistoricalTicks,
            vec![
                // Message type=96 (HistoricalTick/Midpoint), request_id=9000, number_of_ticks=3, then for each tick: timestamp, skip_field, price, size (always 0), then done flag
                "96\09000\03\01705920600\00\0150.375\00\01705920605\00\0150.425\00\01705920610\00\0150.475\00\01\0".to_string(),
            ],
        );

        gateway.start().expect("Failed to start mock gateway");
        gateway
    }

    pub fn setup_historical_ticks_trade() -> MockGateway {
        let mut gateway = MockGateway::new(server_versions::IPO_PRICES);

        // Add response for historical ticks trade request
        gateway.add_interaction(
            OutgoingMessages::RequestHistoricalTicks,
            vec![
                // Message type=98 (HistoricalTickLast/Trade), request_id=9000, number_of_ticks=3, then for each tick: timestamp, mask, price, size, exchange, specialConditions, then done flag
                "98\09000\03\01705920600\00\0150.50\0100\0NASDAQ\0T\01705920605\00\0150.55\0200\0NYSE\0\01705920610\00\0150.60\0150\0NASDAQ\0\01\0"
                    .to_string(),
            ],
        );

        gateway.start().expect("Failed to start mock gateway");
        gateway
    }

    pub fn setup_histogram_data() -> MockGateway {
        let mut gateway = MockGateway::new(server_versions::IPO_PRICES);

        // Add response for histogram data request
        gateway.add_interaction(
            OutgoingMessages::RequestHistogramData,
            vec![
                // Message type=89, request_id=9000, count=3, then for each entry: price, size
                "89\09000\03\0150.00\01000\0150.50\01500\0151.00\0800\0".to_string(),
            ],
        );

        gateway.start().expect("Failed to start mock gateway");
        gateway
    }

    // === News Test Setup Functions ===

    pub fn setup_news_providers() -> MockGateway {
        let mut gateway = MockGateway::new(server_versions::REQ_NEWS_PROVIDERS);

        // Add response for news providers request
        gateway.add_interaction(
            OutgoingMessages::RequestNewsProviders,
            vec![
                // NewsProviders message: type=85, num_providers=3, then for each provider: code, name
                "85\03\0BRFG\0Briefing.com General Market Columns\0BRFUPDN\0Briefing.com Analyst Actions\0DJ-RT\0Dow Jones Real-Time News\0"
                    .to_string(),
            ],
        );

        gateway.start().expect("Failed to start mock gateway");
        gateway
    }

    pub fn setup_news_bulletins() -> MockGateway {
        let mut gateway = MockGateway::new(server_versions::IPO_PRICES);

        // Add response for news bulletins request
        gateway.add_interaction(
            OutgoingMessages::RequestNewsBulletins,
            vec![
                // NewsBulletins message: type=14, version=1, message_id, message_type, message, exchange
                "14\01\0123\01\0Important market announcement\0NYSE\0".to_string(),
                "14\01\0124\02\0Trading halt on symbol XYZ\0NASDAQ\0".to_string(),
            ],
        );

        gateway.start().expect("Failed to start mock gateway");
        gateway
    }

    pub fn setup_historical_news() -> MockGateway {
        let mut gateway = MockGateway::new(server_versions::REQ_HISTORICAL_NEWS);

        // Add response for historical news request
        gateway.add_interaction(
            OutgoingMessages::RequestHistoricalNews,
            vec![
                // HistoricalNews message: type=86, request_id=9000, time, provider_code, article_id, headline
                "86\09000\02024-01-15 14:30:00.000\0DJ-RT\0DJ001234\0Market hits new highs amid positive earnings\0".to_string(),
                "86\09000\02024-01-15 14:25:00.000\0BRFG\0BRF5678\0Federal Reserve announces policy decision\0".to_string(),
                // HistoricalNewsEnd message: type=87, request_id=9000, has_more=false
                "87\09000\00\0".to_string(),
            ],
        );

        gateway.start().expect("Failed to start mock gateway");
        gateway
    }

    pub fn setup_news_article() -> MockGateway {
        let mut gateway = MockGateway::new(server_versions::REQ_NEWS_ARTICLE);

        // Add response for news article request
        gateway.add_interaction(
            OutgoingMessages::RequestNewsArticle,
            vec![
                // NewsArticle message: type=83, request_id=9000, article_type=0 (text), article_text
                "83\09000\00\0This is the full text of the news article. It contains detailed information about the market event described in the headline.\0".to_string(),
            ],
        );

        gateway.start().expect("Failed to start mock gateway");
        gateway
    }

    pub fn setup_scanner_parameters() -> MockGateway {
        let mut gateway = MockGateway::new(server_versions::IPO_PRICES);

        // Add response for scanner parameters request
        gateway.add_interaction(
            OutgoingMessages::RequestScannerParameters,
            vec![
                // ScannerParameters message: type=19, version=1, xml_content
                "19\01\0<ScanParameterResponse><InstrumentList><Instrument>STK</Instrument><Instrument>OPT</Instrument></InstrumentList><LocationTree><Location>US</Location></LocationTree><ScanTypeList><ScanType>TOP_PERC_GAIN</ScanType></ScanTypeList></ScanParameterResponse>\0".to_string(),
            ],
        );

        gateway.start().expect("Failed to start mock gateway");
        gateway
    }

    pub fn setup_scanner_subscription() -> MockGateway {
        let mut gateway = MockGateway::new(server_versions::SCANNER_GENERIC_OPTS);

        // Add response for scanner subscription request
        gateway.add_interaction(
            OutgoingMessages::RequestScannerSubscription,
            vec![
                // ScannerData message: type=20, version, request_id=9000, number_of_items=2
                "20\03\09000\02\0".to_string() +
                // rank, contract_id, symbol, sec_type, expiry, strike, right, exchange, currency, local_symbol, market_name, trading_class
                "1\01234\0AAPL\0STK\0\00.0\0\0SMART\0USD\0AAPL\0NMS\0AAPL\0" +
                // distance, benchmark, projection, legs
                "\0\0\0\0" +
                // rank 2
                "2\05678\0GOOGL\0STK\0\00.0\0\0SMART\0USD\0GOOGL\0NMS\0GOOGL\0" +
                "\0\0\0\0",
                // ScannerDataEnd
                "20\03\09000\0-1\0".to_string(),
            ],
        );

        gateway.start().expect("Failed to start mock gateway");
        gateway
    }

    pub fn setup_wsh_metadata() -> MockGateway {
        let mut gateway = MockGateway::new(server_versions::WSHE_CALENDAR);

        // Add response for WSH metadata request
        gateway.add_interaction(
            OutgoingMessages::RequestWshMetaData,
            vec![
                // WshMetaData message: type=104, request_id=9000, data_json
                "104\09000\0{\"dataJson\":\"sample_metadata\"}\0".to_string(),
            ],
        );

        gateway.start().expect("Failed to start mock gateway");
        gateway
    }

    pub fn setup_wsh_event_data() -> MockGateway {
        let mut gateway = MockGateway::new(server_versions::WSHE_CALENDAR);

        // Add response for WSH event data request
        gateway.add_interaction(
            OutgoingMessages::RequestWshEventData,
            vec![
                // WshEventData message: type=105, request_id=9000, data_json, end_flag
                "105\09000\0{\"dataJson\":\"event1\"}\0".to_string(),
                "105\09000\0{\"dataJson\":\"event2\"}\0".to_string(),
                "105\09000\0\01\0".to_string(), // end flag = true
            ],
        );

        gateway.start().expect("Failed to start mock gateway");
        gateway
    }

    pub fn setup_contract_news() -> MockGateway {
        let mut gateway = MockGateway::new(server_versions::REQ_NEWS_ARTICLE);

        // Add response for contract news request (via market data with news ticks)
        gateway.add_interaction(
            OutgoingMessages::RequestMarketData,
            vec![
                // TickNews message: type=84, request_id=9000, time, provider_code, article_id, headline, extra_data
                "84\09000\01705920600000\0DJ-RT\0DJ001234\0Stock rises on earnings beat\0extraData1\0".to_string(),
                "84\09000\01705920700000\0BRFG\0BRF5678\0Company announces expansion\0extraData2\0".to_string(),
            ],
        );

        gateway.start().expect("Failed to start mock gateway");
        gateway
    }

    pub fn setup_broad_tape_news() -> MockGateway {
        let mut gateway = MockGateway::new(server_versions::REQ_NEWS_ARTICLE);

        // Add response for broad tape news request (via market data with news ticks)
        gateway.add_interaction(
            OutgoingMessages::RequestMarketData,
            vec![
                // TickNews message: type=84, request_id=9000, time, provider_code, article_id, headline, extra_data
                "84\09000\01705920600000\0BRFG\0BRF001\0Market update: Tech sector rallies\0extraData1\0".to_string(),
                "84\09000\01705920700000\0BRFG\0BRF002\0Fed minutes released\0extraData2\0".to_string(),
            ],
        );

        gateway.start().expect("Failed to start mock gateway");
        gateway
    }

    pub fn setup_wsh_event_data_by_filter() -> MockGateway {
        let mut gateway = MockGateway::new(server_versions::WSH_EVENT_DATA_FILTERS);

        // Add response for WSH event data by filter request
        gateway.add_interaction(
            OutgoingMessages::RequestWshEventData,
            vec![
                // WshEventData message: type=105, request_id=9000, data_json, end_flag
                "105\09000\0{\"dataJson\":\"filtered_event1\"}\0".to_string(),
                "105\09000\0{\"dataJson\":\"filtered_event2\"}\0".to_string(),
                "105\09000\0\01\0".to_string(), // end flag = true
            ],
        );

        gateway.start().expect("Failed to start mock gateway");
        gateway
    }
}
