using Knot.Modules.Conversations.Application.Chats.Avatar; using Knot.Modules.Conversations.Application.Chats.Clear; using Knot.Modules.Conversations.Application.Chats.Create; using Knot.Modules.Conversations.Application.Chats.GetChatById; using Knot.Modules.Conversations.Application.Chats.GetChats; using Knot.Modules.Conversations.Application.Chats.GetOrCreateFavorites; using Knot.Modules.Conversations.Application.Chats.LeaveOrDelete; using Knot.Modules.Conversations.Application.Chats.Members; using Knot.Modules.Conversations.Application.Chats.TogglePin; using Knot.Modules.Conversations.Application.Chats.Update; using Knot.Modules.Conversations.Application.DTOs; using Knot.Contracts.Conversations.Domain; using Knot.Shared.Kernel; using MediatR; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Routing; namespace Knot.Modules.Conversations.Presentation.Endpoints; public static class ChatsEndpoints { public static void MapChatsEndpoints(this WebApplication app) { var group = app.MapGroup("api/chats").RequireAuthorization(); group.MapGet("", async (ISender sender, IUserContext userContext, CancellationToken ct) => { var result = await sender.Send(new GetChatsQuery(userContext.UserId), ct); return result.IsSuccess ? Results.Ok(result.Value) : Results.BadRequest(result.Error.Description); }); group.MapPost("", async ([FromBody] CreateChatRequest request, ISender sender, IUserContext userContext, CancellationToken ct) => { var command = new CreateChatCommand(request.Name, request.Type, request.MemberIds); var result = await sender.Send(command, ct); if (result.IsFailure) return Results.BadRequest(result.Error.Description); var chatResult = await sender.Send(new GetChatByIdQuery(userContext.UserId, result.Value), ct); return Results.Ok(chatResult.Value); }); group.MapPost("personal", async ([FromBody] CreatePersonalChatRequest request, ISender sender, IUserContext userContext, CancellationToken ct) => { var command = new CreateChatCommand(string.Empty, ChatType.Personal, new List { userContext.UserId, request.UserId }); var result = await sender.Send(command, ct); if (result.IsFailure) return Results.BadRequest(result.Error.Description); var chatResult = await sender.Send(new GetChatByIdQuery(userContext.UserId, result.Value), ct); return Results.Ok(chatResult.Value); }); group.MapPost("group", async ([FromBody] CreateGroupChatRequest request, ISender sender, IUserContext userContext, CancellationToken ct) => { var memberIds = request.MemberIds.ToList(); if (memberIds.Contains(userContext.UserId)) memberIds.Remove(userContext.UserId); memberIds.Insert(0, userContext.UserId); var command = new CreateChatCommand(request.Name, ChatType.Group, memberIds); var result = await sender.Send(command, ct); if (result.IsFailure) return Results.BadRequest(result.Error.Description); var chatResult = await sender.Send(new GetChatByIdQuery(userContext.UserId, result.Value), ct); return Results.Ok(chatResult.Value); }); group.MapPost("favorites", async (ISender sender, IUserContext userContext, CancellationToken ct) => { var result = await sender.Send(new GetOrCreateFavoritesCommand(userContext.UserId), ct); if (result.IsFailure) return Results.BadRequest(result.Error.Description); var chatResult = await sender.Send(new GetChatByIdQuery(userContext.UserId, result.Value), ct); return Results.Ok(chatResult.Value); }); group.MapPut("{id:guid}", async ([FromRoute] Guid id, [FromBody] UpdateChatRequest request, ISender sender, IUserContext userContext, CancellationToken ct) => { var result = await sender.Send(new UpdateChatCommand(id, userContext.UserId, request.Name, request.Description), ct); if (result.IsFailure) return Results.NotFound(); var chatResult = await sender.Send(new GetChatByIdQuery(userContext.UserId, result.Value), ct); return Results.Ok(chatResult.Value); }); group.MapDelete("{id:guid}", async ([FromRoute] Guid id, ISender sender, IUserContext userContext, CancellationToken ct) => { var result = await sender.Send(new LeaveOrDeleteChatCommand(id, userContext.UserId), ct); if (result.IsFailure) { if (result.Error.Code == "Unauthorized") return Results.Forbid(); return Results.NotFound(); } return Results.Ok(result.Value); }); group.MapPost("{id:guid}/clear", async ([FromRoute] Guid id, ISender sender, IUserContext userContext, CancellationToken ct) => { var result = await sender.Send(new ClearChatCommand(id, userContext.UserId), ct); return result.IsSuccess ? Results.Ok(result.Value) : Results.NotFound(); }); group.MapPost("{id:guid}/pin", async ([FromRoute] Guid id, ISender sender, IUserContext userContext, CancellationToken ct) => { var result = await sender.Send(new TogglePinCommand(id, userContext.UserId), ct); return result.IsSuccess ? Results.Ok(result.Value) : Results.NotFound(); }); group.MapPost("{id:guid}/members", async ([FromRoute] Guid id, [FromBody] AddMembersRequest request, ISender sender, IUserContext userContext, CancellationToken ct) => { var result = await sender.Send(new AddMembersCommand(id, userContext.UserId, request.UserIds.ToList()), ct); if (result.IsFailure) return Results.NotFound(); var chatResult = await sender.Send(new GetChatByIdQuery(userContext.UserId, result.Value), ct); return Results.Ok(chatResult.Value); }); group.MapDelete("{id:guid}/members/{userId:guid}", async ([FromRoute] Guid id, [FromRoute] Guid userId, ISender sender, IUserContext userContext, CancellationToken ct) => { var result = await sender.Send(new RemoveMemberCommand(id, userContext.UserId, userId), ct); if (result.IsFailure) return Results.NotFound(); var chatResult = await sender.Send(new GetChatByIdQuery(userContext.UserId, result.Value), ct); return Results.Ok(chatResult.Value); }); group.MapPost("{id:guid}/avatar", async ([FromRoute] Guid id, HttpRequest req, ISender sender, IUserContext userContext, CancellationToken ct) => { if (!req.HasFormContentType) return Results.BadRequest("No file"); var form = await req.ReadFormAsync(ct); var avatar = form.Files.FirstOrDefault(); if (avatar == null || avatar.Length == 0) return Results.BadRequest("No file"); using var stream = avatar.OpenReadStream(); var result = await sender.Send(new UploadGroupAvatarCommand(id, userContext.UserId, avatar.FileName, avatar.ContentType, stream), ct); if (result.IsFailure) return Results.NotFound(); var chatResult = await sender.Send(new GetChatByIdQuery(userContext.UserId, result.Value), ct); return Results.Ok(chatResult.Value); }).DisableAntiforgery(); group.MapPost("{id:guid}/avatar/crop", async ([FromRoute] Guid id, HttpRequest req, ISender sender, IUserContext userContext, CancellationToken ct) => { if (!req.HasFormContentType) return Results.BadRequest("No file"); var form = await req.ReadFormAsync(ct); var avatar = form.Files.FirstOrDefault(); if (avatar == null || avatar.Length == 0) return Results.BadRequest("No file"); int.TryParse(form["x"], out int x); int.TryParse(form["y"], out int y); int.TryParse(form["width"], out int width); int.TryParse(form["height"], out int height); using var stream = avatar.OpenReadStream(); var result = await sender.Send(new CropGroupAvatarCommand(id, userContext.UserId, avatar.FileName ?? "avatar.jpg", avatar.ContentType, stream, x, y, width, height), ct); if (result.IsFailure) return Results.NotFound(); var chatResult = await sender.Send(new GetChatByIdQuery(userContext.UserId, result.Value), ct); return Results.Ok(chatResult.Value); }).DisableAntiforgery(); group.MapDelete("{id:guid}/avatar", async ([FromRoute] Guid id, ISender sender, IUserContext userContext, CancellationToken ct) => { var result = await sender.Send(new RemoveGroupAvatarCommand(id, userContext.UserId), ct); if (result.IsFailure) return Results.NotFound(); var chatResult = await sender.Send(new GetChatByIdQuery(userContext.UserId, result.Value), ct); return Results.Ok(chatResult.Value); }); } }