use crate::{db::Pool, error::AppResult, models::statistics::Statistics}; const SELECT_STATS: &str = "SELECT profile_id, matches_played, matches_won, matches_drawn, matches_lost, goals_scored, goals_conceded, packs_opened, sbcs_completed, total_coins_earned, win_streak, best_win_streak, updated_at FROM statistics WHERE profile_id = ?"; pub async fn get_or_create(pool: &Pool, profile_id: &str) -> AppResult { if let Some(s) = sqlx::query_as::<_, Statistics>(SELECT_STATS) .bind(profile_id) .fetch_optional(pool) .await? { return Ok(s); } let stats = Statistics::new(profile_id); sqlx::query( "INSERT INTO statistics (profile_id, matches_played, matches_won, matches_drawn, matches_lost, goals_scored, goals_conceded, packs_opened, sbcs_completed, total_coins_earned, win_streak, best_win_streak, updated_at) VALUES (?, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ?)", ) .bind(profile_id) .bind(&stats.updated_at) .execute(pool) .await?; Ok(stats) } pub async fn record_match( pool: &Pool, profile_id: &str, outcome: &str, goals_for: i64, goals_against: i64, coins: i64, ) -> AppResult<()> { let current = get_or_create(pool, profile_id).await?; let now = chrono::Utc::now().to_rfc3339(); let (w, d, l) = match outcome { "win" => (1i64, 0i64, 0i64), "draw" => (0, 1, 0), _ => (0, 0, 1), }; let new_streak = if outcome == "win" { current.win_streak + 1 } else { 0 }; let new_best = new_streak.max(current.best_win_streak); sqlx::query( "UPDATE statistics SET matches_played = matches_played + 1, matches_won = matches_won + ?, matches_drawn = matches_drawn + ?, matches_lost = matches_lost + ?, goals_scored = goals_scored + ?, goals_conceded = goals_conceded + ?, total_coins_earned = total_coins_earned + ?, win_streak = ?, best_win_streak = ?, updated_at = ? WHERE profile_id = ?", ) .bind(w) .bind(d) .bind(l) .bind(goals_for) .bind(goals_against) .bind(coins) .bind(new_streak) .bind(new_best) .bind(&now) .bind(profile_id) .execute(pool) .await?; Ok(()) } pub async fn increment_packs_opened(pool: &Pool, profile_id: &str) -> AppResult<()> { get_or_create(pool, profile_id).await?; let now = chrono::Utc::now().to_rfc3339(); sqlx::query( "UPDATE statistics SET packs_opened = packs_opened + 1, updated_at = ? WHERE profile_id = ?", ) .bind(&now) .bind(profile_id) .execute(pool) .await?; Ok(()) } pub async fn increment_sbcs_completed(pool: &Pool, profile_id: &str) -> AppResult<()> { get_or_create(pool, profile_id).await?; let now = chrono::Utc::now().to_rfc3339(); sqlx::query( "UPDATE statistics SET sbcs_completed = sbcs_completed + 1, updated_at = ? WHERE profile_id = ?", ) .bind(&now) .bind(profile_id) .execute(pool) .await?; Ok(()) } pub async fn record_position_goals( pool: &Pool, profile_id: &str, positions: &[String], ) -> AppResult<()> { for position in positions { sqlx::query( "INSERT INTO position_goals (profile_id, position, goals) VALUES (?, ?, 1) \ ON CONFLICT(profile_id, position) DO UPDATE SET goals = goals + 1", ) .bind(profile_id) .bind(position) .execute(pool) .await?; } Ok(()) } pub async fn get_position_goals(pool: &Pool, profile_id: &str) -> AppResult> { let rows: Vec<(String, i64)> = sqlx::query_as( "SELECT position, goals FROM position_goals WHERE profile_id = ? ORDER BY goals DESC", ) .bind(profile_id) .fetch_all(pool) .await?; Ok(rows) }