#![allow(
    clippy::tests_outside_test_module,
    clippy::unwrap_used,
    clippy::expect_used,
    clippy::indexing_slicing,
    clippy::str_to_string,
    clippy::string_to_string,
    clippy::assertions_on_result_states,
    clippy::assertions_on_constants,
    clippy::let_underscore_must_use,
    clippy::use_debug,
    clippy::doc_markdown,
    clippy::shadow_unrelated,
    clippy::shadow_reuse,
    clippy::similar_names,
    clippy::clone_on_copy,
    clippy::get_unwrap,
    clippy::modulo_arithmetic,
    clippy::integer_division,
    clippy::non_ascii_literal,
    clippy::useless_vec,
    clippy::useless_format,
    clippy::wildcard_enum_match_arm,
    clippy::manual_range_contains,
    clippy::const_is_empty,
    clippy::needless_range_loop,
    clippy::field_reassign_with_default,
    clippy::items_after_test_module,
    clippy::missing_const_for_fn,
    unused_imports,
    unused_variables,
    unused_mut,
    unused_assignments,
    unused_comparisons,
    unused_must_use,
    dead_code,
)]
//! Integration test for real IBKR TWS/Gateway connectivity.
//!
//! These tests require a running IB Gateway or TWS on localhost:4002 (paper trading port).
//! All tests are `#[ignore]` by default so they never run in CI or casual `cargo test`.
//!
//! Run manually with:
//! ```sh
//! SQLX_OFFLINE=true cargo test -p trading_engine --features interactive-brokers \
//!     -- ibkr_connectivity --ignored --nocapture
//! ```
//!
//! Environment variables:
//! - `IBKR_ACCOUNT_ID` - IB paper trading account (default: `DU9600528`)
//! - `IBKR_HOST`       - Gateway host (default: `127.0.0.1`)
//! - `IBKR_PORT`       - Gateway port (default: `4002`)

#[cfg(feature = "interactive-brokers")]
mod ibkr_tests {
    use trading_engine::brokers::config::InteractiveBrokersConfig;
    use trading_engine::brokers::interactive_brokers::InteractiveBrokersClient;
    use trading_engine::trading::data_interface::BrokerInterface;

    use std::sync::atomic::{AtomicI32, Ordering};

    /// Each test gets a unique client_id to avoid IB Gateway rejecting
    /// parallel connections (only one connection per client_id is allowed).
    static NEXT_CLIENT_ID: AtomicI32 = AtomicI32::new(100);

    fn paper_trading_config() -> InteractiveBrokersConfig {
        let client_id = NEXT_CLIENT_ID.fetch_add(1, Ordering::SeqCst);
        InteractiveBrokersConfig {
            enabled: true,
            account_id: Some(
                std::env::var("IBKR_ACCOUNT_ID").unwrap_or_else(|_| "DU9600528".to_owned()),
            ),
            host: std::env::var("IBKR_HOST").unwrap_or_else(|_| "127.0.0.1".to_owned()),
            port: std::env::var("IBKR_PORT")
                .ok()
                .and_then(|p| p.parse().ok())
                .unwrap_or(4002),
            client_id,
        }
    }

    #[tokio::test]
    #[ignore = "Requires running IB Gateway on localhost:4002"]
    async fn ibkr_connectivity_connect_disconnect() {
        let config = paper_trading_config();
        println!(
            "Connecting to IB Gateway at {}:{}  (client_id={})",
            config.host, config.port, config.client_id
        );

        let mut client = InteractiveBrokersClient::new(config);

        let result = client.connect().await;
        assert!(result.is_ok(), "Failed to connect: {:?}", result.err());
        assert!(client.is_connected(), "Client should report connected");

        let result = client.disconnect().await;
        assert!(
            result.is_ok(),
            "Failed to disconnect: {:?}",
            result.err()
        );
        assert!(
            !client.is_connected(),
            "Client should report disconnected"
        );
    }

    #[tokio::test]
    #[ignore = "Requires running IB Gateway on localhost:4002"]
    async fn ibkr_connectivity_account_info() {
        let config = paper_trading_config();
        let mut client = InteractiveBrokersClient::new(config);
        client
            .connect()
            .await
            .expect("Failed to connect to IB Gateway");

        let info = client.get_account_info().await;
        assert!(
            info.is_ok(),
            "Failed to get account info: {:?}",
            info.err()
        );
        let info = info.expect("already checked is_ok");
        println!("Account info ({} fields):", info.len());
        for (key, value) in &info {
            println!("  {} = {}", key, value);
        }
        assert!(!info.is_empty(), "Account info should not be empty");

        client.disconnect().await.ok();
    }

