use betex::{
    book::protocol::{
        command::{Command, CommandKind, Side},
        reject::RejectReason,
    },
    config::Config,
    engine::state::{EngineState, Market},
    types::*,
};
mod common;
use common::two_runner_market_config;

#[test]
fn single_runner_market_is_rejected() {
    let cfg = Config::default();
    let mut state = EngineState::new(cfg);

    // Single runner market should be invalid
    let err = state
        .handle_command(&Command {
            correlation_id: Some(CorrelationId(1.to_string())),
            metadata: None,
            market_id: MarketId(1),
            kind: CommandKind::CreateMarket {
                name: String::new(),
                market_model: MarketModel::ExchangeOdds,
                runner_ids: vec![RunnerId(1)],
                runner_labels: vec![],
                market_kind: MarketKind::InPlayCapable,
                market_phase: MarketPhase::Pre,
            },
        })
        .expect_err("single runner market should be rejected");

    assert_eq!(err, RejectReason::InvalidMarketConfig);
}

#[test]
fn market_already_exists_is_rejected() {
    let cfg = Config::default();
    let mut state = EngineState::new(cfg.clone());

    state.markets.insert(
        MarketId(1),
        Market::from_config_with_cfg(&cfg, two_runner_market_config(MarketId(1))),
    );

    let err = state
        .handle_command(&Command {
            correlation_id: Some(CorrelationId(1.to_string())),
            metadata: None,
            market_id: MarketId(1),
            kind: CommandKind::CreateMarket {
                name: String::from("dup"),
                market_model: MarketModel::ExchangeOdds,
                runner_ids: vec![RunnerId(1), RunnerId(2)],
                runner_labels: vec![],
                market_kind: MarketKind::InPlayCapable,
                market_phase: MarketPhase::Pre,
            },
        })
        .expect_err("duplicate market should be rejected");

    assert_eq!(err, RejectReason::MarketAlreadyExists);
}

#[test]
fn runner_label_mismatch_is_rejected() {
    let cfg = Config::default();
    let mut state = EngineState::new(cfg);

    let err = state
        .handle_command(&Command {
            correlation_id: Some(CorrelationId(1.to_string())),
            metadata: None,
            market_id: MarketId(2),
            kind: CommandKind::CreateMarket {
                name: String::from("labels"),
                market_model: MarketModel::ExchangeOdds,
                runner_ids: vec![RunnerId(1), RunnerId(2)],
                runner_labels: vec![String::from("only-one")],
                market_kind: MarketKind::InPlayCapable,
                market_phase: MarketPhase::Pre,
            },
        })
        .expect_err("runner label mismatch should be rejected");

    assert_eq!(err, RejectReason::InvalidMarketConfig);
}

#[test]
fn missing_runner_labels_are_rejected() {
    let cfg = Config::default();
    let mut state = EngineState::new(cfg);

    let err = state
        .handle_command(&Command {
            correlation_id: Some(CorrelationId(1.to_string())),
            metadata: None,
            market_id: MarketId(3),
            kind: CommandKind::CreateMarket {
                name: String::from("missing-labels"),
                market_model: MarketModel::ExchangeOdds,
                runner_ids: vec![RunnerId(1), RunnerId(2)],
                runner_labels: vec![],
                market_kind: MarketKind::InPlayCapable,
                market_phase: MarketPhase::Pre,
            },
        })
        .expect_err("missing runner labels should be rejected");

    assert_eq!(err, RejectReason::InvalidMarketConfig);
}

#[test]
fn duplicate_runner_labels_are_rejected() {
    let cfg = Config::default();
    let mut state = EngineState::new(cfg);

    let err = state
        .handle_command(&Command {
            correlation_id: Some(CorrelationId(1.to_string())),
            metadata: None,
            market_id: MarketId(4),
            kind: CommandKind::CreateMarket {
                name: String::from("duplicate-labels"),
                market_model: MarketModel::ExchangeOdds,
                runner_ids: vec![RunnerId(1), RunnerId(2)],
                runner_labels: vec![String::from("dup"), String::from("dup")],
                market_kind: MarketKind::InPlayCapable,
                market_phase: MarketPhase::Pre,
            },
        })
        .expect_err("duplicate runner labels should be rejected");

    assert_eq!(err, RejectReason::InvalidMarketConfig);
}

#[test]
fn command_for_missing_market_is_rejected() {
    let cfg = Config::default();
    let mut state = EngineState::new(cfg);

    let err = state
        .handle_command(&Command {
            correlation_id: Some(CorrelationId(1.to_string())),
            metadata: None,
            market_id: MarketId(99),
            kind: CommandKind::PlaceOrder {
                runner_id: RunnerId(1),
                account_id: AccountId::from(1),
                client_order_id: None,
                side: Side::Yes,
                odds: OddsX10000(20000),
                stake: Money(100),
                persistence: betex::book::Persistence::Persist,
                time_in_force: betex::book::TimeInForce::Gtc,
            },
        })
        .expect_err("command for unknown market should be rejected");

    assert_eq!(err, RejectReason::MarketNotFound);
}
