using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Nuuru.Server.Auth; using Nuuru.Server.DTOs.Clan; using Nuuru.Server.Extensions; using Nuuru.Server.Services; namespace Nuuru.Server.Controllers { [ApiController] [Route("api/clans")] public class ClanController : ControllerBase { private readonly IClanService _clanService; public ClanController(IClanService clanService) => _clanService = clanService; private IActionResult FromResult(Models.ServiceResult result, string successMessage = "OK") { if (!result.Success) return BadRequest(new { error = result.Error }); return Ok(new { message = successMessage }); } [HttpPost] [Authorize] public async Task CreateClan([FromBody] CreateClanRequest request) { var userId = User.GetUserId(); if (userId == null) return Unauthorized(); var clan = await _clanService.CreateClanAsync(userId.Value, request); if (clan == null) return BadRequest(new { error = "Failed to create clan. Check your balance, name/tag availability, and that you're not already in a clan." }); return Ok(clan); } [HttpGet("{id:int}")] [AllowAnonymous] public async Task GetClan(int id) { var clan = await _clanService.GetClanAsync(id); return clan == null ? NotFound() : Ok(clan); } [HttpPut("{id:int}")] [Authorize] public async Task UpdateClan(int id, [FromBody] UpdateClanRequest request) { var userId = User.GetUserId(); if (userId == null) return Unauthorized(); return FromResult(await _clanService.UpdateClanAsync(userId.Value, id, request), "Clan updated."); } [HttpPost("{id:int}/invite")] [Authorize] public async Task InviteUser(int id, [FromBody] InviteUserRequest request) { var userId = User.GetUserId(); if (userId == null) return Unauthorized(); return FromResult(await _clanService.InviteUserAsync(userId.Value, id, request.TargetUserId), "Invite sent."); } [HttpGet("{id:int}/invites")] [Authorize] public async Task GetSentInvites(int id) { var userId = User.GetUserId(); if (userId == null) return Unauthorized(); return Ok(await _clanService.GetSentInvitesAsync(userId.Value, id)); } [HttpDelete("{id:int}/invites/{inviteId:int}")] [Authorize] public async Task RevokeInvite(int id, int inviteId) { var userId = User.GetUserId(); if (userId == null) return Unauthorized(); return FromResult(await _clanService.RevokeInviteAsync(userId.Value, id, inviteId), "Invite revoked."); } [HttpPost("invites/{inviteId:int}/accept")] [Authorize] public async Task AcceptInvite(int inviteId) { var userId = User.GetUserId(); if (userId == null) return Unauthorized(); return FromResult(await _clanService.AcceptInviteAsync(userId.Value, inviteId), "Joined clan."); } [HttpDelete("invites/{inviteId:int}")] [Authorize] public async Task DeclineInvite(int inviteId) { var userId = User.GetUserId(); if (userId == null) return Unauthorized(); return FromResult(await _clanService.DeclineInviteAsync(userId.Value, inviteId), "Invite declined."); } [HttpGet("invites")] [Authorize] public async Task GetMyInvites() { var userId = User.GetUserId(); if (userId == null) return Unauthorized(); return Ok(await _clanService.GetPendingInvitesAsync(userId.Value)); } [HttpPost("{id:int}/apply")] [Authorize] public async Task ApplyToClan(int id) { var userId = User.GetUserId(); if (userId == null) return Unauthorized(); return FromResult(await _clanService.ApplyToClanAsync(userId.Value, id), "Application sent."); } [HttpPost("{id:int}/applications/{applicationId:int}/accept")] [Authorize] public async Task AcceptApplication(int id, int applicationId) { var userId = User.GetUserId(); if (userId == null) return Unauthorized(); return FromResult(await _clanService.AcceptApplicationAsync(userId.Value, id, applicationId), "Application accepted."); } [HttpDelete("{id:int}/applications/{applicationId:int}")] [Authorize] public async Task RejectApplication(int id, int applicationId) { var userId = User.GetUserId(); if (userId == null) return Unauthorized(); return FromResult(await _clanService.RejectApplicationAsync(userId.Value, id, applicationId), "Application rejected."); } [HttpGet("{id:int}/applications")] [Authorize] public async Task GetApplications(int id) { var userId = User.GetUserId(); if (userId == null) return Unauthorized(); return Ok(await _clanService.GetPendingApplicationsAsync(userId.Value, id)); } [HttpPost("leave")] [Authorize] public async Task LeaveClan() { var userId = User.GetUserId(); if (userId == null) return Unauthorized(); return FromResult(await _clanService.LeaveClanAsync(userId.Value), "Left clan."); } [HttpPost("{id:int}/kick/{targetUserId:guid}")] [Authorize] public async Task KickMember(int id, Guid targetUserId) { var userId = User.GetUserId(); if (userId == null) return Unauthorized(); return FromResult(await _clanService.KickMemberAsync(userId.Value, id, targetUserId), "Member kicked."); } [HttpPost("{id:int}/expand")] [Authorize] public async Task ExpandSlots(int id) { var userId = User.GetUserId(); if (userId == null) return Unauthorized(); return FromResult(await _clanService.ExpandSlotsAsync(userId.Value, id), "Slots expanded."); } [HttpPost("{id:int}/deposit")] [Authorize] public async Task Deposit(int id, [FromBody] DepositRequest request) { var userId = User.GetUserId(); if (userId == null) return Unauthorized(); return FromResult(await _clanService.DepositToTreasuryAsync(userId.Value, request.Amount), $"Deposited {request.Amount} boints."); } [HttpPost("{id:int}/withdraw")] [Authorize] public async Task Withdraw(int id, [FromBody] WithdrawRequest request) { var userId = User.GetUserId(); if (userId == null) return Unauthorized(); return FromResult(await _clanService.WithdrawFromTreasuryAsync(userId.Value, id, request.TargetUserId, request.Amount), $"Sent {request.Amount} boints from treasury."); } [HttpPost("{id:int}/forum")] [Authorize] public async Task PurchaseForum(int id) { var userId = User.GetUserId(); if (userId == null) return Unauthorized(); return FromResult(await _clanService.PurchaseClanForumAsync(userId.Value, id), "Clan forum created."); } [HttpGet("leaderboard")] [AllowAnonymous] public async Task GetLeaderboard([FromQuery] string sort = "members", [FromQuery] int page = 1, [FromQuery] int pageSize = 20) { var (items, totalCount) = await _clanService.GetLeaderboardAsync(sort, page, pageSize); return Ok(new { items, totalCount }); } [HttpGet("my")] [Authorize] public async Task GetMyClan() { var userId = User.GetUserId(); if (userId == null) return Unauthorized(); var clan = await _clanService.GetUserClanAsync(userId.Value); return Ok(clan); } [HttpGet("{id:int}/members")] [AllowAnonymous] public async Task GetMembers(int id) { var members = await _clanService.GetMembersAsync(id); return Ok(members); } [HttpGet("by-tag/{tag}")] [AllowAnonymous] public async Task GetClanByTag(string tag) { var clan = await _clanService.GetClanByTagAsync(tag); return clan == null ? NotFound() : Ok(clan); } [HttpPost("{id:int}/badge")] [Authorize] public async Task UploadBadge(int id, [FromForm] IFormFile file, [FromForm] int? cropX, [FromForm] int? cropY, [FromForm] int? cropSize) { var userId = User.GetUserId(); if (userId == null) return Unauthorized(); if (file == null || file.Length == 0) return BadRequest(new { error = "No file provided." }); if (file.Length > 5 * 1024 * 1024) return BadRequest(new { error = "File too large. Max 5MB." }); CropRect? crop = null; if (cropX.HasValue && cropY.HasValue && cropSize.HasValue) crop = new CropRect(cropX.Value, cropY.Value, cropSize.Value); using var stream = file.OpenReadStream(); var result = await _clanService.UploadBadgeAsync(userId.Value, id, stream, file.FileName, crop); if (!result.Success) return BadRequest(new { error = result.Error }); return Ok(new { message = "Badge uploaded." }); } [HttpGet("{id:int}/badge")] [AllowAnonymous] public async Task GetBadge(int id) { var result = await _clanService.GetBadgeFileAsync(id); if (result == null) return NotFound(); return File(result.Stream, result.Metadata.ContentType); } } }