package db import ( "context" "github.com/pkg/errors" "github.com/uptrace/bun" ) // GetPlayoffPlayerStats returns aggregated player stats from playoff fixtures only // (fixtures with round < 0) for a season-league. // Reuses the same LeaguePlayerStats struct as regular season stats. func GetPlayoffPlayerStats( ctx context.Context, tx bun.Tx, seasonID, leagueID int, ) ([]*LeaguePlayerStats, error) { if seasonID == 0 { return nil, errors.New("seasonID not provided") } if leagueID == 0 { return nil, errors.New("leagueID not provided") } var stats []*LeaguePlayerStats err := tx.NewRaw(` SELECT agg.player_id, agg.player_name, COALESCE(tr.team_id, 0) AS team_id, COALESCE(t.name, '') AS team_name, COALESCE(t.color, '') AS team_color, agg.games_played, agg.total_periods_played, agg.total_goals, agg.total_assists, agg.total_primary_assists, agg.total_secondary_assists, agg.total_saves, agg.total_shots, agg.total_blocks, agg.total_passes, agg.total_score FROM ( SELECT frps.player_id AS player_id, COALESCE(p.name, frps.player_username) AS player_name, COUNT(DISTINCT frps.fixture_result_id) AS games_played, COALESCE(SUM(frps.periods_played), 0) AS total_periods_played, COALESCE(SUM(frps.goals), 0) AS total_goals, COALESCE(SUM(frps.assists), 0) AS total_assists, COALESCE(SUM(frps.primary_assists), 0) AS total_primary_assists, COALESCE(SUM(frps.secondary_assists), 0) AS total_secondary_assists, COALESCE(SUM(frps.saves), 0) AS total_saves, COALESCE(SUM(frps.shots), 0) AS total_shots, COALESCE(SUM(frps.blocks), 0) AS total_blocks, COALESCE(SUM(frps.passes), 0) AS total_passes, COALESCE(SUM(frps.score), 0) AS total_score FROM fixture_result_player_stats frps JOIN fixture_results fr ON fr.id = frps.fixture_result_id JOIN fixtures f ON f.id = fr.fixture_id LEFT JOIN players p ON p.id = frps.player_id WHERE fr.finalized = true AND f.season_id = ? AND f.league_id = ? AND f.round < 0 AND frps.period_num = 3 AND frps.player_id IS NOT NULL GROUP BY frps.player_id, COALESCE(p.name, frps.player_username) ) agg LEFT JOIN team_rosters tr ON tr.player_id = agg.player_id AND tr.season_id = ? AND tr.league_id = ? LEFT JOIN teams t ON t.id = tr.team_id ORDER BY agg.total_score DESC `, seasonID, leagueID, seasonID, leagueID).Scan(ctx, &stats) if err != nil { return nil, errors.Wrap(err, "tx.NewRaw") } return stats, nil } // GetPlayoffTopGoalScorers returns the top 10 goal scorers from playoff fixtures. func GetPlayoffTopGoalScorers( ctx context.Context, tx bun.Tx, seasonID, leagueID int, ) ([]*LeagueTopGoalScorer, error) { if seasonID == 0 { return nil, errors.New("seasonID not provided") } if leagueID == 0 { return nil, errors.New("leagueID not provided") } var stats []*LeagueTopGoalScorer err := tx.NewRaw(` SELECT agg.player_id, agg.player_name, COALESCE(tr.team_id, 0) AS team_id, COALESCE(t.name, '') AS team_name, COALESCE(t.color, '') AS team_color, agg.total_goals, agg.total_periods_played, agg.total_shots FROM ( SELECT frps.player_id AS player_id, COALESCE(p.name, frps.player_username) AS player_name, COALESCE(SUM(frps.goals), 0) AS total_goals, COALESCE(SUM(frps.periods_played), 0) AS total_periods_played, COALESCE(SUM(frps.shots), 0) AS total_shots FROM fixture_result_player_stats frps JOIN fixture_results fr ON fr.id = frps.fixture_result_id JOIN fixtures f ON f.id = fr.fixture_id LEFT JOIN players p ON p.id = frps.player_id WHERE fr.finalized = true AND f.season_id = ? AND f.league_id = ? AND f.round < 0 AND frps.period_num = 3 AND frps.player_id IS NOT NULL GROUP BY frps.player_id, COALESCE(p.name, frps.player_username) ORDER BY total_goals DESC, total_periods_played ASC, total_shots ASC LIMIT 10 ) agg LEFT JOIN team_rosters tr ON tr.player_id = agg.player_id AND tr.season_id = ? AND tr.league_id = ? LEFT JOIN teams t ON t.id = tr.team_id ORDER BY agg.total_goals DESC, agg.total_periods_played ASC, agg.total_shots ASC `, seasonID, leagueID, seasonID, leagueID).Scan(ctx, &stats) if err != nil { return nil, errors.Wrap(err, "tx.NewRaw") } return stats, nil } // GetPlayoffTopAssisters returns the top 10 assisters from playoff fixtures. func GetPlayoffTopAssisters( ctx context.Context, tx bun.Tx, seasonID, leagueID int, ) ([]*LeagueTopAssister, error) { if seasonID == 0 { return nil, errors.New("seasonID not provided") } if leagueID == 0 { return nil, errors.New("leagueID not provided") } var stats []*LeagueTopAssister err := tx.NewRaw(` SELECT agg.player_id, agg.player_name, COALESCE(tr.team_id, 0) AS team_id, COALESCE(t.name, '') AS team_name, COALESCE(t.color, '') AS team_color, agg.total_assists, agg.total_periods_played, agg.total_primary_assists FROM ( SELECT frps.player_id AS player_id, COALESCE(p.name, frps.player_username) AS player_name, COALESCE(SUM(frps.assists), 0) AS total_assists, COALESCE(SUM(frps.periods_played), 0) AS total_periods_played, COALESCE(SUM(frps.primary_assists), 0) AS total_primary_assists FROM fixture_result_player_stats frps JOIN fixture_results fr ON fr.id = frps.fixture_result_id JOIN fixtures f ON f.id = fr.fixture_id LEFT JOIN players p ON p.id = frps.player_id WHERE fr.finalized = true AND f.season_id = ? AND f.league_id = ? AND f.round < 0 AND frps.period_num = 3 AND frps.player_id IS NOT NULL GROUP BY frps.player_id, COALESCE(p.name, frps.player_username) ORDER BY total_assists DESC, total_periods_played ASC, total_primary_assists DESC LIMIT 10 ) agg LEFT JOIN team_rosters tr ON tr.player_id = agg.player_id AND tr.season_id = ? AND tr.league_id = ? LEFT JOIN teams t ON t.id = tr.team_id ORDER BY agg.total_assists DESC, agg.total_periods_played ASC, agg.total_primary_assists DESC `, seasonID, leagueID, seasonID, leagueID).Scan(ctx, &stats) if err != nil { return nil, errors.Wrap(err, "tx.NewRaw") } return stats, nil } // GetPlayoffTopSavers returns the top 10 savers from playoff fixtures. func GetPlayoffTopSavers( ctx context.Context, tx bun.Tx, seasonID, leagueID int, ) ([]*LeagueTopSaver, error) { if seasonID == 0 { return nil, errors.New("seasonID not provided") } if leagueID == 0 { return nil, errors.New("leagueID not provided") } var stats []*LeagueTopSaver err := tx.NewRaw(` SELECT agg.player_id, agg.player_name, COALESCE(tr.team_id, 0) AS team_id, COALESCE(t.name, '') AS team_name, COALESCE(t.color, '') AS team_color, agg.total_saves, agg.total_periods_played, agg.total_blocks FROM ( SELECT frps.player_id AS player_id, COALESCE(p.name, frps.player_username) AS player_name, COALESCE(SUM(frps.saves), 0) AS total_saves, COALESCE(SUM(frps.periods_played), 0) AS total_periods_played, COALESCE(SUM(frps.blocks), 0) AS total_blocks FROM fixture_result_player_stats frps JOIN fixture_results fr ON fr.id = frps.fixture_result_id JOIN fixtures f ON f.id = fr.fixture_id LEFT JOIN players p ON p.id = frps.player_id WHERE fr.finalized = true AND f.season_id = ? AND f.league_id = ? AND f.round < 0 AND frps.period_num = 3 AND frps.player_id IS NOT NULL GROUP BY frps.player_id, COALESCE(p.name, frps.player_username) ORDER BY total_saves DESC, total_periods_played ASC, total_blocks DESC LIMIT 10 ) agg LEFT JOIN team_rosters tr ON tr.player_id = agg.player_id AND tr.season_id = ? AND tr.league_id = ? LEFT JOIN teams t ON t.id = tr.team_id ORDER BY agg.total_saves DESC, agg.total_periods_played ASC, agg.total_blocks DESC `, seasonID, leagueID, seasonID, leagueID).Scan(ctx, &stats) if err != nil { return nil, errors.Wrap(err, "tx.NewRaw") } return stats, nil }