//! Integration tests for pickleball-elo v3.0

#[cfg(test)]
mod integration_tests {
    use sqlx::sqlite::SqlitePoolOptions;
    
    async fn setup_test_db() -> sqlx::SqlitePool {
        let pool = SqlitePoolOptions::new()
            .max_connections(2)
            .connect("sqlite::memory:")
            .await
            .expect("Failed to create test database");
        
        // Initialize schema
        let statements = vec![
            "CREATE TABLE players (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                name TEXT NOT NULL UNIQUE,
                email TEXT,
                rating REAL NOT NULL DEFAULT 1500.0,
                created_at TEXT NOT NULL DEFAULT (datetime('now')),
                last_played TEXT NOT NULL DEFAULT (datetime('now'))
            )",
            "CREATE TABLE sessions (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                start_time TEXT NOT NULL DEFAULT (datetime('now')),
                end_time TEXT,
                summary_sent BOOLEAN NOT NULL DEFAULT 0,
                notes TEXT
            )",
            "CREATE TABLE matches (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                session_id INTEGER NOT NULL,
                match_type TEXT NOT NULL CHECK(match_type IN ('singles', 'doubles')),
                timestamp TEXT NOT NULL DEFAULT (datetime('now')),
                team1_score INTEGER NOT NULL,
                team2_score INTEGER NOT NULL,
                FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE
            )",
            "CREATE TABLE match_participants (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                match_id INTEGER NOT NULL,
                player_id INTEGER NOT NULL,
                team INTEGER NOT NULL CHECK(team IN (1, 2)),
                rating_before REAL NOT NULL,
                rating_after REAL NOT NULL,
                rating_change REAL NOT NULL,
                FOREIGN KEY (match_id) REFERENCES matches(id) ON DELETE CASCADE,
                FOREIGN KEY (player_id) REFERENCES players(id) ON DELETE CASCADE
            )",
            "CREATE INDEX idx_players_rating ON players(rating DESC)",
        ];
        
        for stmt in statements {
            sqlx::query(stmt)
                .execute(&pool)
                .await
                .expect("Failed to create schema");
        }
        
        pool
    }
    
    #[tokio::test]
    async fn test_create_players_record_match() {
        let pool = setup_test_db().await;
        
        // Create two players
        sqlx::query("INSERT INTO players (name, rating) VALUES (?, ?)")
            .bind("Alice")
            .bind(1500.0)
            .execute(&pool)
            .await
            .unwrap();
        
        sqlx::query("INSERT INTO players (name, rating) VALUES (?, ?)")
            .bind("Bob")
            .bind(1500.0)
            .execute(&pool)
            .await
            .unwrap();
        
        // Create session
        sqlx::query("INSERT INTO sessions DEFAULT VALUES")
            .execute(&pool)
            .await
            .unwrap();
        
        // Record match
        sqlx::query(
            "INSERT INTO matches (session_id, match_type, team1_score, team2_score) VALUES (?, ?, ?, ?)"
        )
        .bind(1i64)
        .bind("singles")
        .bind(11)
        .bind(9)
        .execute(&pool)
        .await
        .unwrap();
        
        // Record rating changes
        sqlx::query(
            "INSERT INTO match_participants (match_id, player_id, team, rating_before, rating_after, rating_change) \
             VALUES (?, ?, ?, ?, ?, ?)"
        )
        .bind(1i64)  // match_id
        .bind(1i64)  // alice
        .bind(1)
        .bind(1500.0)
        .bind(1516.0)
        .bind(16.0)
        .execute(&pool)
        .await
        .unwrap();
        
        sqlx::query(
            "INSERT INTO match_participants (match_id, player_id, team, rating_before, rating_after, rating_change) \
             VALUES (?, ?, ?, ?, ?, ?)"
        )
        .bind(1i64)  // match_id
        .bind(2i64)  // bob
        .bind(2)
        .bind(1500.0)
        .bind(1484.0)
        .bind(-16.0)
        .execute(&pool)
        .await
        .unwrap();
        
        // Verify match was recorded
        let match_count: i64 = sqlx::query_scalar("SELECT COUNT(*) FROM matches")
            .fetch_one(&pool)
            .await
            .unwrap();
        
        assert_eq!(match_count, 1);
        
        // Verify rating changes
        let alice_change: f64 = sqlx::query_scalar(
            "SELECT rating_change FROM match_participants WHERE player_id = 1"
        )
        .fetch_one(&pool)
        .await
        .unwrap();
        
        let bob_change: f64 = sqlx::query_scalar(
            "SELECT rating_change FROM match_participants WHERE player_id = 2"
        )
        .fetch_one(&pool)
        .await
        .unwrap();
        
        assert_eq!(alice_change, 16.0);
        assert_eq!(bob_change, -16.0);
    }
    
    #[tokio::test]
    async fn test_multiple_matches_leaderboard() {
        let pool = setup_test_db().await;
        
        // Create 3 players
        for (name, rating) in &[("Alice", 1600.0), ("Bob", 1500.0), ("Charlie", 1400.0)] {
            sqlx::query("INSERT INTO players (name, rating) VALUES (?, ?)")
                .bind(name)
                .bind(rating)
                .execute(&pool)
                .await
                .unwrap();
        }
        
        // Create session
        sqlx::query("INSERT INTO sessions DEFAULT VALUES")
            .execute(&pool)
            .await
            .unwrap();
        
        // Record 3 matches
        for match_num in 0..3 {
            sqlx::query(
                "INSERT INTO matches (session_id, match_type, team1_score, team2_score) VALUES (?, ?, ?, ?)"
            )
            .bind(1i64)
            .bind("singles")
            .bind(11)
            .bind(5 + match_num)
            .execute(&pool)
            .await
            .unwrap();
        }
        
        // Verify all matches recorded
        let match_count: i64 = sqlx::query_scalar("SELECT COUNT(*) FROM matches")
            .fetch_one(&pool)
            .await
            .unwrap();
        
        assert_eq!(match_count, 3);
        
        // Verify leaderboard can be retrieved
        let leaderboard: Vec<(String, f64)> = sqlx::query_as(
            "SELECT name, rating FROM players ORDER BY rating DESC"
        )
        .fetch_all(&pool)
        .await
        .unwrap();
        
        assert_eq!(leaderboard.len(), 3);
        assert_eq!(leaderboard[0].0, "Alice");
        assert_eq!(leaderboard[1].0, "Bob");
        assert_eq!(leaderboard[2].0, "Charlie");
    }
    
    #[tokio::test]
    async fn test_session_with_many_matches() {
        let pool = setup_test_db().await;
        
        // Create 2 players
        sqlx::query("INSERT INTO players (name, rating) VALUES (?, ?)")
            .bind("Player1")
            .bind(1500.0)
            .execute(&pool)
            .await
            .unwrap();
        
        sqlx::query("INSERT INTO players (name, rating) VALUES (?, ?)")
            .bind("Player2")
            .bind(1500.0)
            .execute(&pool)
            .await
            .unwrap();
        
        // Create session
        sqlx::query("INSERT INTO sessions DEFAULT VALUES")
            .execute(&pool)
            .await
            .unwrap();
        
        // Record 10 matches in a session
        for i in 0..10 {
            sqlx::query(
                "INSERT INTO matches (session_id, match_type, team1_score, team2_score) VALUES (?, ?, ?, ?)"
            )
            .bind(1i64)
            .bind("singles")
            .bind(11)
            .bind(5 + (i % 7))
            .execute(&pool)
            .await
            .unwrap();
        }
        
        // Verify all matches in session
        let session_matches: i64 = sqlx::query_scalar(
            "SELECT COUNT(*) FROM matches WHERE session_id = 1"
        )
        .fetch_one(&pool)
        .await
        .unwrap();
        
        assert_eq!(session_matches, 10);
    }
    
    #[tokio::test]
    async fn test_rating_history() {
        let pool = setup_test_db().await;
        
        // Create player
        sqlx::query("INSERT INTO players (name, rating) VALUES (?, ?)")
            .bind("Tracker")
            .bind(1500.0)
            .execute(&pool)
            .await
            .unwrap();
        
        // Create session
        sqlx::query("INSERT INTO sessions DEFAULT VALUES")
            .execute(&pool)
            .await
            .unwrap();
        
        // Record 5 matches with escalating ratings
        let mut rating = 1500.0;
        for i in 1..=5 {
            sqlx::query(
                "INSERT INTO matches (session_id, match_type, team1_score, team2_score) VALUES (?, ?, ?, ?)"
            )
            .bind(1i64)
            .bind("singles")
            .bind(11)
            .bind(5)
            .execute(&pool)
            .await
            .unwrap();
            
            let new_rating = rating + 16.0;
            
            sqlx::query(
                "INSERT INTO match_participants (match_id, player_id, team, rating_before, rating_after, rating_change) \
                 VALUES (?, ?, ?, ?, ?, ?)"
            )
            .bind(i as i64)
            .bind(1i64)
            .bind(1)
            .bind(rating)
            .bind(new_rating)
            .bind(16.0)
            .execute(&pool)
            .await
            .unwrap();
            
            rating = new_rating;
        }
        
        // Retrieve final rating
        let final_rating: f64 = sqlx::query_scalar(
            "SELECT MAX(rating_after) FROM match_participants WHERE player_id = 1"
        )
        .fetch_one(&pool)
        .await
        .unwrap();
        
        assert_eq!(final_rating, 1580.0, "Final rating should be 1580 after 5 wins");
    }
    
    #[tokio::test]
    async fn test_match_deletion_cascade() {
        let pool = setup_test_db().await;
        
        // Setup: player, session, match, participant
        sqlx::query("INSERT INTO players (name, rating) VALUES (?, ?)")
            .bind("Test")
            .bind(1500.0)
            .execute(&pool)
            .await
            .unwrap();
        
        sqlx::query("INSERT INTO sessions DEFAULT VALUES")
            .execute(&pool)
            .await
            .unwrap();
        
        sqlx::query(
            "INSERT INTO matches (session_id, match_type, team1_score, team2_score) VALUES (?, ?, ?, ?)"
        )
        .bind(1i64)
        .bind("singles")
        .bind(11)
        .bind(5)
        .execute(&pool)
        .await
        .unwrap();
        
        sqlx::query(
            "INSERT INTO match_participants (match_id, player_id, team, rating_before, rating_after, rating_change) \
             VALUES (?, ?, ?, ?, ?, ?)"
        )
        .bind(1i64)
        .bind(1i64)
        .bind(1)
        .bind(1500.0)
        .bind(1520.0)
        .bind(20.0)
        .execute(&pool)
        .await
        .unwrap();
        
        // Delete the match
        sqlx::query("DELETE FROM matches WHERE id = ?")
            .bind(1i64)
            .execute(&pool)
            .await
            .unwrap();
        
        // Verify participants were deleted
        let participant_count: i64 = sqlx::query_scalar(
            "SELECT COUNT(*) FROM match_participants WHERE match_id = 1"
        )
        .fetch_one(&pool)
        .await
        .unwrap();
        
        assert_eq!(participant_count, 0, "Participants should cascade delete");
    }
    
    #[tokio::test]
    async fn test_doubles_match_recording() {
        let pool = setup_test_db().await;
        
        // Create 4 players
        for i in 1..=4 {
            sqlx::query("INSERT INTO players (name, rating) VALUES (?, ?)")
                .bind(format!("Player{}", i))
                .bind(1500.0)
                .execute(&pool)
                .await
                .unwrap();
        }
        
        // Create session
        sqlx::query("INSERT INTO sessions DEFAULT VALUES")
            .execute(&pool)
            .await
            .unwrap();
        
        // Record doubles match
        sqlx::query(
            "INSERT INTO matches (session_id, match_type, team1_score, team2_score) VALUES (?, ?, ?, ?)"
        )
        .bind(1i64)
        .bind("doubles")
        .bind(11)
        .bind(9)
        .execute(&pool)
        .await
        .unwrap();
        
        // Record all 4 participants
        for player_id in 1..=4 {
            let team = if player_id <= 2 { 1 } else { 2 };
            sqlx::query(
                "INSERT INTO match_participants (match_id, player_id, team, rating_before, rating_after, rating_change) \
                 VALUES (?, ?, ?, ?, ?, ?)"
            )
            .bind(1i64)
            .bind(player_id as i64)
            .bind(team)
            .bind(1500.0)
            .bind(1516.0)
            .bind(16.0)
            .execute(&pool)
            .await
            .unwrap();
        }
        
        // Verify all participants recorded
        let participant_count: i64 = sqlx::query_scalar(
            "SELECT COUNT(*) FROM match_participants WHERE match_id = 1"
        )
        .fetch_one(&pool)
        .await
        .unwrap();
        
        assert_eq!(participant_count, 4, "Doubles match should have 4 participants");
    }
}
