-- Read through the server only: never expose session secrets or round answers. create index game_sessions_leaderboard_idx on public.game_sessions (difficulty, owner_id) where current_round = 5 and owner_id is not null; create function public.player_leaderboard( p_difficulty text default 'all', p_search text default '', p_page integer default 1, p_limit integer default 25 ) returns jsonb language sql stable security invoker set search_path = '' as $$ with completed as ( select s.id, s.owner_id, sum((r.result->>'score')::integer) as score from public.game_sessions s join public.game_rounds r on r.session_id = s.id where s.current_round = 5 and s.owner_id is not null and (p_difficulty = 'all' or s.difficulty = p_difficulty) group by s.id, s.owner_id having count(*) = 5 and count(r.result) = 5 ), players as ( select p.id as player_id, p.display_name, max(c.score) as best_score, avg(c.score) as average_score, count(*) as games_played from completed c join public.profiles p on p.id = c.owner_id group by p.id, p.display_name ), ranked as ( select row_number() over ( order by best_score desc, average_score desc, games_played desc, player_id ) as rank, player_id, display_name, best_score, round(average_score)::integer as average_score, games_played from players ), matched as ( select * from ranked where strpos(lower(display_name), lower(left(trim(coalesce(p_search, '')), 64))) > 0 ), settings as ( select greatest(1, least(coalesce(p_limit, 25), 100)) as page_size ), counts as ( select (select count(*) from matched) as matching_players, greatest(1, ceil((select count(*) from matched)::numeric / settings.page_size)::integer) as pages from settings ), pagination as ( select *, greatest(1, least(coalesce(p_page, 1), pages)) as page from counts cross join settings ), entries as ( select * from matched order by rank limit (select page_size from pagination) offset (select (page - 1) * page_size from pagination) ) select jsonb_build_object( 'entries', coalesce((select jsonb_agg(to_jsonb(e) order by rank) from entries e), '[]'::jsonb), 'leaders', coalesce((select jsonb_agg(to_jsonb(r) order by rank) from (select * from ranked order by rank limit 3) r), '[]'::jsonb), 'total_players', (select count(*) from ranked), 'total_games', (select count(*) from completed), 'matching_players', matching_players, 'page', page, 'pages', pages ) from pagination; $$; revoke all on function public.player_leaderboard(text, text, integer, integer) from public, anon, authenticated; grant execute on function public.player_leaderboard(text, text, integer, integer) to service_role;