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

#[cfg(test)]
mod database_tests {
    use sqlx::sqlite::SqlitePoolOptions;
    
    /// Create an in-memory test database
    async fn create_test_db() -> sqlx::SqlitePool {
        let pool = SqlitePoolOptions::new()
            .max_connections(1)
            .connect("sqlite::memory:")
            .await
            .expect("Failed to create test database");
        
        // Run migrations
        crate::db::run_migrations(&pool)
            .await
            .expect("Failed to run migrations");
        
        pool
    }
    
    #[tokio::test]
    async fn test_create_player() {
        let pool = create_test_db().await;
        
        // Insert a player
        sqlx::query(
            "INSERT INTO players (name, email, rating) VALUES (?, ?, ?)"
        )
        .bind("Alice")
        .bind(Some("alice@example.com"))
        .bind(1500.0)
        .execute(&pool)
        .await
        .expect("Failed to insert player");
        
        // Verify player was created
        let player: Option<(String, f64)> = sqlx::query_as(
            "SELECT name, rating FROM players WHERE name = ?"
        )
        .bind("Alice")
        .fetch_optional(&pool)
        .await
        .expect("Query failed");
        
        assert!(player.is_some(), "Player not found");
        let (name, rating) = player.unwrap();
        assert_eq!(name, "Alice");
        assert_eq!(rating, 1500.0);
    }
    
    #[tokio::test]
    async fn test_create_multiple_players() {
        let pool = create_test_db().await;
        
        // Insert 5 players
        for i in 0..5 {
            let name = format!("Player{}", i);
            let rating = 1500.0 + (i as f64 * 50.0);
            
            sqlx::query(
                "INSERT INTO players (name, rating) VALUES (?, ?)"
            )
            .bind(&name)
            .bind(rating)
            .execute(&pool)
            .await
            .expect("Failed to insert player");
        }
        
        // Count players
        let count: i64 = sqlx::query_scalar("SELECT COUNT(*) FROM players")
            .fetch_one(&pool)
            .await
            .unwrap();
        
        assert_eq!(count, 5, "Should have 5 players");
    }
    
    #[tokio::test]
    async fn test_update_player_rating() {
        let pool = create_test_db().await;
        
        // Create player
        sqlx::query("INSERT INTO players (name, rating) VALUES (?, ?)")
            .bind("Bob")
            .bind(1500.0)
            .execute(&pool)
            .await
            .unwrap();
        
        // Update rating
        sqlx::query("UPDATE players SET rating = ? WHERE name = ?")
            .bind(1516.0)
            .bind("Bob")
            .execute(&pool)
            .await
            .unwrap();
        
        // Verify update
        let rating: f64 = sqlx::query_scalar("SELECT rating FROM players WHERE name = ?")
            .bind("Bob")
            .fetch_one(&pool)
            .await
            .unwrap();
        
        assert_eq!(rating, 1516.0, "Rating should be updated to 1516");
    }
    
    #[tokio::test]
    async fn test_unique_player_names() {
        let pool = create_test_db().await;
        
        // Insert first player
        sqlx::query("INSERT INTO players (name, rating) VALUES (?, ?)")
            .bind("Charlie")
            .bind(1500.0)
            .execute(&pool)
            .await
            .unwrap();
        
        // Try to insert duplicate name - should fail
        let result = sqlx::query("INSERT INTO players (name, rating) VALUES (?, ?)")
            .bind("Charlie")
            .bind(1600.0)
            .execute(&pool)
            .await;
        
        assert!(result.is_err(), "Should reject duplicate player name");
    }
    
    #[tokio::test]
    async fn test_record_match_and_participants() {
        let pool = create_test_db().await;
        
        // Create 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();
        
        // Create match
        sqlx::query(
            "INSERT INTO matches (session_id, match_type, team1_score, team2_score) VALUES (?, ?, ?, ?)"
        )
        .bind(1i64)  // Session ID
        .bind("singles")
        .bind(11)
        .bind(9)
        .execute(&pool)
        .await
        .unwrap();
        
        // Verify match exists
        let match_count: i64 = sqlx::query_scalar("SELECT COUNT(*) FROM matches")
            .fetch_one(&pool)
            .await
            .unwrap();
        
        assert_eq!(match_count, 1, "Should have 1 match");
    }
    
