using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using RR3CommunityServer.Data; using RR3CommunityServer.Models; namespace RR3CommunityServer.Controllers; [ApiController] [Route("synergy/friends")] public class FriendsController : ControllerBase { private readonly RR3DbContext _context; private readonly ILogger _logger; public FriendsController(RR3DbContext context, ILogger logger) { _context = context; _logger = logger; } // ===== FRIEND MANAGEMENT (4 endpoints) ===== // GET /synergy/friends/list - Get player's friend list [HttpGet("list")] public async Task> GetFriendsList([FromQuery] string synergyId) { try { var user = await _context.Users.FirstOrDefaultAsync(u => u.SynergyId == synergyId); if (user == null) { return Ok(new FriendsListResponse { ResultCode = -1, Message = "User not found" }); } // Get friends where user is either User1 or User2 var friends = await _context.Friends .Where(f => f.User1Id == user.Id || f.User2Id == user.Id) .Include(f => f.User1) .Include(f => f.User2) .ToListAsync(); var friendDtos = friends.Select(f => { var friend = f.User1Id == user.Id ? f.User2 : f.User1; return new FriendDto { UserId = friend!.Id, Nickname = friend.Nickname ?? "Player", SynergyId = friend.SynergyId, Level = friend.Level ?? 1, LastOnline = friend.CreatedAt, FriendsSince = f.CreatedAt }; }).ToList(); return Ok(new FriendsListResponse { ResultCode = 0, Friends = friendDtos, TotalCount = friendDtos.Count }); } catch (Exception ex) { _logger.LogError(ex, "Error getting friends list for {SynergyId}", synergyId); return Ok(new FriendsListResponse { ResultCode = -1, Message = "Error loading friends" }); } } // POST /synergy/friends/add - Send friend request [HttpPost("add")] public async Task> SendFriendRequest( [FromQuery] string synergyId, [FromQuery] string? targetSynergyId = null, [FromQuery] string? targetUsername = null) { try { var user = await _context.Users.FirstOrDefaultAsync(u => u.SynergyId == synergyId); if (user == null) { return Ok(new SimpleResponse { ResultCode = -1, Message = "User not found" }); } // Find target user by SynergyId or Username User? targetUser = null; if (!string.IsNullOrEmpty(targetSynergyId)) { targetUser = await _context.Users.FirstOrDefaultAsync(u => u.SynergyId == targetSynergyId); } else if (!string.IsNullOrEmpty(targetUsername)) { targetUser = await _context.Users.FirstOrDefaultAsync(u => u.Nickname == targetUsername); } if (targetUser == null) { return Ok(new SimpleResponse { ResultCode = -2, Message = "Target user not found" }); } if (user.Id == targetUser.Id) { return Ok(new SimpleResponse { ResultCode = -3, Message = "Cannot add yourself as friend" }); } // Check if already friends var existingFriendship = await _context.Friends .AnyAsync(f => (f.User1Id == user.Id && f.User2Id == targetUser.Id) || (f.User1Id == targetUser.Id && f.User2Id == user.Id)); if (existingFriendship) { return Ok(new SimpleResponse { ResultCode = -4, Message = "Already friends" }); } // Check for existing pending invitation var existingInvite = await _context.FriendInvitations .FirstOrDefaultAsync(i => i.SenderId == user.Id && i.ReceiverId == targetUser.Id && i.Status == "pending"); if (existingInvite != null) { return Ok(new SimpleResponse { ResultCode = -5, Message = "Friend request already sent" }); } // Create friend invitation var invitation = new FriendInvitation { SenderId = user.Id, ReceiverId = targetUser.Id, Status = "pending", CreatedAt = DateTime.UtcNow, ExpiresAt = DateTime.UtcNow.AddDays(7) }; _context.FriendInvitations.Add(invitation); await _context.SaveChangesAsync(); _logger.LogInformation("Friend request sent: {Sender} -> {Receiver}", user.SynergyId, targetUser.SynergyId); return Ok(new SimpleResponse { ResultCode = 0, Message = "Friend request sent" }); } catch (Exception ex) { _logger.LogError(ex, "Error sending friend request"); return Ok(new SimpleResponse { ResultCode = -1, Message = "Error sending request" }); } } // POST /synergy/friends/accept - Accept friend request [HttpPost("accept")] public async Task> AcceptFriendRequest( [FromQuery] string synergyId, [FromQuery] int invitationId) { try { var user = await _context.Users.FirstOrDefaultAsync(u => u.SynergyId == synergyId); if (user == null) { return Ok(new SimpleResponse { ResultCode = -1, Message = "User not found" }); } var invitation = await _context.FriendInvitations .FirstOrDefaultAsync(i => i.Id == invitationId && i.ReceiverId == user.Id && i.Status == "pending"); if (invitation == null) { return Ok(new SimpleResponse { ResultCode = -2, Message = "Invitation not found" }); } if (invitation.ExpiresAt < DateTime.UtcNow) { invitation.Status = "expired"; await _context.SaveChangesAsync(); return Ok(new SimpleResponse { ResultCode = -3, Message = "Invitation expired" }); } // Create friendship var friendship = new Friend { User1Id = invitation.SenderId, User2Id = invitation.ReceiverId, CreatedAt = DateTime.UtcNow }; _context.Friends.Add(friendship); // Update invitation status invitation.Status = "accepted"; invitation.RespondedAt = DateTime.UtcNow; await _context.SaveChangesAsync(); _logger.LogInformation("Friend request accepted: {Invitation}", invitationId); return Ok(new SimpleResponse { ResultCode = 0, Message = "Friend request accepted" }); } catch (Exception ex) { _logger.LogError(ex, "Error accepting friend request"); return Ok(new SimpleResponse { ResultCode = -1, Message = "Error accepting request" }); } } // DELETE /synergy/friends/remove - Remove friend [HttpDelete("remove")] public async Task> RemoveFriend( [FromQuery] string synergyId, [FromQuery] string friendSynergyId) { try { var user = await _context.Users.FirstOrDefaultAsync(u => u.SynergyId == synergyId); var friend = await _context.Users.FirstOrDefaultAsync(u => u.SynergyId == friendSynergyId); if (user == null || friend == null) { return Ok(new SimpleResponse { ResultCode = -1, Message = "User not found" }); } var friendship = await _context.Friends .FirstOrDefaultAsync(f => (f.User1Id == user.Id && f.User2Id == friend.Id) || (f.User1Id == friend.Id && f.User2Id == user.Id)); if (friendship == null) { return Ok(new SimpleResponse { ResultCode = -2, Message = "Not friends" }); } _context.Friends.Remove(friendship); await _context.SaveChangesAsync(); _logger.LogInformation("Friendship removed: {User1} <-> {User2}", user.SynergyId, friend.SynergyId); return Ok(new SimpleResponse { ResultCode = 0, Message = "Friend removed" }); } catch (Exception ex) { _logger.LogError(ex, "Error removing friend"); return Ok(new SimpleResponse { ResultCode = -1, Message = "Error removing friend" }); } } // ===== SEARCH & DISCOVERY (2 endpoints) ===== // GET /synergy/friends/search - Search for players [HttpGet("search")] public async Task> SearchUsers( [FromQuery] string query, [FromQuery] string? requestingSynergyId = null, [FromQuery] int limit = 20) { try { User? requestingUser = null; if (!string.IsNullOrEmpty(requestingSynergyId)) { requestingUser = await _context.Users.FirstOrDefaultAsync(u => u.SynergyId == requestingSynergyId); } var users = await _context.Users .Where(u => (u.Nickname != null && u.Nickname.Contains(query)) || u.SynergyId.Contains(query)) .Take(limit) .ToListAsync(); var results = new List(); foreach (var user in users) { bool isFriend = false; bool hasPendingInvite = false; if (requestingUser != null) { isFriend = await _context.Friends.AnyAsync(f => (f.User1Id == requestingUser.Id && f.User2Id == user.Id) || (f.User1Id == user.Id && f.User2Id == requestingUser.Id)); hasPendingInvite = await _context.FriendInvitations.AnyAsync(i => i.SenderId == requestingUser.Id && i.ReceiverId == user.Id && i.Status == "pending"); } results.Add(new UserSearchResultDto { UserId = user.Id, Nickname = user.Nickname ?? "Player", SynergyId = user.SynergyId, Level = user.Level ?? 1, IsFriend = isFriend, HasPendingInvite = hasPendingInvite }); } return Ok(new UserSearchResponse { ResultCode = 0, Users = results }); } catch (Exception ex) { _logger.LogError(ex, "Error searching users"); return Ok(new UserSearchResponse { ResultCode = -1, Message = "Error searching users" }); } } // GET /synergy/friends/invitations/pending - Get pending friend invitations [HttpGet("invitations/pending")] public async Task> GetPendingInvitations([FromQuery] string synergyId) { try { var user = await _context.Users.FirstOrDefaultAsync(u => u.SynergyId == synergyId); if (user == null) { return Ok(new PendingInvitationsResponse { ResultCode = -1, Message = "User not found" }); } var invitations = await _context.FriendInvitations .Where(i => i.ReceiverId == user.Id && i.Status == "pending" && i.ExpiresAt > DateTime.UtcNow) .Include(i => i.Sender) .OrderByDescending(i => i.CreatedAt) .ToListAsync(); var invitationDtos = invitations.Select(i => new FriendInvitationDto { InvitationId = i.Id, SenderId = i.SenderId, SenderNickname = i.Sender!.Nickname ?? "Player", SenderSynergyId = i.Sender.SynergyId, SenderLevel = i.Sender.Level ?? 1, Status = i.Status, CreatedAt = i.CreatedAt, ExpiresAt = i.ExpiresAt }).ToList(); return Ok(new PendingInvitationsResponse { ResultCode = 0, Invitations = invitationDtos }); } catch (Exception ex) { _logger.LogError(ex, "Error getting pending invitations"); return Ok(new PendingInvitationsResponse { ResultCode = -1, Message = "Error loading invitations" }); } } // ===== GIFTS (3 endpoints) ===== // POST /synergy/friends/gift/send - Send gift to friend [HttpPost("gift/send")] public async Task> SendGift([FromBody] SendGiftRequest request) { try { var sender = await _context.Users.FirstOrDefaultAsync(u => u.SynergyId == request.SynergyId); var receiver = await _context.Users.FirstOrDefaultAsync(u => u.SynergyId == request.FriendSynergyId); if (sender == null || receiver == null) { return Ok(new SimpleResponse { ResultCode = -1, Message = "User not found" }); } // Verify they're friends var areFriends = await _context.Friends.AnyAsync(f => (f.User1Id == sender.Id && f.User2Id == receiver.Id) || (f.User1Id == receiver.Id && f.User2Id == sender.Id)); if (!areFriends) { return Ok(new SimpleResponse { ResultCode = -2, Message = "Not friends" }); } // Create gift var gift = new Gift { SenderId = sender.Id, ReceiverId = receiver.Id, GiftType = request.GiftType, Amount = request.Amount, Message = request.Message, SentAt = DateTime.UtcNow, ExpiresAt = DateTime.UtcNow.AddDays(7) }; _context.Gifts.Add(gift); await _context.SaveChangesAsync(); _logger.LogInformation("Gift sent: {Sender} -> {Receiver} ({Type} x{Amount})", sender.SynergyId, receiver.SynergyId, request.GiftType, request.Amount); return Ok(new SimpleResponse { ResultCode = 0, Message = "Gift sent" }); } catch (Exception ex) { _logger.LogError(ex, "Error sending gift"); return Ok(new SimpleResponse { ResultCode = -1, Message = "Error sending gift" }); } } // GET /synergy/friends/gifts/pending - Get pending gifts [HttpGet("gifts/pending")] public async Task> GetPendingGifts([FromQuery] string synergyId) { try { var user = await _context.Users.FirstOrDefaultAsync(u => u.SynergyId == synergyId); if (user == null) { return Ok(new PendingGiftsResponse { ResultCode = -1, Message = "User not found" }); } var gifts = await _context.Gifts .Where(g => g.ReceiverId == user.Id && !g.Claimed && g.ExpiresAt > DateTime.UtcNow) .Include(g => g.Sender) .OrderByDescending(g => g.SentAt) .ToListAsync(); var giftDtos = gifts.Select(g => new GiftDto { GiftId = g.Id, SenderId = g.SenderId, SenderNickname = g.Sender!.Nickname ?? "Player", GiftType = g.GiftType, Amount = g.Amount, Message = g.Message, SentAt = g.SentAt, ExpiresAt = g.ExpiresAt }).ToList(); return Ok(new PendingGiftsResponse { ResultCode = 0, Gifts = giftDtos }); } catch (Exception ex) { _logger.LogError(ex, "Error getting pending gifts"); return Ok(new PendingGiftsResponse { ResultCode = -1, Message = "Error loading gifts" }); } } // POST /synergy/friends/gifts/claim - Claim a gift [HttpPost("gifts/claim")] public async Task> ClaimGift( [FromQuery] string synergyId, [FromQuery] int giftId) { try { var user = await _context.Users.FirstOrDefaultAsync(u => u.SynergyId == synergyId); if (user == null) { return Ok(new ClaimGiftResponse { ResultCode = -1, Message = "User not found" }); } var gift = await _context.Gifts .FirstOrDefaultAsync(g => g.Id == giftId && g.ReceiverId == user.Id && !g.Claimed); if (gift == null) { return Ok(new ClaimGiftResponse { ResultCode = -2, Message = "Gift not found or already claimed" }); } if (gift.ExpiresAt < DateTime.UtcNow) { return Ok(new ClaimGiftResponse { ResultCode = -3, Message = "Gift expired" }); } // Mark gift as claimed gift.Claimed = true; gift.ClaimedAt = DateTime.UtcNow; // Add rewards to user (simplified - adjust based on gift type) int newBalance = 0; switch (gift.GiftType.ToLower()) { case "gold": user.Gold += gift.Amount; newBalance = user.Gold ?? 0; break; case "cash": user.Cash += gift.Amount; newBalance = user.Cash ?? 0; break; } await _context.SaveChangesAsync(); _logger.LogInformation("Gift claimed: {User} claimed gift {GiftId} ({Type} x{Amount})", user.SynergyId, giftId, gift.GiftType, gift.Amount); return Ok(new ClaimGiftResponse { ResultCode = 0, GiftType = gift.GiftType, Amount = gift.Amount, NewBalance = newBalance }); } catch (Exception ex) { _logger.LogError(ex, "Error claiming gift"); return Ok(new ClaimGiftResponse { ResultCode = -1, Message = "Error claiming gift" }); } } // ===== CLUBS/TEAMS (3 endpoints) ===== // GET /synergy/clubs/list - Get available clubs [HttpGet("/synergy/clubs/list")] public async Task> GetClubsList( [FromQuery] bool publicOnly = true, [FromQuery] bool recruitingOnly = false, [FromQuery] int limit = 50) { try { var query = _context.Clubs.AsQueryable(); if (publicOnly) query = query.Where(c => c.IsPublic); if (recruitingOnly) query = query.Where(c => c.IsRecruiting); var clubs = await query .OrderByDescending(c => c.TotalPoints) .Take(limit) .ToListAsync(); var clubDtos = new List(); foreach (var club in clubs) { var memberCount = await _context.ClubMembers.CountAsync(m => m.ClubId == club.Id); clubDtos.Add(new ClubDto { ClubId = club.Id, Name = club.Name, Description = club.Description, Tag = club.Tag, MemberCount = memberCount, MaxMembers = club.MaxMembers, IsPublic = club.IsPublic, IsRecruiting = club.IsRecruiting, TotalPoints = club.TotalPoints, CreatedAt = club.CreatedAt }); } return Ok(new ClubsListResponse { ResultCode = 0, Clubs = clubDtos }); } catch (Exception ex) { _logger.LogError(ex, "Error getting clubs list"); return Ok(new ClubsListResponse { ResultCode = -1, Message = "Error loading clubs" }); } } // POST /synergy/clubs/join - Join a club [HttpPost("/synergy/clubs/join")] public async Task> JoinClub( [FromQuery] string synergyId, [FromQuery] int clubId) { try { var user = await _context.Users.FirstOrDefaultAsync(u => u.SynergyId == synergyId); if (user == null) { return Ok(new SimpleResponse { ResultCode = -1, Message = "User not found" }); } var club = await _context.Clubs.FirstOrDefaultAsync(c => c.Id == clubId); if (club == null) { return Ok(new SimpleResponse { ResultCode = -2, Message = "Club not found" }); } // Check if user is already in a club var existingMembership = await _context.ClubMembers .AnyAsync(m => m.UserId == user.Id); if (existingMembership) { return Ok(new SimpleResponse { ResultCode = -3, Message = "Already in a club" }); } // Check if club is full var memberCount = await _context.ClubMembers.CountAsync(m => m.ClubId == clubId); if (memberCount >= club.MaxMembers) { return Ok(new SimpleResponse { ResultCode = -4, Message = "Club is full" }); } // Check if club is recruiting if (!club.IsRecruiting && club.OwnerId != user.Id) { return Ok(new SimpleResponse { ResultCode = -5, Message = "Club is not recruiting" }); } // Add member var member = new ClubMember { ClubId = clubId, UserId = user.Id, Role = "member", JoinedAt = DateTime.UtcNow }; _context.ClubMembers.Add(member); await _context.SaveChangesAsync(); _logger.LogInformation("User {User} joined club {Club}", user.SynergyId, club.Name); return Ok(new SimpleResponse { ResultCode = 0, Message = "Joined club successfully" }); } catch (Exception ex) { _logger.LogError(ex, "Error joining club"); return Ok(new SimpleResponse { ResultCode = -1, Message = "Error joining club" }); } } // GET /synergy/clubs/{clubId}/members - Get club members [HttpGet("/synergy/clubs/{clubId}/members")] public async Task> GetClubMembers(int clubId) { try { var club = await _context.Clubs.FirstOrDefaultAsync(c => c.Id == clubId); if (club == null) { return Ok(new ClubMembersResponse { ResultCode = -1, Message = "Club not found" }); } var members = await _context.ClubMembers .Where(m => m.ClubId == clubId) .Include(m => m.User) .OrderByDescending(m => m.ContributedPoints) .ToListAsync(); var memberDtos = members.Select(m => new ClubMemberDto { UserId = m.UserId, Nickname = m.User!.Nickname ?? "Player", SynergyId = m.User.SynergyId, Level = m.User.Level ?? 1, Role = m.Role, ContributedPoints = m.ContributedPoints, JoinedAt = m.JoinedAt }).ToList(); var memberCount = members.Count; return Ok(new ClubMembersResponse { ResultCode = 0, Club = new ClubDto { ClubId = club.Id, Name = club.Name, Description = club.Description, Tag = club.Tag, MemberCount = memberCount, MaxMembers = club.MaxMembers, IsPublic = club.IsPublic, IsRecruiting = club.IsRecruiting, TotalPoints = club.TotalPoints, CreatedAt = club.CreatedAt }, Members = memberDtos }); } catch (Exception ex) { _logger.LogError(ex, "Error getting club members"); return Ok(new ClubMembersResponse { ResultCode = -1, Message = "Error loading members" }); } } }