using Microsoft.EntityFrameworkCore; using WorkClub.Application.Interfaces; using WorkClub.Application.Tasks.DTOs; using WorkClub.Domain.Entities; using WorkClub.Domain.Enums; using WorkClub.Infrastructure.Data; namespace WorkClub.Api.Services; public class TaskService { private readonly AppDbContext _context; private readonly ITenantProvider _tenantProvider; public TaskService(AppDbContext context, ITenantProvider tenantProvider) { _context = context; _tenantProvider = tenantProvider; } public async Task GetTasksAsync(string? statusFilter, int page, int pageSize) { var query = _context.WorkItems.AsQueryable(); if (!string.IsNullOrEmpty(statusFilter)) { if (Enum.TryParse(statusFilter, ignoreCase: true, out var status)) { query = query.Where(w => w.Status == status); } } var total = await query.CountAsync(); var items = await query .OrderBy(w => w.CreatedAt) .Skip((page - 1) * pageSize) .Take(pageSize) .ToListAsync(); var itemDtos = items.Select(w => new TaskListItemDto( w.Id, w.Title, w.Status.ToString(), w.AssigneeId, w.CreatedAt )).ToList(); return new TaskListDto(itemDtos, total, page, pageSize); } public async Task GetTaskByIdAsync(Guid id) { var workItem = await _context.WorkItems.FindAsync(id); if (workItem == null) return null; return new TaskDetailDto( workItem.Id, workItem.Title, workItem.Description, workItem.Status.ToString(), workItem.AssigneeId, workItem.CreatedById, workItem.ClubId, workItem.DueDate, workItem.CreatedAt, workItem.UpdatedAt ); } public async Task<(TaskDetailDto? task, string? error)> CreateTaskAsync(CreateTaskRequest request, Guid createdById) { var tenantId = _tenantProvider.GetTenantId(); var workItem = new WorkItem { Id = Guid.NewGuid(), TenantId = tenantId, Title = request.Title, Description = request.Description, Status = WorkItemStatus.Open, ClubId = request.ClubId, AssigneeId = request.AssigneeId, DueDate = request.DueDate, CreatedById = createdById, CreatedAt = DateTimeOffset.UtcNow, UpdatedAt = DateTimeOffset.UtcNow }; _context.WorkItems.Add(workItem); await _context.SaveChangesAsync(); var dto = new TaskDetailDto( workItem.Id, workItem.Title, workItem.Description, workItem.Status.ToString(), workItem.AssigneeId, workItem.CreatedById, workItem.ClubId, workItem.DueDate, workItem.CreatedAt, workItem.UpdatedAt ); return (dto, null); } public async Task<(TaskDetailDto? task, string? error, bool isConflict)> UpdateTaskAsync(Guid id, UpdateTaskRequest request) { var workItem = await _context.WorkItems.FindAsync(id); if (workItem == null) return (null, "Task not found", false); if (request.Title != null) workItem.Title = request.Title; if (request.Description != null) workItem.Description = request.Description; if (request.AssigneeId.HasValue) workItem.AssigneeId = request.AssigneeId; if (request.DueDate.HasValue) workItem.DueDate = request.DueDate; if (request.Status != null) { if (!Enum.TryParse(request.Status, ignoreCase: true, out var newStatus)) { return (null, $"Invalid status: {request.Status}", false); } if (!workItem.CanTransitionTo(newStatus)) { return (null, $"Cannot transition from {workItem.Status} to {newStatus}", false); } workItem.TransitionTo(newStatus); } workItem.UpdatedAt = DateTimeOffset.UtcNow; try { await _context.SaveChangesAsync(); } catch (DbUpdateConcurrencyException) { return (null, "Task was modified by another user. Please refresh and try again.", true); } var dto = new TaskDetailDto( workItem.Id, workItem.Title, workItem.Description, workItem.Status.ToString(), workItem.AssigneeId, workItem.CreatedById, workItem.ClubId, workItem.DueDate, workItem.CreatedAt, workItem.UpdatedAt ); return (dto, null, false); } public async Task DeleteTaskAsync(Guid id) { var workItem = await _context.WorkItems.FindAsync(id); if (workItem == null) return false; _context.WorkItems.Remove(workItem); await _context.SaveChangesAsync(); return true; } }