//! Match reversal/deletion tests
//!
//! Ensures that deleting matches correctly reverts ratings

use sqlx::SqlitePool;
use sqlx::sqlite::SqlitePoolOptions;

async fn setup_test_db() -> SqlitePool {
    let pool = SqlitePoolOptions::new()
        .max_connections(1)
        .connect("sqlite::memory:")
        .await
        .expect("Failed to create test database");
    
    sqlx::query(r#"
        CREATE TABLE players (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            name TEXT NOT NULL UNIQUE,
            email TEXT DEFAULT '',
            singles_rating REAL DEFAULT 1500.0,
            singles_rd REAL DEFAULT 350.0,
            doubles_rating REAL DEFAULT 1500.0,
            doubles_rd REAL DEFAULT 350.0,
            created_at TEXT DEFAULT (datetime('now'))
        )
    "#).execute(&pool).await.unwrap();
    
    sqlx::query(r#"
        CREATE TABLE matches (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            match_type TEXT NOT NULL,
            team1_score INTEGER NOT NULL,
            team2_score INTEGER NOT NULL,
            timestamp TEXT DEFAULT (datetime('now')),
            session_id INTEGER
        )
    "#).execute(&pool).await.unwrap();
    
    sqlx::query(r#"
        CREATE TABLE match_participants (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            match_id INTEGER NOT NULL,
            player_id INTEGER NOT NULL,
            team INTEGER NOT NULL,
            rating_before REAL,
            rating_after REAL,
            rating_change REAL,
            FOREIGN KEY (match_id) REFERENCES matches(id),
            FOREIGN KEY (player_id) REFERENCES players(id)
        )
    "#).execute(&pool).await.unwrap();
    
    pool
}

#[tokio::test]
async fn test_singles_match_reversal() {
    let pool = setup_test_db().await;
    
    // Create two players
    sqlx::query("INSERT INTO players (name, singles_rating) VALUES (?, ?)")
        .bind("Reversal A")
        .bind(1500.0)
        .execute(&pool)
        .await
        .unwrap();
    
    sqlx::query("INSERT INTO players (name, singles_rating) VALUES (?, ?)")
        .bind("Reversal B")
        .bind(1500.0)
        .execute(&pool)
        .await
        .unwrap();
    
    let pa_id: (i64,) = sqlx::query_as("SELECT id FROM players WHERE name = ?")
        .bind("Reversal A")
        .fetch_one(&pool)
        .await
        .unwrap();
    
    let pb_id: (i64,) = sqlx::query_as("SELECT id FROM players WHERE name = ?")
        .bind("Reversal B")
        .fetch_one(&pool)
        .await
        .unwrap();
    
    // Record match with rating changes
    let match_result = sqlx::query(
        "INSERT INTO matches (match_type, team1_score, team2_score) VALUES (?, ?, ?)"
    )
    .bind("singles")
    .bind(11)
    .bind(5)
    .execute(&pool)
    .await
    .unwrap();
    
    let match_id = match_result.last_insert_rowid();
    
    let rating_change_a = 10.0;
    let rating_change_b = -10.0;
    
    // Record participants with rating changes
    sqlx::query(
        "INSERT INTO match_participants (match_id, player_id, team, rating_before, rating_after, rating_change) VALUES (?, ?, ?, ?, ?, ?)"
    )
    .bind(match_id)
    .bind(pa_id.0)
    .bind(1)
    .bind(1500.0)
    .bind(1510.0)
    .bind(rating_change_a)
    .execute(&pool)
    .await
    .unwrap();
    
    sqlx::query(
        "INSERT INTO match_participants (match_id, player_id, team, rating_before, rating_after, rating_change) VALUES (?, ?, ?, ?, ?, ?)"
    )
    .bind(match_id)
    .bind(pb_id.0)
    .bind(2)
    .bind(1500.0)
    .bind(1490.0)
    .bind(rating_change_b)
    .execute(&pool)
    .await
    .unwrap();
    
    // Apply rating changes
    sqlx::query("UPDATE players SET singles_rating = singles_rating + ? WHERE id = ?")
        .bind(rating_change_a)
        .bind(pa_id.0)
        .execute(&pool)
        .await
        .unwrap();
    
    sqlx::query("UPDATE players SET singles_rating = singles_rating + ? WHERE id = ?")
        .bind(rating_change_b)
        .bind(pb_id.0)
        .execute(&pool)
        .await
        .unwrap();
    
    // Verify ratings changed
    let rating_a: (f64,) = sqlx::query_as("SELECT singles_rating FROM players WHERE id = ?")
        .bind(pa_id.0)
        .fetch_one(&pool)
        .await
        .unwrap();
    assert!((rating_a.0 - 1510.0).abs() < 0.01);
    
    let rating_b: (f64,) = sqlx::query_as("SELECT singles_rating FROM players WHERE id = ?")
        .bind(pb_id.0)
        .fetch_one(&pool)
        .await
        .unwrap();
    assert!((rating_b.0 - 1490.0).abs() < 0.01);
    
    // NOW REVERSE THE MATCH
    // Get rating changes to revert
    let participants: Vec<(i64, f64)> = sqlx::query_as(
        "SELECT player_id, rating_change FROM match_participants WHERE match_id = ?"
    )
    .bind(match_id)
    .fetch_all(&pool)
    .await
    .unwrap();
    
    // Revert each player's rating
    for (player_id, change) in participants {
        sqlx::query("UPDATE players SET singles_rating = singles_rating - ? WHERE id = ?")
            .bind(change)
            .bind(player_id)
            .execute(&pool)
            .await
            .unwrap();
    }
    
    // Delete match participants and match
    sqlx::query("DELETE FROM match_participants WHERE match_id = ?")
        .bind(match_id)
        .execute(&pool)
        .await
        .unwrap();
    
    sqlx::query("DELETE FROM matches WHERE id = ?")
        .bind(match_id)
        .execute(&pool)
        .await
        .unwrap();
    
    // Verify ratings are back to original
    let final_rating_a: (f64,) = sqlx::query_as("SELECT singles_rating FROM players WHERE id = ?")
        .bind(pa_id.0)
        .fetch_one(&pool)
        .await
        .unwrap();
    assert!((final_rating_a.0 - 1500.0).abs() < 0.01, 
        "Player A rating not reverted: {}", final_rating_a.0);
    
    let final_rating_b: (f64,) = sqlx::query_as("SELECT singles_rating FROM players WHERE id = ?")
        .bind(pb_id.0)
        .fetch_one(&pool)
        .await
        .unwrap();
    assert!((final_rating_b.0 - 1500.0).abs() < 0.01,
        "Player B rating not reverted: {}", final_rating_b.0);
}

#[tokio::test]
async fn test_doubles_match_reversal() {
    let pool = setup_test_db().await;
    
    // Create four players
    let players = [
        ("D1", 1500.0), ("D2", 1520.0), ("D3", 1480.0), ("D4", 1500.0)
    ];
    
    for (name, rating) in &players {
        sqlx::query("INSERT INTO players (name, singles_rating) VALUES (?, ?)")
            .bind(*name)
            .bind(*rating)
            .execute(&pool)
            .await
            .unwrap();
    }
    
    let d1_id: (i64,) = sqlx::query_as("SELECT id FROM players WHERE name = 'D1'")
        .fetch_one(&pool).await.unwrap();
    let d2_id: (i64,) = sqlx::query_as("SELECT id FROM players WHERE name = 'D2'")
        .fetch_one(&pool).await.unwrap();
    let d3_id: (i64,) = sqlx::query_as("SELECT id FROM players WHERE name = 'D3'")
        .fetch_one(&pool).await.unwrap();
    let d4_id: (i64,) = sqlx::query_as("SELECT id FROM players WHERE name = 'D4'")
        .fetch_one(&pool).await.unwrap();
    
    let d1_id = d1_id.0;
    let d2_id = d2_id.0;
    let d3_id = d3_id.0;
    let d4_id = d4_id.0;
    
    // Record doubles match
    let match_result = sqlx::query(
        "INSERT INTO matches (match_type, team1_score, team2_score) VALUES (?, ?, ?)"
    )
    .bind("doubles")
    .bind(11)
    .bind(8)
    .execute(&pool)
    .await
    .unwrap();
    
    let match_id = match_result.last_insert_rowid();
    
    // Team 1 (D1 + D2) wins, Team 2 (D3 + D4) loses
    // Each player has different rating change due to effective opponent
    let changes = [
        (d1_id, 1, 8.0),
        (d2_id, 1, 5.0),  // Stronger player gains less
        (d3_id, 2, -7.0),
        (d4_id, 2, -6.0),
    ];
    
    for (player_id, team, change) in &changes {
        let before = match *team {
            1 => if *player_id == d1_id { 1500.0 } else { 1520.0 },
            _ => if *player_id == d3_id { 1480.0 } else { 1500.0 },
        };
        
        sqlx::query(
            "INSERT INTO match_participants (match_id, player_id, team, rating_before, rating_after, rating_change) VALUES (?, ?, ?, ?, ?, ?)"
        )
        .bind(match_id)
        .bind(player_id)
        .bind(team)
        .bind(before)
        .bind(before + change)
        .bind(change)
        .execute(&pool)
        .await
        .unwrap();
        
        // Apply rating change
        sqlx::query("UPDATE players SET singles_rating = singles_rating + ? WHERE id = ?")
            .bind(change)
            .bind(player_id)
            .execute(&pool)
            .await
            .unwrap();
    }
    
    // Store original ratings for comparison
    let original_ratings: Vec<(i64, f64)> = vec![
        (d1_id, 1500.0), (d2_id, 1520.0), (d3_id, 1480.0), (d4_id, 1500.0)
    ];
    
    // REVERSE THE MATCH
    let participants: Vec<(i64, f64)> = sqlx::query_as(
        "SELECT player_id, rating_change FROM match_participants WHERE match_id = ?"
    )
    .bind(match_id)
    .fetch_all(&pool)
    .await
    .unwrap();
    
    for (player_id, change) in participants {
        sqlx::query("UPDATE players SET singles_rating = singles_rating - ? WHERE id = ?")
            .bind(change)
            .bind(player_id)
            .execute(&pool)
            .await
            .unwrap();
    }
    
    sqlx::query("DELETE FROM match_participants WHERE match_id = ?")
        .bind(match_id)
        .execute(&pool)
        .await
        .unwrap();
    
    sqlx::query("DELETE FROM matches WHERE id = ?")
        .bind(match_id)
        .execute(&pool)
        .await
        .unwrap();
    
    // Verify all ratings reverted
    for (player_id, original_rating) in original_ratings {
        let final_rating: (f64,) = sqlx::query_as("SELECT singles_rating FROM players WHERE id = ?")
            .bind(player_id)
            .fetch_one(&pool)
            .await
            .unwrap();
        
        assert!((final_rating.0 - original_rating).abs() < 0.01,
            "Player {} rating not reverted: {} (expected {})", 
            player_id, final_rating.0, original_rating);
    }
}

#[tokio::test]
async fn test_multiple_match_reversal() {
    let pool = setup_test_db().await;
    
    sqlx::query("INSERT INTO players (name, singles_rating) VALUES (?, ?)")
        .bind("Multi A")
        .bind(1500.0)
        .execute(&pool)
        .await
        .unwrap();
    
    sqlx::query("INSERT INTO players (name, singles_rating) VALUES (?, ?)")
        .bind("Multi B")
        .bind(1500.0)
        .execute(&pool)
        .await
        .unwrap();
    
    let pa_id: (i64,) = sqlx::query_as("SELECT id FROM players WHERE name = ?")
        .bind("Multi A")
        .fetch_one(&pool)
        .await
        .unwrap();
    
    let pb_id: (i64,) = sqlx::query_as("SELECT id FROM players WHERE name = ?")
        .bind("Multi B")
        .fetch_one(&pool)
        .await
        .unwrap();
    
    // Record 5 matches
    let mut match_ids = vec![];
    let mut changes_a = vec![];
    let mut changes_b = vec![];
    
    for i in 0..5 {
        let score1 = 11;
        let score2 = 5 + i;
        let change_a = 10.0 - i as f64;
        let change_b = -(10.0 - i as f64);
        
        let match_result = sqlx::query(
            "INSERT INTO matches (match_type, team1_score, team2_score) VALUES (?, ?, ?)"
        )
        .bind("singles")
        .bind(score1)
        .bind(score2)
        .execute(&pool)
        .await
        .unwrap();
        
        let match_id = match_result.last_insert_rowid();
        match_ids.push(match_id);
        changes_a.push(change_a);
        changes_b.push(change_b);
        
        // Record participants
        sqlx::query(
            "INSERT INTO match_participants (match_id, player_id, team, rating_change) VALUES (?, ?, ?, ?)"
        )
        .bind(match_id)
        .bind(pa_id.0)
        .bind(1)
        .bind(change_a)
        .execute(&pool)
        .await
        .unwrap();
        
        sqlx::query(
            "INSERT INTO match_participants (match_id, player_id, team, rating_change) VALUES (?, ?, ?, ?)"
        )
        .bind(match_id)
        .bind(pb_id.0)
        .bind(2)
        .bind(change_b)
        .execute(&pool)
        .await
        .unwrap();
        
        // Apply changes
        sqlx::query("UPDATE players SET singles_rating = singles_rating + ? WHERE id = ?")
            .bind(change_a)
            .bind(pa_id.0)
            .execute(&pool)
            .await
            .unwrap();
        
        sqlx::query("UPDATE players SET singles_rating = singles_rating + ? WHERE id = ?")
            .bind(change_b)
            .bind(pb_id.0)
            .execute(&pool)
            .await
            .unwrap();
    }
    
    // Verify ratings after 5 matches
    // A gains: 10+9+8+7+6 = 40
    // B loses: -40
    let rating_a: (f64,) = sqlx::query_as("SELECT singles_rating FROM players WHERE id = ?")
        .bind(pa_id.0)
        .fetch_one(&pool)
        .await
        .unwrap();
    assert!((rating_a.0 - 1540.0).abs() < 0.01);
    
    // Delete matches in REVERSE order (LIFO - last in, first out)
    for match_id in match_ids.iter().rev() {
        let participants: Vec<(i64, f64)> = sqlx::query_as(
            "SELECT player_id, rating_change FROM match_participants WHERE match_id = ?"
        )
        .bind(match_id)
        .fetch_all(&pool)
        .await
        .unwrap();
        
        for (player_id, change) in participants {
            sqlx::query("UPDATE players SET singles_rating = singles_rating - ? WHERE id = ?")
                .bind(change)
                .bind(player_id)
                .execute(&pool)
                .await
                .unwrap();
        }
        
        sqlx::query("DELETE FROM match_participants WHERE match_id = ?")
            .bind(match_id)
            .execute(&pool)
            .await
            .unwrap();
        
        sqlx::query("DELETE FROM matches WHERE id = ?")
            .bind(match_id)
            .execute(&pool)
            .await
            .unwrap();
    }
    
    // Verify both players back to 1500
    let final_a: (f64,) = sqlx::query_as("SELECT singles_rating FROM players WHERE id = ?")
        .bind(pa_id.0)
        .fetch_one(&pool)
        .await
        .unwrap();
    assert!((final_a.0 - 1500.0).abs() < 0.01);
    
    let final_b: (f64,) = sqlx::query_as("SELECT singles_rating FROM players WHERE id = ?")
        .bind(pb_id.0)
        .fetch_one(&pool)
        .await
        .unwrap();
    assert!((final_b.0 - 1500.0).abs() < 0.01);
}

#[tokio::test]
async fn test_reversal_preserves_other_matches() {
    let pool = setup_test_db().await;
    
    // Create 3 players: A plays both B and C
    for (name, rating) in [("Preserve A", 1500.0), ("Preserve B", 1500.0), ("Preserve C", 1500.0)] {
        sqlx::query("INSERT INTO players (name, singles_rating) VALUES (?, ?)")
            .bind(name)
            .bind(rating)
            .execute(&pool)
            .await
            .unwrap();
    }
    
    let pa_id: (i64,) = sqlx::query_as("SELECT id FROM players WHERE name = ?")
        .bind("Preserve A")
        .fetch_one(&pool)
        .await
        .unwrap();
    
    let pb_id: (i64,) = sqlx::query_as("SELECT id FROM players WHERE name = ?")
        .bind("Preserve B")
        .fetch_one(&pool)
        .await
        .unwrap();
    
    let pc_id: (i64,) = sqlx::query_as("SELECT id FROM players WHERE name = ?")
        .bind("Preserve C")
        .fetch_one(&pool)
        .await
        .unwrap();
    
    // Match 1: A vs B (A wins)
    let match1 = sqlx::query("INSERT INTO matches (match_type, team1_score, team2_score) VALUES (?, ?, ?)")
        .bind("singles").bind(11).bind(5)
        .execute(&pool).await.unwrap();
    let match1_id = match1.last_insert_rowid();
    
    sqlx::query("INSERT INTO match_participants (match_id, player_id, team, rating_change) VALUES (?, ?, ?, ?)")
        .bind(match1_id).bind(pa_id.0).bind(1).bind(10.0)
        .execute(&pool).await.unwrap();
    sqlx::query("INSERT INTO match_participants (match_id, player_id, team, rating_change) VALUES (?, ?, ?, ?)")
        .bind(match1_id).bind(pb_id.0).bind(2).bind(-10.0)
        .execute(&pool).await.unwrap();
    
    sqlx::query("UPDATE players SET singles_rating = singles_rating + 10 WHERE id = ?")
        .bind(pa_id.0).execute(&pool).await.unwrap();
    sqlx::query("UPDATE players SET singles_rating = singles_rating - 10 WHERE id = ?")
        .bind(pb_id.0).execute(&pool).await.unwrap();
    
    // Match 2: A vs C (A wins)
    let match2 = sqlx::query("INSERT INTO matches (match_type, team1_score, team2_score) VALUES (?, ?, ?)")
        .bind("singles").bind(11).bind(7)
        .execute(&pool).await.unwrap();
    let match2_id = match2.last_insert_rowid();
    
    sqlx::query("INSERT INTO match_participants (match_id, player_id, team, rating_change) VALUES (?, ?, ?, ?)")
        .bind(match2_id).bind(pa_id.0).bind(1).bind(8.0)
        .execute(&pool).await.unwrap();
    sqlx::query("INSERT INTO match_participants (match_id, player_id, team, rating_change) VALUES (?, ?, ?, ?)")
        .bind(match2_id).bind(pc_id.0).bind(2).bind(-8.0)
        .execute(&pool).await.unwrap();
    
    sqlx::query("UPDATE players SET singles_rating = singles_rating + 8 WHERE id = ?")
        .bind(pa_id.0).execute(&pool).await.unwrap();
    sqlx::query("UPDATE players SET singles_rating = singles_rating - 8 WHERE id = ?")
        .bind(pc_id.0).execute(&pool).await.unwrap();
    
    // After both matches: A=1518, B=1490, C=1492
    
    // Delete ONLY match 1 (A vs B)
    let participants: Vec<(i64, f64)> = sqlx::query_as(
        "SELECT player_id, rating_change FROM match_participants WHERE match_id = ?"
    )
    .bind(match1_id)
    .fetch_all(&pool)
    .await
    .unwrap();
    
    for (player_id, change) in participants {
        sqlx::query("UPDATE players SET singles_rating = singles_rating - ? WHERE id = ?")
            .bind(change)
            .bind(player_id)
            .execute(&pool)
            .await
            .unwrap();
    }
    
    sqlx::query("DELETE FROM match_participants WHERE match_id = ?")
        .bind(match1_id)
        .execute(&pool)
        .await
        .unwrap();
    
    sqlx::query("DELETE FROM matches WHERE id = ?")
        .bind(match1_id)
        .execute(&pool)
        .await
        .unwrap();
    
    // After reverting match 1: A=1508, B=1500, C=1492
    // Match 2's effect on A and C should be preserved
    
    let final_a: (f64,) = sqlx::query_as("SELECT singles_rating FROM players WHERE id = ?")
        .bind(pa_id.0)
        .fetch_one(&pool)
        .await
        .unwrap();
    assert!((final_a.0 - 1508.0).abs() < 0.01, "A rating wrong: {}", final_a.0);
    
    let final_b: (f64,) = sqlx::query_as("SELECT singles_rating FROM players WHERE id = ?")
        .bind(pb_id.0)
        .fetch_one(&pool)
        .await
        .unwrap();
    assert!((final_b.0 - 1500.0).abs() < 0.01, "B rating wrong: {}", final_b.0);
    
    let final_c: (f64,) = sqlx::query_as("SELECT singles_rating FROM players WHERE id = ?")
        .bind(pc_id.0)
        .fetch_one(&pool)
        .await
        .unwrap();
    assert!((final_c.0 - 1492.0).abs() < 0.01, "C rating wrong: {}", final_c.0);
    
    // Match 2 should still exist
    let match_count: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM matches")
        .fetch_one(&pool)
        .await
        .unwrap();
    assert_eq!(match_count.0, 1);
}