    #[tokio::test]
    async fn test_match_participants_rating_changes() {
        let pool = create_test_db().await;
        
        // Create 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 and match
        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(9)
        .execute(&pool)
        .await
        .unwrap();
        
        // Record participants
        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)     // team
        .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)     // team
        .bind(1500.0)
        .bind(1484.0)
        .bind(-16.0)
        .execute(&pool)
        .await
        .unwrap();
        
        // Verify changes
        let alice_change: f64 = sqlx::query_scalar(
            "SELECT rating_change FROM match_participants WHERE player_id = 1 AND match_id = 1"
        )
        .fetch_one(&pool)
        .await
        .unwrap();
        
        let bob_change: f64 = sqlx::query_scalar(
            "SELECT rating_change FROM match_participants WHERE player_id = 2 AND match_id = 1"
        )
        .fetch_one(&pool)
        .await
        .unwrap();
        
        assert_eq!(alice_change, 16.0);
        assert_eq!(bob_change, -16.0);
    }
    
    #[tokio::test]
    async fn test_leaderboard_sorted_by_rating() {
        let pool = create_test_db().await;
        
        // Create players with different ratings
        let players = vec![
            ("Alice", 1700.0),
            ("Bob", 1600.0),
            ("Charlie", 1500.0),
            ("Diana", 1400.0),
        ];
        
        for (name, rating) in &players {
            sqlx::query("INSERT INTO players (name, rating) VALUES (?, ?)")
                .bind(name)
                .bind(rating)
                .execute(&pool)
                .await
                .unwrap();
        }
        
        // Get leaderboard
        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(), 4);
        assert_eq!(leaderboard[0].0, "Alice");
        assert_eq!(leaderboard[1].0, "Bob");
        assert_eq!(leaderboard[2].0, "Charlie");
        assert_eq!(leaderboard[3].0, "Diana");
    }
    
    #[tokio::test]
    async fn test_match_deletion_cascades() {
        let pool = create_test_db().await;
        
        // Create player, session, match, participant
        sqlx::query("INSERT INTO players (name, rating) VALUES (?, ?)")
            .bind("Player")
            .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 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 = ?"
        )
        .bind(1i64)
        .fetch_one(&pool)
        .await
        .unwrap();
        
        assert_eq!(participant_count, 0, "Participants should be deleted");
    }
    
    #[tokio::test]
    async fn test_player_match_count() {
        let pool = create_test_db().await;
        
        // Create 2 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 and 3 matches
        sqlx::query("INSERT INTO sessions DEFAULT VALUES")
            .execute(&pool)
            .await
            .unwrap();
        
        for i 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 + i)
            .execute(&pool)
            .await
            .unwrap();
            
            sqlx::query(
                "INSERT INTO match_participants (match_id, player_id, team, rating_before, rating_after, rating_change) \
                 VALUES (?, ?, ?, ?, ?, ?)"
            )
            .bind((i + 1) as i64)
            .bind(1i64)  // Alice
            .bind(1)
            .bind(1500.0)
            .bind(1520.0)
            .bind(20.0)
            .execute(&pool)
            .await
            .unwrap();
        }
        
        // Count Alice's matches
        let alice_matches: i64 = sqlx::query_scalar(
            "SELECT COUNT(DISTINCT match_id) FROM match_participants WHERE player_id = 1"
        )
        .fetch_one(&pool)
        .await
        .unwrap();
        
        assert_eq!(alice_matches, 3, "Alice should have 3 matches");
    }
    
    #[tokio::test]
    async fn test_rating_history_tracking() {
        let pool = create_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 3 matches with increasing ratings
        let expected_ratings = vec![1516.0, 1536.0, 1560.0];
        let mut rating_before = 1500.0;
        
        for (match_num, expected_after) in expected_ratings.iter().enumerate() {
            let match_id = (match_num + 1) as i64;
            
            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(match_id)
            .bind(1i64)
            .bind(1)
            .bind(rating_before)
            .bind(expected_after)
            .bind(expected_after - rating_before)
            .execute(&pool)
            .await
            .unwrap();
            
            rating_before = *expected_after;
        }
        
        // Retrieve rating history
        let history: Vec<f64> = sqlx::query_scalar(
            "SELECT rating_after FROM match_participants WHERE player_id = 1 ORDER BY match_id"
        )
        .fetch_all(&pool)
        .await
        .unwrap();
        
        assert_eq!(history.len(), 3);
        assert_eq!(history[0], 1516.0);
        assert_eq!(history[1], 1536.0);
        assert_eq!(history[2], 1560.0);
    }
}