    #[tokio::test]
    #[ignore = "Requires running IB Gateway on localhost:4002"]
    async fn ibkr_connectivity_positions() {
        let config = paper_trading_config();
        let mut client = InteractiveBrokersClient::new(config);
        client
            .connect()
            .await
            .expect("Failed to connect to IB Gateway");

        let positions = client.get_positions().await;
        assert!(
            positions.is_ok(),
            "Failed to get positions: {:?}",
            positions.err()
        );
        let positions = positions.expect("already checked is_ok");
        println!("Open positions: {}", positions.len());
        for pos in &positions {
            println!("  {:?}", pos);
        }

        client.disconnect().await.ok();
    }

    #[tokio::test]
    #[ignore = "Requires running IB Gateway on localhost:4002"]
    async fn ibkr_connectivity_connection_status() {
        use trading_engine::trading::data_interface::BrokerConnectionStatus;

        let config = paper_trading_config();
        let mut client = InteractiveBrokersClient::new(config);

        // Before connect
        assert_eq!(
            client.connection_status(),
            BrokerConnectionStatus::Disconnected,
            "Should be disconnected before connect()"
        );

        client
            .connect()
            .await
            .expect("Failed to connect to IB Gateway");

        // After connect
        assert_eq!(
            client.connection_status(),
            BrokerConnectionStatus::Connected,
            "Should be connected after connect()"
        );

        // Broker name
        assert_eq!(client.broker_name(), "Interactive Brokers");

        client.disconnect().await.ok();
    }

    #[tokio::test]
    #[ignore = "Requires running IB Gateway on localhost:4002"]
    async fn ibkr_connectivity_submit_and_cancel_paper_order() {
        use chrono::Utc;
        use common::{OrderSide, OrderStatus, OrderType, TimeInForce};
        use rust_decimal::Decimal;
        use std::collections::HashMap;
        use trading_engine::trading_operations::TradingOrder;

        let config = paper_trading_config();
        let mut client = InteractiveBrokersClient::new(config);
        client
            .connect()
            .await
            .expect("Failed to connect to IB Gateway");

        // Create a limit BUY order far below market to avoid fills.
        // ES micro futures at an absurdly low price ensures no execution.
        let order = TradingOrder {
            id: common::OrderId::new(),
            symbol: "ES.FUT".to_owned(),
            side: OrderSide::Buy,
            order_type: OrderType::Limit,
            quantity: Decimal::from(1),
            price: Decimal::from(1000), // Far below market (~5500 at time of writing)
            time_in_force: TimeInForce::Day,
            account_id: None,
            metadata: {
                let mut m = HashMap::new();
                m.insert("exchange".to_owned(), "CME".to_owned());
                m.insert("currency".to_owned(), "USD".to_owned());
                m
            },
            created_at: Utc::now(),
            submitted_at: None,
            executed_at: None,
            status: OrderStatus::Created,
            fill_quantity: Decimal::ZERO,
            average_fill_price: None,
        };

        let submit_result = client.submit_order(&order).await;
        assert!(
            submit_result.is_ok(),
            "Failed to submit paper order: {:?}",
            submit_result.err()
        );
        let broker_order_id = submit_result.expect("already checked is_ok");
        println!("Paper order submitted, broker_order_id={}", broker_order_id);

        // Give TWS a moment to process
        tokio::time::sleep(std::time::Duration::from_secs(1)).await;

        // Verify order status
        let status = client.get_order_status(&broker_order_id).await;
        println!("Order status: {:?}", status);

        // Cancel the order
        let cancel_result = client.cancel_order(&broker_order_id).await;
        assert!(
            cancel_result.is_ok(),
            "Failed to cancel paper order: {:?}",
            cancel_result.err()
        );
        println!("Paper order {} cancelled", broker_order_id);

        client.disconnect().await.ok();
    }

    #[tokio::test]
    #[ignore = "Requires running IB Gateway on localhost:4002"]
    async fn ibkr_connectivity_heartbeat() {
        let config = paper_trading_config();
        let mut client = InteractiveBrokersClient::new(config);
        client
            .connect()
            .await
            .expect("Failed to connect to IB Gateway");

        // Heartbeat should succeed while connected
        let result = client.send_heartbeat().await;
        assert!(
            result.is_ok(),
            "Heartbeat failed: {:?}",
            result.err()
        );

        client.disconnect().await.ok();
    }
}
