using AutoMapper; using Mws.Application.Common; using Mws.Domain.Documents; using Mws.Domain.Projects; namespace Mws.Application.Projects; public class ProjectService(IUnitOfWork uow, IMapper mapper) : IProjectService { public async Task> GetProjectsAsync(Guid userId, CancellationToken ct = default) { var projects = await uow.Projects.GetForUserAsync(userId, ct); return mapper.Map>(projects); } public async Task GetProjectAsync(Guid userId, Guid projectId, CancellationToken ct = default) { var project = await GetProjectForUserAsync(userId, projectId, ct); return mapper.Map(project); } public async Task CreateProjectAsync(Guid userId, CreateProjectRequest request, CancellationToken ct = default) { if (string.IsNullOrWhiteSpace(request.Name)) { throw new BadRequestException("Project name is required"); } var now = DateTime.UtcNow; var project = new Project { Id = Guid.NewGuid(), Name = request.Name.Trim(), Description = request.Description, Status = ProjectStatus.Active, CreatedAt = now, UpdatedAt = now, }; project.Members.Add(new ProjectMember { ProjectId = project.Id, UserId = userId, Role = MemberRole.Owner, }); uow.Projects.Add(project); await uow.SaveChangesAsync(ct); return mapper.Map(project); } public async Task UpdateProjectAsync(Guid userId, Guid projectId, UpdateProjectRequest request, CancellationToken ct = default) { var project = await GetProjectForUserAsync(userId, projectId, ct); if (MemberRole.Owner != await GetMemberRoleAsync(userId, projectId, ct)) { throw new ForbiddenException("Only the project owner can update the project"); } if (string.IsNullOrWhiteSpace(request.Name)) { throw new BadRequestException("Project name is required"); } project.Name = request.Name.Trim(); project.Description = request.Description; project.Status = request.Status; project.UpdatedAt = DateTime.UtcNow; await uow.SaveChangesAsync(ct); return mapper.Map(project); } public async Task ArchiveProjectAsync(Guid userId, Guid projectId, CancellationToken ct = default) { var project = await GetProjectForUserAsync(userId, projectId, ct); if (await GetMemberRoleAsync(userId, projectId, ct) != MemberRole.Owner) { throw new ForbiddenException("Only the project owner can archive the project"); } project.Status = ProjectStatus.Archived; project.UpdatedAt = DateTime.UtcNow; await uow.SaveChangesAsync(ct); } public async Task GetOverviewAsync(Guid userId, Guid projectId, CancellationToken ct = default) { var project = await GetProjectForUserAsync(userId, projectId, ct); var memberCount = await uow.Projects.CountMembersAsync(projectId, ct); var documentCount = await uow.Documents.CountByTypeForProjectAsync(projectId, DocumentType.Document, ct); var taskCounts = await uow.Tasks.CountByStatusForProjectAsync(projectId, ct); var taskCountsByStatus = taskCounts.ToDictionary(kv => kv.Key.ToString(), kv => kv.Value); var recentTasks = await uow.Tasks.GetRecentForProjectAsync(projectId, 5, ct); var recentDocuments = await uow.Documents.GetRecentForProjectAsync(projectId, DocumentType.Document, 5, ct); return new ProjectOverviewDto { Project = mapper.Map(project), MemberCount = memberCount, DocumentCount = documentCount, TaskCountsByStatus = taskCountsByStatus, RecentTasks = mapper.Map>(recentTasks), RecentDocuments = mapper.Map>(recentDocuments), }; } public async Task> SearchAsync(Guid userId, string term, CancellationToken ct = default) { var projects = await uow.Projects.SearchForUserAsync(userId, term, ct); return mapper.Map>(projects); } protected async Task GetProjectForUserAsync(Guid userId, Guid projectId, CancellationToken ct = default) { return await uow.Projects.GetForUserAsync(userId, projectId, ct) ?? throw new NotFoundException("Project not found"); } protected Task GetMemberRoleAsync(Guid userId, Guid projectId, CancellationToken ct = default) => uow.Projects.GetMemberRoleAsync(projectId, userId, ct); }