using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using SVSim.Database; using SVSim.Database.Services.Guild; using SVSim.EmulatedEntrypoint.Models.Dtos.Common; using SVSim.EmulatedEntrypoint.Models.Dtos.Common.Guild; using SVSim.EmulatedEntrypoint.Models.Dtos.GuildChat; using SVSim.EmulatedEntrypoint.Models.Dtos.Requests; namespace SVSim.EmulatedEntrypoint.Controllers; /// /guild_chat/* — 7 endpoints. See docs/api-spec/endpoints/post-login/guild_chat-*.md. [Route("guild_chat")] public sealed class GuildChatController : SVSimController { private readonly IGuildChatService _chat; private readonly SVSimDbContext _db; public GuildChatController(IGuildChatService chat, SVSimDbContext db) { _chat = chat; _db = db; } [HttpPost("messages")] public async Task> Messages( [FromBody] GuildChatMessagesRequest req, CancellationToken ct) { if (!TryGetViewerId(out var viewerId)) return Unauthorized(); var window = await _chat.GetWindowAsync( viewerId, (int)req.StartMessageId, req.Direction, req.WaitInterval, ct); // Build chat_message list var chatMessages = window.Messages.Select(m => new ChatMessageDto { ViewerId = m.AuthorViewerId, MessageId = m.MessageId, MessageType = (int)m.MessageType, CreateTime = new DateTimeOffset(m.CreatedAt, TimeSpan.Zero).ToUnixTimeSeconds(), Message = m.Body, // deck / replay / room remain null until Task 17 }).ToList(); // Build deduplicated users[] catalog from message authors var authorIds = window.Messages .Select(m => m.AuthorViewerId) .Distinct() .ToList(); var viewers = authorIds.Count == 0 ? new Dictionary() : await _db.Viewers .AsNoTracking() .Include(v => v.Info.SelectedEmblem) .Include(v => v.Info.SelectedDegree) .Where(v => authorIds.Contains(v.Id)) .ToDictionaryAsync(v => v.Id, ct); var users = authorIds.Select(vid => { viewers.TryGetValue(vid, out var v); return new ChatUserDto { ViewerId = vid, Name = v?.DisplayName ?? "", EmblemId = v?.Info?.SelectedEmblem?.Id is > 0 ? v.Info.SelectedEmblem.Id : 100_000_000L, CountryCode = v?.Info?.CountryCode ?? "", Rank = 1, // TODO: real rank when rank tracking lands DegreeId = v?.Info?.SelectedDegree?.Id ?? 0, }; }).ToList(); return new GuildChatMessagesResponse { MaintenanceCardList = new(), Users = users, ChatMessage = chatMessages, WaitInterval = window.WaitIntervalSeconds, }; } [HttpPost("post")] public Task> Post([FromBody] GuildChatPostRequest req, CancellationToken ct) => Task.FromResult>(new GuildChatPostResponse()); [HttpPost("add_deck")] public Task> AddDeck([FromBody] GuildChatAddDeckRequest req, CancellationToken ct) => Task.FromResult>(new EmptyResponse()); [HttpPost("delete_deck")] public Task> DeleteDeck([FromBody] GuildChatDeleteDeckRequest req, CancellationToken ct) => Task.FromResult>(new EmptyResponse()); [HttpPost("add_replay")] public Task> AddReplay([FromBody] GuildChatAddReplayRequest req, CancellationToken ct) => Task.FromResult>(new EmptyResponse()); [HttpPost("replay_detail")] public Task> ReplayDetail([FromBody] GuildChatReplayDetailRequest req, CancellationToken ct) => Task.FromResult>(new GuildChatReplayDetailResponse()); [HttpPost("deck_log")] public Task> DeckLog([FromBody] GuildChatDeckLogRequest req, CancellationToken ct) => Task.FromResult>(new GuildChatDeckLogResponse()); }