using SVSim.Database.Entities.Guild; using SVSim.Database.Models.Config; using SVSim.Database.Repositories.Guild; using SVSim.Database.Services; namespace SVSim.Database.Services.Guild; public sealed class GuildChatService : IGuildChatService { private readonly IGuildMemberRepository _members; private readonly IGuildChatMessageRepository _msgs; private readonly IGameConfigService _cfg; public GuildChatService( IGuildMemberRepository members, IGuildChatMessageRepository msgs, IGameConfigService cfg) { _members = members; _msgs = msgs; _cfg = cfg; } // ── System-event emission ──────────────────────────────────────────────── public async Task EmitSystemEventAsync( int guildId, long actorViewerId, GuildChatMessageType type, string? body = null, CancellationToken ct = default) { var msg = new GuildChatMessage { GuildId = guildId, AuthorViewerId = actorViewerId, MessageType = type, Body = body ?? "", CreatedAt = DateTime.UtcNow, }; await _msgs.AppendAsync(msg, ct); } // ── Window query ───────────────────────────────────────────────────────── public async Task GetWindowAsync( long viewerId, int startMessageId, int direction, int waitIntervalHint, CancellationToken ct = default) { var membership = await _members.GetMembershipAsync(viewerId, ct); if (membership is null) { var idleInterval = _cfg.Get().ChatPollIdleSeconds; return new(Array.Empty(), idleInterval); } const int limit = 50; var msgs = await _msgs.GetWindowAsync(membership.GuildId, startMessageId, direction, limit, ct); var cfg = _cfg.Get(); int wait = msgs.Count == 0 ? cfg.ChatPollIdleSeconds : cfg.ChatPollActiveSeconds; return new(msgs, wait); } // ── Post text / stamp ──────────────────────────────────────────────────── public async Task PostTextOrStampAsync(long viewerId, int type, string message, CancellationToken ct = default) { var m = await _members.GetMembershipAsync(viewerId, ct); if (m is null) return new(false, null); var cfg = _cfg.Get(); if (type == 1) { if (!int.TryParse(message, out var stampId) || !cfg.UsableStampList.Contains(stampId)) return new(false, null); } else if (type != 0) { // Only NORMAL (0) and STAMP (1) are valid via this endpoint. return new(false, null); } var now = DateTime.UtcNow; var nextId = await _msgs.GetMaxMessageIdAsync(m.GuildId, ct) + 1; var stored = await _msgs.AppendAsync(new GuildChatMessage { GuildId = m.GuildId, MessageId = nextId, AuthorViewerId = viewerId, MessageType = type == 1 ? GuildChatMessageType.Stamp : GuildChatMessageType.Normal, Body = message, CreatedAt = now, }, ct); return new(true, stored.MessageId); } public Task PostDeckAsync(long viewerId, string deckPayloadJson, CancellationToken ct = default) => throw new NotImplementedException(); public Task DeleteDeckAsync(long viewerId, int messageId, CancellationToken ct = default) => throw new NotImplementedException(); public Task PostReplayAsync(long viewerId, string replayPayloadJson, CancellationToken ct = default) => throw new NotImplementedException(); public Task GetReplayDetailAsync(long viewerId, int messageId, CancellationToken ct = default) => throw new NotImplementedException(); public Task GetDeckLogAsync(long viewerId, int messageId, CancellationToken ct = default) => throw new NotImplementedException(); }