use betex::{
    book::Book,
    book::protocol::command::{Command, CommandKind, Persistence, Side, TimeInForce},
    types::{AccountId, CorrelationId, MarketId, Money, OddsX10000, OrderId, RunnerId},
};

fn cid(n: u64) -> CorrelationId {
    CorrelationId(n.to_string())
}

fn exec(book: &mut Book, cmd: Command) {
    let (events, _) = book.handle(&cmd).expect("command should succeed");
    book.apply_all_events(&events);
}

fn place_odds(
    book: &mut Book,
    market_id: MarketId,
    correlation_id: u64,
    account_id: u64,
    runner_id: u32,
    side: Side,
) {
    exec(
        book,
        Command {
            correlation_id: Some(cid(correlation_id)),
            metadata: None,
            market_id,
            kind: CommandKind::PlaceOrder {
                runner_id: RunnerId(runner_id),
                account_id: AccountId::from(account_id),
                client_order_id: None,
                side,
                odds: OddsX10000(20_000),
                stake: Money(100),
                persistence: Persistence::Persist,
                time_in_force: TimeInForce::Gtc,
            },
        },
    );
}

#[test]
fn two_runner_active_order_count_tracks_lifecycle() {
    let market_id = MarketId(9001);
    let mut book = Book::new_two_runner(market_id, RunnerId(1), RunnerId(2));

    place_odds(&mut book, market_id, 1, 10, 1, Side::Yes);
    assert_eq!(book.active_order_count(), 1);

    place_odds(&mut book, market_id, 2, 20, 1, Side::No);
    assert_eq!(book.active_order_count(), 0);

    place_odds(&mut book, market_id, 3, 10, 1, Side::Yes);
    assert_eq!(book.active_order_count(), 1);

    exec(
        &mut book,
        Command {
            correlation_id: Some(cid(4)),
            metadata: None,
            market_id,
            kind: CommandKind::CancelOrder {
                order_id: OrderId(3),
                account_id: AccountId::from(10),
            },
        },
    );
    assert_eq!(book.active_order_count(), 0);
}

#[test]
fn multi_runner_active_order_count_tracks_lifecycle() {
    let market_id = MarketId(9002);
    let mut book = Book::new_multi_runner(market_id, [RunnerId(1), RunnerId(2), RunnerId(3)]);

    place_odds(&mut book, market_id, 1, 10, 1, Side::Yes);
    assert_eq!(book.active_order_count(), 1);

    place_odds(&mut book, market_id, 2, 20, 1, Side::No);
    assert_eq!(book.active_order_count(), 0);

    place_odds(&mut book, market_id, 3, 10, 2, Side::Yes);
    assert_eq!(book.active_order_count(), 1);

    exec(
        &mut book,
        Command {
            correlation_id: Some(cid(4)),
            metadata: None,
            market_id,
            kind: CommandKind::CancelOrder {
                order_id: OrderId(3),
                account_id: AccountId::from(10),
            },
        },
    );
    assert_eq!(book.active_order_count(), 0);
}

#[test]
fn binary_yes_active_order_count_tracks_lifecycle() {
    let market_id = MarketId(9003);
    let mut book = Book::new_binary_yes(market_id, RunnerId(1), RunnerId(2), 10_000);

    exec(
        &mut book,
        Command {
            correlation_id: Some(cid(1)),
            metadata: None,
            market_id,
            kind: CommandKind::PlaceBinaryOrder {
                account_id: AccountId::from(10),
                client_order_id: None,
                side: Side::Yes,
                price_ticks: 4000,
                qty_shares: 10,
                time_in_force: TimeInForce::Gtc,
            },
        },
    );
    exec(
        &mut book,
        Command {
            correlation_id: Some(cid(2)),
            metadata: None,
            market_id,
            kind: CommandKind::PlaceBinaryOrder {
                account_id: AccountId::from(20),
                client_order_id: None,
                side: Side::No,
                price_ticks: 7000,
                qty_shares: 10,
                time_in_force: TimeInForce::Gtc,
            },
        },
    );
    assert_eq!(book.active_order_count(), 2);

    exec(
        &mut book,
        Command {
            correlation_id: Some(cid(3)),
            metadata: None,
            market_id,
            kind: CommandKind::CancelOrder {
                order_id: OrderId(1),
                account_id: AccountId::from(10),
            },
        },
    );
    assert_eq!(book.active_order_count(), 1);

    exec(
        &mut book,
        Command {
            correlation_id: Some(cid(4)),
            metadata: None,
            market_id,
            kind: CommandKind::VoidMarket {
                reason: "VOID".to_string(),
            },
        },
    );
    assert_eq!(book.active_order_count(), 0);
}
