using AutoMapper; using Marco.Pms.DataAccess.Data; using Marco.Pms.Model.Dtos.ServiceProject; using Marco.Pms.Model.Employees; using Marco.Pms.Model.Entitlements; using Marco.Pms.Model.ServiceProject; using Marco.Pms.Model.Utilities; using Marco.Pms.Model.ViewModels.Activities; using Marco.Pms.Model.ViewModels.Expanses; using Marco.Pms.Model.ViewModels.Master; using Marco.Pms.Model.ViewModels.Projects; using Marco.Pms.Model.ViewModels.ServiceProject; using Marco.Pms.Services.Helpers; using Marco.Pms.Services.Service.ServiceInterfaces; using MarcoBMS.Services.Service; using Microsoft.EntityFrameworkCore; namespace Marco.Pms.Services.Service { public class ServiceProjectService : IServiceProject { private readonly IDbContextFactory _dbContextFactory; private readonly IServiceScopeFactory _serviceScopeFactory; private readonly ApplicationDbContext _context; // Keeping this for direct scoped context use where appropriate private readonly ILoggingService _logger; private readonly CacheUpdateHelper _cache; private readonly IMapper _mapper; public ServiceProjectService(IDbContextFactory dbContextFactory,IServiceScopeFactory serviceScopeFactory, ApplicationDbContext context, ILoggingService logger, CacheUpdateHelper cache, IMapper mapper) { _serviceScopeFactory = serviceScopeFactory; _context = context; _logger = logger; _cache = cache; _mapper = mapper; _dbContextFactory = dbContextFactory; } public async Task> CreateServiceProject(ServiceProjectDto model, Guid tenantId, Employee loggedInEmployee) { using var scope = _serviceScopeFactory.CreateScope(); var _firebase = scope.ServiceProvider.GetService(); var tenant = await _context.Tenants.FirstOrDefaultAsync(t => t.Id == tenantId && t.OrganizationId == loggedInEmployee.OrganizationId); if (tenant == null) { _logger.LogWarning("Access DENIED (OrgId:{OrgId}) by Employee {EmployeeId} for tenantId={TenantId}.", loggedInEmployee.OrganizationId, loggedInEmployee.Id, tenantId); return ApiResponse.ErrorResponse("Access Denied", "You do not have permission to create a service project for this tenant.", 403); } var serviceProject = _mapper.Map(model); serviceProject.Id = Guid.NewGuid(); serviceProject.CreatedById = loggedInEmployee.Id; serviceProject.CreatedAt = DateTime.UtcNow; serviceProject.TenantId = tenantId; serviceProject.IsActive = true; var projectServiceMapping = model.ServiceIds.Select(serviceId => new ServiceProjectServiceMapping { ServiceId = serviceId, ProjectId = serviceProject.Id, TenantId = tenantId }); try { _context.ServiceProjects.Add(serviceProject); _context.ServiceProjectServiceMapping.AddRange(projectServiceMapping); await _context.SaveChangesAsync(); _logger.LogInfo("Service Project {ProjectId} created successfully for TenantId={TenantId}, by Employee {EmployeeId}.", serviceProject.Id, tenantId, loggedInEmployee); var serviceProjectVm = _mapper.Map(serviceProject); var services = await _context.ServiceMasters.Where(t => model.ServiceIds.Contains(t.Id) && t.TenantId == tenantId && t.IsActive).ToListAsync(); serviceProjectVm.Services = _mapper.Map>(services); serviceProjectVm.CreatedBy = _mapper.Map(loggedInEmployee); serviceProjectVm.CreatedAt = DateTime.UtcNow; return ApiResponse.SuccessResponse(serviceProjectVm, "An Successfullly occurred while saving the project.", 201); } catch (Exception ex) { _logger.LogError(ex, "DB Failure: Service Project creation failed for TenantId={TenantId}. Rolling back.", tenantId); return ApiResponse.ErrorResponse("An error occurred while saving the project.", ex.Message, 500); } } public async Task> GetServiceProjectList(Guid tenantId, Employee loggedInEmployee) { try { var serviceProjects = await _context.ServiceProjects.Include(sp=>sp.CreatedBy).Where(sp=>sp.TenantId == tenantId && sp.IsActive).ToListAsync(); List serviceProjectLists = _mapper.Map>(serviceProjects); _logger.LogInfo("Successfully retrieved a total of {ProjectCount} projects.", serviceProjectLists.Count); return ApiResponse.SuccessResponse(serviceProjectLists, "Projects retrieved successfully.", 200); } catch (Exception ex) { // --- Step 5: Graceful Error Handling --- _logger.LogError(ex, "An unexpected error occurred in GetAllProjects for tenant {TenantId}.", tenantId); return ApiResponse.ErrorResponse("An internal server error occurred. Please try again later.", null, 500); } } public async Task> UpdateServiceProject(Guid id, ServiceProjectDto model, Guid tenantId, Employee loggedInEmployee) { try { var serviceProject = await _context.ServiceProjects.Where(sp => sp.Id == id && sp.TenantId == tenantId).FirstOrDefaultAsync(); if(serviceProject == null) { _logger.LogWarning("Attempt to update non-existent Service project with ID {ProjectId} by user {UserId}.", id, loggedInEmployee.Id); return ApiResponse.ErrorResponse("Project not found.", $"No project found with ID {id}.", 404); } _mapper.Map(model, serviceProject); serviceProject.UpdatedAt = DateTime.UtcNow; serviceProject.UpdatedById = loggedInEmployee.Id; await _context.SaveChangesAsync(); serviceProject = await _context.ServiceProjects.Include(sp => sp.UpdatedBy).Where(sp => sp.Id == id && sp.TenantId == tenantId).FirstOrDefaultAsync(); var services = await _context.ServiceProjectServiceMapping.Include(t => t.Service).Where(t => t.ProjectId == serviceProject!.Id && t.Service != null && t.TenantId == tenantId).Select(t => t.Service!).ToListAsync(); ServiceProjectVm serviceProjectVm = _mapper.Map(serviceProject); serviceProjectVm.Services = _mapper.Map>(services); return ApiResponse.SuccessResponse(serviceProjectVm, "Service Project updated successfully.", 200); } catch (Exception ex) { _logger.LogError(ex, "An unexpected error occurred in Updating Service Project for tenant {TenantId}.", tenantId); return ApiResponse.ErrorResponse("An internal server error occurred. Please try again later.", null, 500); } } } }