using Microsoft.AspNetCore.Mvc; using SVSim.EmulatedEntrypoint.Models.Dtos.Ranking; using SVSim.EmulatedEntrypoint.Models.Dtos.Requests; using SVSim.EmulatedEntrypoint.Services; namespace SVSim.EmulatedEntrypoint.Controllers; /// /// /ranking/* — Rankings menu. Stub: the period picker renders a real /// deterministic monthly schedule, but every leaderboard returns an empty /// `ranking: []`. See docs/superpowers/specs/2026-06-10-ranking-stubs-design.md. /// [Route("ranking")] public sealed class RankingController : SVSimController { [HttpPost("get_viewable_ranking_period_list")] public IActionResult GetViewablePeriodList([FromBody] BaseRequest req) { if (!TryGetViewerId(out _)) return Unauthorized(); var now = DateTime.UtcNow; return Ok(new PeriodListResponseDto { RankMatch = ToBase(RankingPeriodSchedule.GenerateFor(RankingPeriodSchedule.Family.RankMatch, now)), MasterPoint = ToMasterPoint(RankingPeriodSchedule.GenerateFor(RankingPeriodSchedule.Family.MasterPoint, now)), TwoPick = ToTwoPick(RankingPeriodSchedule.GenerateFor(RankingPeriodSchedule.Family.TwoPick, now)), Sealed = ToBase(RankingPeriodSchedule.GenerateFor(RankingPeriodSchedule.Family.Sealed, now)), // Crossover arrays stay empty — captured prod returned [] for both. }); } [HttpPost("master_point_rotation_info")] public IActionResult MasterPointRotation([FromBody] MasterPointInfoRequestDto req) => RankingFor(RankingPeriodSchedule.Family.MasterPoint, req.PeriodId); [HttpPost("master_point_unlimited_info")] public IActionResult MasterPointUnlimited([FromBody] MasterPointInfoRequestDto req) => RankingFor(RankingPeriodSchedule.Family.MasterPoint, req.PeriodId); [HttpPost("rank_match_class_win_rotation_info")] public IActionResult RankMatchClassWinRotation([FromBody] ClassWinInfoRequestDto req) => RankingFor(RankingPeriodSchedule.Family.RankMatch, req.PeriodId); [HttpPost("rank_match_class_win_unlimited_info")] public IActionResult RankMatchClassWinUnlimited([FromBody] ClassWinInfoRequestDto req) => RankingFor(RankingPeriodSchedule.Family.RankMatch, req.PeriodId); [HttpPost("two_pick_win_info")] public IActionResult TwoPickWin([FromBody] TwoPickWinInfoRequestDto req) => RankingFor(RankingPeriodSchedule.Family.TwoPick, req.PeriodId); private IActionResult RankingFor(RankingPeriodSchedule.Family family, int periodId) { if (!TryGetViewerId(out _)) return Unauthorized(); var entry = RankingPeriodSchedule.TryFindById(family, periodId, DateTime.UtcNow); var periodDto = entry is null ? new PeriodEntryDto { Id = periodId.ToString() } : new PeriodEntryDto { Id = entry.Id, PeriodNum = entry.PeriodNum, BeginTime = entry.BeginTime, EndTime = entry.EndTime, }; return Ok(new MonthlyRankingResponseDto { Period = periodDto, Ranking = new() }); } private static List ToBase(IReadOnlyList src) => src.Select(e => new PeriodEntryDto { Id = e.Id, PeriodNum = e.PeriodNum, BeginTime = e.BeginTime, EndTime = e.EndTime, }).ToList(); private static List ToMasterPoint(IReadOnlyList src) => src.Select(e => new MasterPointPeriodEntryDto { Id = e.Id, PeriodNum = e.PeriodNum, BeginTime = e.BeginTime, EndTime = e.EndTime, NecessaryScore = "0", }).ToList(); private static List ToTwoPick(IReadOnlyList src) => src.Select(e => new TwoPickPeriodEntryDto { Id = e.Id, PeriodNum = e.PeriodNum, BeginTime = e.BeginTime, EndTime = e.EndTime, Type = "2", Over460 = "1", }).ToList(); }