using Marco.Pms.DataAccess.Data; using Marco.Pms.Model.Directory; using Marco.Pms.Model.Dtos.Master; using Marco.Pms.Model.Employees; using Marco.Pms.Model.Entitlements; using Marco.Pms.Model.Mapper; using Marco.Pms.Model.Master; using Marco.Pms.Model.Utilities; using Marco.Pms.Model.ViewModels.Master; using Marco.Pms.Services.Service; using MarcoBMS.Services.Helpers; using MarcoBMS.Services.Service; using Microsoft.EntityFrameworkCore; namespace Marco.Pms.Services.Helpers { public class MasterHelper { private readonly ApplicationDbContext _context; private readonly ILoggingService _logger; private readonly UserHelper _userHelper; private readonly PermissionServices _permissionService; public MasterHelper(ApplicationDbContext context, ILoggingService logger, UserHelper userHelper, PermissionServices permissionServices) { _context = context; _logger = logger; _userHelper = userHelper; _permissionService = permissionServices; } // -------------------------------- Contact Category -------------------------------- public async Task> CreateContactCategory(CreateContactCategoryDto contactCategoryDto) { Guid tenantId = _userHelper.GetTenantId(); var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); if (contactCategoryDto != null) { ContactCategoryMaster? existingContactCategory = await _context.ContactCategoryMasters.FirstOrDefaultAsync(c => c.TenantId == tenantId && c.Name.ToLower() == (contactCategoryDto.Name != null ? contactCategoryDto.Name.ToLower() : "")); if (existingContactCategory == null) { ContactCategoryMaster contactCategory = contactCategoryDto.ToContactCategoryMasterFromCreateContactCategoryDto(tenantId); _context.ContactCategoryMasters.Add(contactCategory); await _context.SaveChangesAsync(); ContactCategoryVM categoryVM = contactCategory.ToContactCategoryVMFromContactCategoryMaster(); _logger.LogInfo("Employee ID {LoggedInEmployeeId} created a contact category {ContactCategoryId}.", LoggedInEmployee.Id, contactCategory.Id); return ApiResponse.SuccessResponse(categoryVM, "Category Created Successfully", 200); } _logger.LogWarning("Employee ID {LoggedInEmployeeId} attempted to create an existing contact category.", LoggedInEmployee.Id); return ApiResponse.ErrorResponse("Category already existed", "Category already existed", 409); } _logger.LogWarning("Employee with ID {LoggedInEmployeeId} sended empty payload", LoggedInEmployee.Id); return ApiResponse.ErrorResponse("User Send empty Payload", "User Send empty Payload", 400); } public async Task> UpdateContactCategory(Guid id, UpdateContactCategoryDto contactCategoryDto) { Guid tenantId = _userHelper.GetTenantId(); var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); if (contactCategoryDto != null && id == contactCategoryDto.Id) { ContactCategoryMaster? contactCategory = await _context.ContactCategoryMasters.FirstOrDefaultAsync(c => c.TenantId == tenantId && c.Id == id); if (contactCategory != null) { contactCategory.Name = contactCategoryDto.Name ?? ""; contactCategory.Description = contactCategoryDto.Description ?? ""; _context.DirectoryUpdateLogs.Add(new DirectoryUpdateLog { RefereanceId = contactCategory.Id, UpdatedById = LoggedInEmployee.Id, UpdateAt = DateTime.UtcNow }); await _context.SaveChangesAsync(); ContactCategoryVM categoryVM = contactCategory.ToContactCategoryVMFromContactCategoryMaster(); _logger.LogInfo("Employee ID {LoggedInEmployeeId} created a contact category {ContactCategoryId}.", LoggedInEmployee.Id, contactCategory.Id); return ApiResponse.SuccessResponse(categoryVM, "Category Created Successfully", 200); } _logger.LogWarning("Employee ID {LoggedInEmployeeId} attempted to update a contact category but not found in database.", LoggedInEmployee.Id); return ApiResponse.ErrorResponse("Category not found", "Category not found", 404); } _logger.LogWarning("Employee with ID {LoggedInEmployeeId} sended empty payload", LoggedInEmployee.Id); return ApiResponse.ErrorResponse("User Send empty Payload", "User Send empty Payload", 400); } public async Task> GetContactCategoriesList() { Guid tenantId = _userHelper.GetTenantId(); var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); var categoryList = await _context.ContactCategoryMasters.Where(c => c.TenantId == tenantId).ToListAsync(); List contactCategories = new List(); foreach (var category in categoryList) { ContactCategoryVM categoryVM = category.ToContactCategoryVMFromContactCategoryMaster(); contactCategories.Add(categoryVM); } _logger.LogInfo("{count} contact categoires are fetched by Employee with ID {LoggedInEmployeeId}", contactCategories.Count, LoggedInEmployee.Id); return ApiResponse.SuccessResponse(contactCategories, System.String.Format("{0} contact categories fetched successfully", contactCategories.Count), 200); } public async Task> GetContactCategoryById(Guid id) { Guid tenantId = _userHelper.GetTenantId(); var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); var category = await _context.ContactCategoryMasters.FirstOrDefaultAsync(c => c.Id == id && c.TenantId == tenantId); if (category != null) { ContactCategoryVM categoryVM = category.ToContactCategoryVMFromContactCategoryMaster(); _logger.LogInfo("Employee {EmployeeId} fetched contact category {ContactCategoryID}", LoggedInEmployee.Id, category.Id); return ApiResponse.SuccessResponse(categoryVM, "Category fetched successfully", 200); } _logger.LogWarning("Employee {EmployeeId} attempted to fetch contact category {ContactCategoryID} but not found in database", LoggedInEmployee.Id, id); return ApiResponse.ErrorResponse("Category not found", "Category not found", 404); } public async Task> DeleteContactCategory(Guid id) { Guid tenantId = _userHelper.GetTenantId(); var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); ContactCategoryMaster? contactCategory = await _context.ContactCategoryMasters.FirstOrDefaultAsync(c => c.Id == id && c.TenantId == tenantId); if (contactCategory != null) { List? existingContacts = await _context.Contacts.AsNoTracking().Where(c => c.ContactCategoryId == contactCategory.Id).ToListAsync(); if (existingContacts.Count > 0) { List? contacts = new List(); foreach (var contact in existingContacts) { contact.ContactCategoryId = null; contacts.Add(contact); } _context.Contacts.UpdateRange(contacts); } _context.ContactCategoryMasters.Remove(contactCategory); _context.DirectoryUpdateLogs.Add(new DirectoryUpdateLog { RefereanceId = id, UpdatedById = LoggedInEmployee.Id, UpdateAt = DateTime.UtcNow }); await _context.SaveChangesAsync(); _logger.LogInfo("Employee {EmployeeId} deleted contact category {ContactCategoryId}", LoggedInEmployee.Id, id); } _logger.LogWarning("Employee {EmployeeId} tries to delete Category {CategoryId} but not found in database", LoggedInEmployee.Id, id); return ApiResponse.SuccessResponse(new { }, "Category deleted successfully", 200); } // -------------------------------- Contact Tag -------------------------------- public async Task> GetContactTags() { Guid tenantId = _userHelper.GetTenantId(); var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); var taglist = await _context.ContactTagMasters.Where(t => t.TenantId == tenantId).ToListAsync(); List contactTags = new List(); foreach (var tag in taglist) { ContactTagVM tagVm = tag.ToContactTagVMFromContactTagMaster(); contactTags.Add(tagVm); } _logger.LogInfo("{count} contact Tags are fetched by Employee with ID {LoggedInEmployeeId}", contactTags.Count, LoggedInEmployee.Id); return ApiResponse.SuccessResponse(contactTags, System.String.Format("{0} contact tags fetched successfully", contactTags.Count), 200); } public async Task> CreateContactTag(CreateContactTagDto contactTagDto) { Guid tenantId = _userHelper.GetTenantId(); var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); if (contactTagDto != null) { ContactTagMaster? existingContactTag = await _context.ContactTagMasters.FirstOrDefaultAsync(c => c.TenantId == tenantId && c.Name.ToLower() == (contactTagDto.Name != null ? contactTagDto.Name.ToLower() : "")); if (existingContactTag == null) { ContactTagMaster contactTag = contactTagDto.ToContactTagMasterFromCreateContactTagDto(tenantId); _context.ContactTagMasters.Add(contactTag); await _context.SaveChangesAsync(); ContactTagVM tagVM = contactTag.ToContactTagVMFromContactTagMaster(); _logger.LogInfo("Employee ID {LoggedInEmployeeId} created a contact tag {ContactTagId}.", LoggedInEmployee.Id, contactTag.Id); return ApiResponse.SuccessResponse(tagVM, "Tag Created Successfully", 200); } _logger.LogWarning("Employee ID {LoggedInEmployeeId} attempted to create an existing contact tag.", LoggedInEmployee.Id); return ApiResponse.ErrorResponse("Tag already existed", "Tag already existed", 409); } _logger.LogWarning("Employee with ID {LoggedInEmployeeId} sended empty payload", LoggedInEmployee.Id); return ApiResponse.ErrorResponse("User Send empty Payload", "User Send empty Payload", 400); } public async Task> UpdateContactTag(Guid id, UpdateContactTagDto contactTagDto) { var tenantId = _userHelper.GetTenantId(); Employee LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); if (contactTagDto != null && contactTagDto.Id == id) { ContactTagMaster? contactTag = await _context.ContactTagMasters.AsNoTracking().FirstOrDefaultAsync(t => t.TenantId == tenantId && t.Id == contactTagDto.Id); if (contactTag != null) { contactTag = contactTagDto.ToContactTagMasterFromUpdateContactTagDto(tenantId); _context.ContactTagMasters.Update(contactTag); _context.DirectoryUpdateLogs.Add(new DirectoryUpdateLog { RefereanceId = contactTag.Id, UpdatedById = LoggedInEmployee.Id, UpdateAt = DateTime.UtcNow }); await _context.SaveChangesAsync(); ContactTagVM contactTagVm = contactTag.ToContactTagVMFromContactTagMaster(); _logger.LogInfo("Contact tag master {ConatctTagId} updated successfully by employee {EmployeeId}", contactTagVm.Id, LoggedInEmployee.Id); return ApiResponse.SuccessResponse(contactTagVm, "Contact Tag master updated successfully", 200); } _logger.LogWarning("Contact Tag master {ContactTagId} not found in database", id); return ApiResponse.ErrorResponse("Contact Tag master not found", "Contact tag master not found", 404); } _logger.LogWarning("Employee with ID {LoggedInEmployeeId} sended empty payload", LoggedInEmployee.Id); return ApiResponse.ErrorResponse("User Send empty Payload", "User Send empty Payload", 400); } public async Task> DeleteContactTag(Guid id) { Guid tenantId = _userHelper.GetTenantId(); var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); ContactTagMaster? contactTag = await _context.ContactTagMasters.FirstOrDefaultAsync(c => c.Id == id && c.TenantId == tenantId); if (contactTag != null) { List? tagMappings = await _context.ContactTagMappings.Where(t => t.ContactTagId == contactTag.Id).ToListAsync(); _context.ContactTagMasters.Remove(contactTag); if (tagMappings.Any()) { _context.ContactTagMappings.RemoveRange(tagMappings); } _context.DirectoryUpdateLogs.Add(new DirectoryUpdateLog { RefereanceId = id, UpdatedById = LoggedInEmployee.Id, UpdateAt = DateTime.UtcNow }); await _context.SaveChangesAsync(); _logger.LogInfo("Employee {EmployeeId} deleted contact tag {ContactTagId}", LoggedInEmployee.Id, id); } _logger.LogWarning("Employee {EmployeeId} tries to delete Tag {ContactTagId} but not found in database", LoggedInEmployee.Id, id); return ApiResponse.SuccessResponse(new { }, "Tag deleted successfully", 200); } // -------------------------------- Work Status -------------------------------- public async Task> GetWorkStatusList() { _logger.LogInfo("GetWorkStatusList called."); try { // Step 1: Get tenant and logged-in employee info Guid tenantId = _userHelper.GetTenantId(); var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); // Step 2: Check permission to view master data bool hasViewPermission = await _permissionService.HasPermission(PermissionsMaster.ViewMasters, loggedInEmployee.Id); if (!hasViewPermission) { _logger.LogWarning("Access denied for employeeId: {EmployeeId}", loggedInEmployee.Id); return ApiResponse.ErrorResponse("You don't have access", "Don't have access to take action", 403); } // Step 3: Fetch work statuses for the tenant var workStatusList = await _context.WorkStatusMasters .Where(ws => ws.TenantId == tenantId) .Select(ws => new { ws.Id, ws.Name, ws.Description, ws.IsSystem }) .ToListAsync(); _logger.LogInfo("{Count} work statuses fetched for tenantId: {TenantId}", workStatusList.Count, tenantId); // Step 4: Return successful response return ApiResponse.SuccessResponse( workStatusList, $"{workStatusList.Count} work status records fetched successfully", 200 ); } catch (Exception ex) { _logger.LogWarning("Error occurred while fetching work status list : {Error}", ex.Message); return ApiResponse.ErrorResponse("An error occurred", "Unable to fetch work status list", 500); } } public async Task> CreateWorkStatus(CreateWorkStatusMasterDto createWorkStatusDto) { _logger.LogInfo("CreateWorkStatus called with Name: {Name}", createWorkStatusDto.Name ?? ""); try { // Step 1: Get tenant and logged-in employee Guid tenantId = _userHelper.GetTenantId(); var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); // Step 2: Check if user has permission to manage master data var hasManageMasterPermission = await _permissionService.HasPermission(PermissionsMaster.ManageMasters, loggedInEmployee.Id); if (!hasManageMasterPermission) { _logger.LogWarning("Access denied for employeeId: {EmployeeId}", loggedInEmployee.Id); return ApiResponse.ErrorResponse("You don't have access", "Don't have access to take action", 403); } // Step 3: Check if work status with the same name already exists var existingWorkStatus = await _context.WorkStatusMasters .FirstOrDefaultAsync(ws => ws.Name == createWorkStatusDto.Name && ws.TenantId == tenantId); if (existingWorkStatus != null) { _logger.LogWarning("Work status already exists: {Name}", createWorkStatusDto.Name ?? ""); return ApiResponse.ErrorResponse("Work status already exists", "Work status already exists", 400); } // Step 4: Create new WorkStatusMaster entry var workStatus = new WorkStatusMaster { Name = createWorkStatusDto.Name?.Trim() ?? "", Description = createWorkStatusDto.Description?.Trim() ?? "", IsSystem = false, TenantId = tenantId }; _context.WorkStatusMasters.Add(workStatus); await _context.SaveChangesAsync(); _logger.LogInfo("Work status created successfully: {Id}, Name: {Name}", workStatus.Id, workStatus.Name); return ApiResponse.SuccessResponse(workStatus, "Work status created successfully", 200); } catch (Exception ex) { _logger.LogWarning("Error occurred while creating work status : {Error}", ex.Message); return ApiResponse.ErrorResponse("An error occurred", "Unable to create work status", 500); } } public async Task> UpdateWorkStatus(Guid id, UpdateWorkStatusMasterDto updateWorkStatusDto) { _logger.LogInfo("UpdateWorkStatus called for WorkStatus ID: {Id}, New Name: {Name}", id, updateWorkStatusDto.Name ?? ""); try { // Step 1: Validate input if (id == Guid.Empty || id != updateWorkStatusDto.Id) { _logger.LogWarning("Invalid ID provided for update. Route ID: {RouteId}, DTO ID: {DtoId}", id, updateWorkStatusDto.Id); return ApiResponse.ErrorResponse("Invalid data provided", "The provided work status ID is invalid", 400); } // Step 2: Get tenant and logged-in employee Guid tenantId = _userHelper.GetTenantId(); var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); // Step 3: Check permissions var hasManageMasterPermission = await _permissionService.HasPermission(PermissionsMaster.ManageMasters, loggedInEmployee.Id); if (!hasManageMasterPermission) { _logger.LogWarning("Access denied. EmployeeId: {EmployeeId} does not have Manage Master permission.", loggedInEmployee.Id); return ApiResponse.ErrorResponse("Access denied", "You do not have permission to update this work status", 403); } // Step 4: Retrieve the work status record var workStatus = await _context.WorkStatusMasters .FirstOrDefaultAsync(ws => ws.Id == id && ws.TenantId == tenantId); if (workStatus == null) { _logger.LogWarning("Work status not found for ID: {Id}", id); return ApiResponse.ErrorResponse("Work status not found", "No work status found with the provided ID", 404); } // Step 5: Check for duplicate name (optional) var isDuplicate = await _context.WorkStatusMasters .AnyAsync(ws => ws.Name == updateWorkStatusDto.Name && ws.Id != id && ws.TenantId == tenantId); if (isDuplicate) { _logger.LogWarning("Duplicate work status name '{Name}' detected during update. ID: {Id}", updateWorkStatusDto.Name ?? "", id); return ApiResponse.ErrorResponse("Work status with the same name already exists", "Duplicate name", 400); } // Step 6: Update fields workStatus.Name = updateWorkStatusDto.Name?.Trim() ?? ""; workStatus.Description = updateWorkStatusDto.Description?.Trim() ?? ""; await _context.SaveChangesAsync(); _logger.LogInfo("Work status updated successfully. ID: {Id}", id); return ApiResponse.SuccessResponse(workStatus, "Work status updated successfully", 200); } catch (Exception ex) { _logger.LogError(ex, "Error occurred while updating work status ID: {Id}", id); return ApiResponse.ErrorResponse("An error occurred", "Unable to update the work status at this time", 500); } } public async Task> DeleteWorkStatus(Guid id) { _logger.LogInfo("DeleteWorkStatus called for Id: {Id}", id); try { // Step 1: Get current tenant and logged-in employee Guid tenantId = _userHelper.GetTenantId(); var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); // Step 2: Check permission to manage master data var hasManageMasterPermission = await _permissionService.HasPermission(PermissionsMaster.ManageMasters, loggedInEmployee.Id); if (!hasManageMasterPermission) { _logger.LogWarning("Delete denied. EmployeeId: {EmployeeId} lacks Manage_Master permission.", loggedInEmployee.Id); return ApiResponse.ErrorResponse("You don't have access", "Access denied for deleting work status", 403); } // Step 3: Find the work status var workStatus = await _context.WorkStatusMasters .FirstOrDefaultAsync(ws => ws.Id == id && ws.TenantId == tenantId); if (workStatus == null) { _logger.LogWarning("Work status not found for Id: {Id}", id); return ApiResponse.ErrorResponse("Work status not found", "Work status not found", 404); } // Step 4: Check for dependencies in TaskAllocations bool hasDependency = await _context.TaskAllocations .AnyAsync(ta => ta.TenantId == tenantId && ta.WorkStatusId == id); if (hasDependency) { _logger.LogWarning("Cannot delete WorkStatus Id: {Id} due to existing task dependency", id); return ApiResponse.ErrorResponse( "Work status has a dependency in assigned tasks and cannot be deleted", "Deletion failed due to associated tasks", 400 ); } // Step 5: Delete and persist _context.WorkStatusMasters.Remove(workStatus); await _context.SaveChangesAsync(); _logger.LogInfo("Work status deleted successfully. Id: {Id}", id); return ApiResponse.SuccessResponse(new { }, "Work status deleted successfully", 200); } catch (Exception ex) { _logger.LogError(ex, "Error occurred while deleting WorkStatus Id: {Id}", id); return ApiResponse.ErrorResponse("An error occurred", "Unable to delete work status", 500); } } } }