using System.Text.Json; using Hokm.Server.Data; using Hokm.Server.Game; using Hokm.Server.Hubs; using Hokm.Server.Profiles; using Microsoft.AspNetCore.SignalR; using Microsoft.EntityFrameworkCore; namespace Hokm.Server.Social; public class SocialService { private readonly AppDbContext _db; private readonly GameManager _mgr; private readonly IHubContext _hub; public SocialService(AppDbContext db, GameManager mgr, IHubContext hub) { _db = db; _mgr = mgr; _hub = hub; } private async Task FriendDtoFor(string userId) { var row = await _db.Profiles.FindAsync(userId); var p = row != null ? JsonSerializer.Deserialize(row.Json, JsonOpts.Default) : null; return new FriendDto { Id = userId, Username = p?.Username ?? userId, DisplayName = p?.DisplayName ?? userId, Avatar = p?.Avatar ?? "a-fox", Level = p?.Level ?? 1, Rating = p?.Rating ?? 1000, Status = _mgr.IsOnline(userId) ? "online" : "offline", }; } /* ----------------------------- friends ----------------------------- */ public async Task> ListFriends(string uid) { var ids = await _db.Friends.Where(f => f.UserId == uid).Select(f => f.FriendId).ToListAsync(); var list = new List(); foreach (var id in ids) list.Add(await FriendDtoFor(id)); return list; } public async Task> ListRequests(string uid) { var reqs = await _db.FriendRequests.Where(r => r.ToUserId == uid).ToListAsync(); var list = new List(); foreach (var r in reqs) list.Add(new FriendRequestDto { Id = r.Id.ToString(), From = await FriendDtoFor(r.FromUserId), CreatedAt = new DateTimeOffset(r.CreatedAt).ToUnixTimeMilliseconds() }); return list; } public async Task<(bool ok, string messageFa, string messageEn)> AddFriend(string uid, string query) { var digits = new string(query.Where(char.IsDigit).ToArray()); var targetId = query.Contains(':') ? query.Trim() : (digits.Length >= 4 ? "phone:" + digits : query.Trim()); var target = await _db.Profiles.FindAsync(targetId); if (target == null || targetId == uid) return (false, "کاربر پیدا نشد", "User not found"); if (await _db.Friends.AnyAsync(f => f.UserId == uid && f.FriendId == targetId)) return (false, "از قبل دوست هستید", "Already friends"); if (!await _db.FriendRequests.AnyAsync(r => r.FromUserId == uid && r.ToUserId == targetId)) { _db.FriendRequests.Add(new FriendRequestRow { FromUserId = uid, ToUserId = targetId, CreatedAt = DateTime.UtcNow }); await _db.SaveChangesAsync(); await _hub.Clients.User(targetId).SendAsync("friendRequest", await FriendDtoFor(uid)); } return (true, "درخواست دوستی ارسال شد", "Friend request sent"); } public async Task Accept(string uid, long requestId) { var req = await _db.FriendRequests.FirstOrDefaultAsync(r => r.Id == requestId && r.ToUserId == uid); if (req == null) return; _db.Friends.Add(new FriendEdgeRow { UserId = uid, FriendId = req.FromUserId }); _db.Friends.Add(new FriendEdgeRow { UserId = req.FromUserId, FriendId = uid }); _db.FriendRequests.Remove(req); await _db.SaveChangesAsync(); await _hub.Clients.User(req.FromUserId).SendAsync("social", "friend-added"); } public async Task Decline(string uid, long requestId) { var req = await _db.FriendRequests.FirstOrDefaultAsync(r => r.Id == requestId && r.ToUserId == uid); if (req != null) { _db.FriendRequests.Remove(req); await _db.SaveChangesAsync(); } } public async Task Remove(string uid, string friendId) { var edges = await _db.Friends.Where(f => (f.UserId == uid && f.FriendId == friendId) || (f.UserId == friendId && f.FriendId == uid)).ToListAsync(); _db.Friends.RemoveRange(edges); await _db.SaveChangesAsync(); await _hub.Clients.User(friendId).SendAsync("social", "friend-removed"); } /* ------------------------------- chat ------------------------------ */ public async Task> Conversations(string uid) { var msgs = await _db.Messages.Where(m => m.UserId == uid || m.PeerId == uid).ToListAsync(); var byPartner = msgs.GroupBy(m => m.UserId == uid ? m.PeerId : m.UserId); var convs = new List(); foreach (var g in byPartner) { var last = g.OrderByDescending(m => m.CreatedAt).First(); convs.Add(new ConversationDto { Friend = await FriendDtoFor(g.Key), LastMessage = ToDto(last, uid), Unread = g.Count(m => m.PeerId == uid && !m.ReadByPeer), }); } return convs.OrderByDescending(c => c.LastMessage?.Ts ?? 0).ToList(); } public async Task> Messages(string uid, string peerId) { var msgs = await _db.Messages .Where(m => (m.UserId == uid && m.PeerId == peerId) || (m.UserId == peerId && m.PeerId == uid)) .OrderBy(m => m.CreatedAt).ToListAsync(); var unread = msgs.Where(m => m.UserId == peerId && m.PeerId == uid && !m.ReadByPeer).ToList(); if (unread.Count > 0) { unread.ForEach(m => m.ReadByPeer = true); await _db.SaveChangesAsync(); } return msgs.Select(m => ToDto(m, uid)).ToList(); } public async Task Send(string uid, string peerId, string text) { var m = new MessageRow { UserId = uid, PeerId = peerId, Text = text.Trim(), CreatedAt = DateTime.UtcNow }; _db.Messages.Add(m); await _db.SaveChangesAsync(); await _hub.Clients.User(peerId).SendAsync("chat", new { peerId = uid }); return ToDto(m, uid); } private static ChatMessageDto ToDto(MessageRow m, string uid) => new() { Id = m.Id.ToString(), FromMe = m.UserId == uid, Text = m.Text, Ts = new DateTimeOffset(m.CreatedAt).ToUnixTimeMilliseconds(), }; }