//! Integration tests for `Outcome::Scored` routing through `History::add_events`.

use smallvec::smallvec;
use trueskill_tt::{ConstantDrift, Event, History, Member, Outcome, Team};

#[test]
fn scored_two_team_one_event_pulls_winner_up() {
    let mut h: History = History::builder()
        .mu(0.0)
        .sigma(2.0)
        .beta(1.0)
        .drift(ConstantDrift(0.0))
        .score_sigma(1.0)
        .build();

    let events: Vec<Event<i64, &'static str>> = vec![Event {
        time: 1,
        teams: smallvec![
            Team::with_members([Member::new("a")]),
            Team::with_members([Member::new("b")]),
        ],
        outcome: Outcome::scores([10.0, 4.0]),
    }];
    h.add_events(events).unwrap();

    let mu_a = h.current_skill(&"a").unwrap().mu();
    let mu_b = h.current_skill(&"b").unwrap().mu();

    assert!(
        mu_a > 0.0,
        "winner mu should be pulled up; got mu_a = {mu_a}"
    );
    assert!(
        mu_b < 0.0,
        "loser mu should be pulled down; got mu_b = {mu_b}"
    );
    assert!(
        mu_a > mu_b,
        "winner mu should exceed loser mu; got mu_a = {mu_a}, mu_b = {mu_b}"
    );
}

#[test]
fn scored_zero_margin_treats_as_tie() {
    let mut h: History = History::builder()
        .mu(0.0)
        .sigma(2.0)
        .beta(1.0)
        .drift(ConstantDrift(0.0))
        .score_sigma(1.0)
        .build();

    let events: Vec<Event<i64, &'static str>> = vec![Event {
        time: 1,
        teams: smallvec![
            Team::with_members([Member::new("a")]),
            Team::with_members([Member::new("b")]),
        ],
        outcome: Outcome::scores([5.0, 5.0]),
    }];
    h.add_events(events).unwrap();

    let mu_a = h.current_skill(&"a").unwrap().mu();
    let mu_b = h.current_skill(&"b").unwrap().mu();
    let sigma_a = h.current_skill(&"a").unwrap().sigma();

    // Equal scores: posterior means stay symmetric around the prior mean.
    assert!(
        (mu_a - mu_b).abs() < 1e-9,
        "equal scores should leave mu_a == mu_b; got {mu_a} vs {mu_b}"
    );
    assert!(
        mu_a.abs() < 1e-9,
        "equal scores against equal priors should leave mu near zero; got {mu_a}"
    );

    // A zero-margin scored event still reduces uncertainty.
    assert!(
        sigma_a < 2.0,
        "expected sigma to tighten below prior 2.0; got {}",
        sigma_a
    );
}

#[test]
fn scored_three_team_partial_order() {
    let mut h: History = History::builder()
        .mu(0.0)
        .sigma(2.0)
        .beta(1.0)
        .drift(ConstantDrift(0.0))
        .score_sigma(1.0)
        .build();

    let events: Vec<Event<i64, &'static str>> = vec![Event {
        time: 1,
        teams: smallvec![
            Team::with_members([Member::new("a")]),
            Team::with_members([Member::new("b")]),
            Team::with_members([Member::new("c")]),
        ],
        outcome: Outcome::scores([9.0, 5.0, 1.0]),
    }];
    h.add_events(events).unwrap();

    let mu_a = h.current_skill(&"a").unwrap().mu();
    let mu_b = h.current_skill(&"b").unwrap().mu();
    let mu_c = h.current_skill(&"c").unwrap().mu();

    assert!(
        mu_a > mu_b,
        "team with highest score should rank highest; mu_a = {mu_a}, mu_b = {mu_b}"
    );
    assert!(
        mu_b > mu_c,
        "middle score should outrank lowest; mu_b = {mu_b}, mu_c = {mu_c}"
    );
}

#[test]
fn scored_rejects_outcome_team_count_mismatch() {
    use trueskill_tt::InferenceError;

    let mut h: History = History::builder().build();
    let events: Vec<Event<i64, &'static str>> = vec![Event {
        time: 1,
        teams: smallvec![
            Team::with_members([Member::new("a")]),
            Team::with_members([Member::new("b")]),
        ],
        outcome: Outcome::scores([10.0, 4.0, 1.0]), // 3 scores, 2 teams
    }];

    let err = h.add_events(events).unwrap_err();
    assert!(
        matches!(err, InferenceError::MismatchedShape { .. }),
        "expected MismatchedShape error, got {err:?}"
    );
}
