use trueskill_tt::{
    ConstantDrift, ConvergenceOptions, Game, GameOptions, Gaussian, InferenceError, Outcome, Rating,
};

type R = Rating<i64, ConstantDrift>;

fn default_rating() -> R {
    R::new(
        Gaussian::from_ms(25.0, 25.0 / 3.0),
        25.0 / 6.0,
        ConstantDrift(25.0 / 300.0),
    )
}

#[test]
fn game_ranked_1v1_golden() {
    let a = default_rating();
    let b = default_rating();
    let g = Game::<i64, _>::ranked(
        &[&[a], &[b]],
        Outcome::winner(0, 2),
        &GameOptions::default(),
    )
    .unwrap();
    let p = g.posteriors();
    assert!(p[0][0].mu() > 25.0);
    assert!(p[1][0].mu() < 25.0);
    assert!((p[0][0].sigma() - p[1][0].sigma()).abs() < 1e-6);
}

#[test]
fn game_one_v_one_shortcut() {
    let a = default_rating();
    let b = default_rating();
    let (a_post, b_post) = Game::<i64, _>::one_v_one(&a, &b, Outcome::winner(0, 2)).unwrap();
    assert!(a_post.mu() > 25.0);
    assert!(b_post.mu() < 25.0);
}

#[test]
fn game_ranked_rejects_bad_p_draw() {
    let a = R::new(Gaussian::default(), 1.0, ConstantDrift(0.0));
    let err = Game::<i64, _>::ranked(
        &[&[a], &[a]],
        Outcome::winner(0, 2),
        &GameOptions {
            p_draw: 1.5,
            score_sigma: 1.0,
            convergence: ConvergenceOptions::default(),
        },
    )
    .unwrap_err();
    assert!(matches!(err, InferenceError::InvalidProbability { .. }));
}

#[test]
fn game_ranked_rejects_mismatched_ranks() {
    let a = R::new(Gaussian::default(), 1.0, ConstantDrift(0.0));
    let err = Game::<i64, _>::ranked(
        &[&[a], &[a]],
        Outcome::ranking([0, 1, 2]),
        &GameOptions::default(),
    )
    .unwrap_err();
    assert!(matches!(err, InferenceError::MismatchedShape { .. }));
}

#[test]
fn game_free_for_all_three_players() {
    let a = default_rating();
    let b = default_rating();
    let c = default_rating();
    let g = Game::<i64, _>::free_for_all(
        &[&a, &b, &c],
        Outcome::ranking([0, 1, 2]),
        &GameOptions::default(),
    )
    .unwrap();
    let p = g.posteriors();
    assert_eq!(p.len(), 3);
    assert!(p[0][0].mu() > p[1][0].mu());
    assert!(p[1][0].mu() > p[2][0].mu());
}

#[test]
fn game_log_evidence_is_finite() {
    let a = default_rating();
    let b = default_rating();
    let g = Game::<i64, _>::ranked(
        &[&[a], &[b]],
        Outcome::winner(0, 2),
        &GameOptions::default(),
    )
    .unwrap();
    assert!(g.log_evidence().is_finite());
    assert!(g.log_evidence() < 0.0);
}
