using AutoMapper; using Marco.Pms.DataAccess.Data; using Marco.Pms.Model.Dtos.Organization; using Marco.Pms.Model.Employees; using Marco.Pms.Model.Entitlements; using Marco.Pms.Model.OrganizationModel; using Marco.Pms.Model.Utilities; using Marco.Pms.Model.ViewModels.Organization; using Marco.Pms.Services.Service; using MarcoBMS.Services.Helpers; using MarcoBMS.Services.Service; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using System.Net; namespace Marco.Pms.Services.Controllers { [Route("api/[controller]")] [ApiController] public class OrganizationController : ControllerBase { private readonly IDbContextFactory _dbContextFactory; private readonly IServiceScopeFactory _serviceScope; private readonly UserHelper _userHelper; private readonly Guid tenantId; private readonly IMapper _mapper; private readonly ILoggingService _logger; private static readonly Guid PMCProvider = Guid.Parse("b1877a3b-8832-47b1-bbe3-dc7e98672f49"); public OrganizationController(IDbContextFactory dbContextFactory, IServiceScopeFactory serviceScope, UserHelper userHelper, ILoggingService logger, IMapper mapper) { _dbContextFactory = dbContextFactory ?? throw new ArgumentNullException(nameof(dbContextFactory)); _serviceScope = serviceScope ?? throw new ArgumentNullException(nameof(serviceScope)); _userHelper = userHelper ?? throw new ArgumentNullException(nameof(userHelper)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _mapper = mapper ?? throw new ArgumentNullException(nameof(mapper)); tenantId = userHelper.GetTenantId(); } #region =================================================================== Get Functions =================================================================== //[HttpGet("list")] //public async Task GetListServiceProviderAsync([FromQuery] string? searchString, [FromQuery] string? filter, [FromQuery] bool active = true, // [FromQuery] int pageNumber = 1, [FromQuery] int pageSize = 1) //{ // await using var _context = await _dbContextFactory.CreateDbContextAsync(); // using var scope = _serviceScope.CreateScope(); // var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); // var serviceProviderQuery = _context.ServiceProviders // .Include(sp => sp.ProviderType) // .Where(sp => sp.TenantId == tenantId && sp.IsActive == active); // var filterObject = TryDeserializeServicesProviderFilter(filter); // if (filterObject != null) // { // if (filterObject.ProviderTypeIds?.Any() ?? false) // { // serviceProviderQuery = serviceProviderQuery.Where(sp => filterObject.ProviderTypeIds.Contains(sp.ProviderTypeId)); // } // } // if (!string.IsNullOrWhiteSpace(searchString)) // { // if (!(Int32.TryParse(searchString, out int num))) // { // num = 0; // } // serviceProviderQuery = serviceProviderQuery.Where(sp => // sp.ProviderName.Contains(searchString) || // sp.ContactPerson.Contains(searchString) || // sp.Address.Contains(searchString) || // sp.Email.Contains(searchString) || // sp.PhoneNumber.Contains(searchString) || // sp.SPRID == num); // } // var serviceProviders = await serviceProviderQuery // .OrderBy(e => e.ProviderName) // .Skip((pageNumber - 1) * pageSize) // .Take(pageSize) // .ToListAsync(); // var response = _mapper.Map>(serviceProviders); // return Ok(ApiResponse.SuccessResponse(response, "Successfully fetched the Service Provider list", 200)); //} #endregion #region =================================================================== Post Functions =================================================================== [HttpPost("create")] public async Task CreateServiceProviderAsync([FromBody] CreateOrganizationDto model) { await using var _context = await _dbContextFactory.CreateDbContextAsync(); using var scope = _serviceScope.CreateScope(); var _permission = scope.ServiceProvider.GetRequiredService(); var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); var hasPermission = await _permission.HasPermission(PermissionsMaster.AddOrganization, loggedInEmployee.Id); if (!hasPermission) { return StatusCode(403, ApiResponse.ErrorResponse("Access Denied", "You do not have permission to create new service provider.", 403)); } var lastOrganization = await _context.Organizations.OrderByDescending(sp => sp.SPRID).FirstOrDefaultAsync(); var lastSPRID = lastOrganization != null ? lastOrganization.SPRID : 5400; Organization organization = _mapper.Map(model); organization.SPRID = lastSPRID + 1; organization.CreatedAt = DateTime.UtcNow; organization.IsActive = true; _context.Organizations.Add(organization); await _context.SaveChangesAsync(); var user = new ApplicationUser { UserName = model.Email, Email = model.Email, EmailConfirmed = true }; var _configuration = scope.ServiceProvider.GetRequiredService(); var _emailSender = scope.ServiceProvider.GetRequiredService(); var _userManager = scope.ServiceProvider.GetRequiredService>(); // Create Identity User var result = await _userManager.CreateAsync(user, "User@123"); if (!result.Succeeded) return BadRequest(ApiResponse.ErrorResponse("Failed to create user", result.Errors, 400)); var jobRole = await _context.ApplicationRoles.FirstOrDefaultAsync(ar => ar.Role == "Admin" && ar.TenantId == tenantId); if (jobRole == null) jobRole = await _context.ApplicationRoles.FirstOrDefaultAsync(ar => ar.TenantId == tenantId); var fullName = model.ContactPerson.Split(" "); Employee newEmployee = new Employee { FirstName = fullName[0], LastName = fullName[fullName.Length - 1], Email = model.Email, PermanentAddress = model.Address, CurrentAddress = model.Address, PhoneNumber = model.ContactNumber, ApplicationUserId = user.Id, JobRoleId = jobRole?.Id ?? Guid.Empty, IsActive = true, IsSystem = false, //IsPrimary = true, //OrganizationId = organization.Id, TenantId = tenantId }; _context.Employees.Add(newEmployee); await _context.SaveChangesAsync(); /* SEND USER REGISTRATION MAIL*/ var token = await _userManager.GeneratePasswordResetTokenAsync(user); var resetLink = $"{_configuration["AppSettings:WebFrontendUrl"]}/reset-password?token={WebUtility.UrlEncode(token)}"; if (newEmployee.FirstName != null) { await _emailSender.SendResetPasswordEmailOnRegister(user.Email, newEmployee.FirstName, resetLink); } var response = _mapper.Map(organization); return Ok(ApiResponse.SuccessResponse(response, "Successfully created the service provider", 200)); } //[HttpPost("assign/project")] //public async Task AssignServiceProviderToProjectAsync([FromBody] AssignOrganizationDto model) //{ // await using var _context = await _dbContextFactory.CreateDbContextAsync(); // using var scope = _serviceScope.CreateScope(); // var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); // var todaysDate = DateTime.UtcNow.Date; // var serviceTask = Task.Run(async () => // { // await using var context = await _dbContextFactory.CreateDbContextAsync(); // //return await context.ServiceMasters.FirstOrDefaultAsync(sp => sp.Id == model.ServiceId); // }); // var projectServiceTask = Task.Run(async () => // { // await using var context = await _dbContextFactory.CreateDbContextAsync(); // return await context.ProjectServiceMappings // .FirstOrDefaultAsync(sp => sp.ServiceId == model.ServiceId && sp.ProjectId == model.ProjectId && sp.PlannedEndDate.Date <= todaysDate // && sp.PlannedStartDate.Date >= todaysDate && sp.ActualStartDate.Date >= todaysDate); // }); // var serviceProviderTask = Task.Run(async () => // { // await using var context = await _dbContextFactory.CreateDbContextAsync(); // return await context.ServiceProviders.FirstOrDefaultAsync(sp => sp.Id == model.ServiceProviderId); // }); // var projectTask = Task.Run(async () => // { // await using var context = await _dbContextFactory.CreateDbContextAsync(); // return await context.Projects.FirstOrDefaultAsync(sp => sp.Id == model.ProjectId); // }); // await Task.WhenAll(serviceProviderTask, projectTask, serviceTask, projectServiceTask); // var serviceProvider = serviceProviderTask.Result; // var projectService = projectServiceTask.Result; // //var service = serviceTask.Result; // var project = projectTask.Result; // //if (service == null) // //{ // // return NotFound(ApiResponse.ErrorResponse("Service not found", "Service not found in database", 404)); // //} // if (serviceProvider == null) // { // return NotFound(ApiResponse.ErrorResponse("Service Provider not found", "Service Provider not found in database", 404)); // } // if (project == null) // { // return NotFound(ApiResponse.ErrorResponse("Project not found", "Project not found in database", 404)); // } // if (projectService == null) // { // return NotFound(ApiResponse.ErrorResponse("Project Service not found", "Project Service not found in database", 404)); // } // var serviceProviderTenantMapping = await _context.ServiceProviderTenantMappings // .FirstOrDefaultAsync(spt => spt.ServiceProviderId == model.ServiceProviderId && spt.TenantId == project.TenantId); // if (serviceProviderTenantMapping == null) // { // var newServiceProviderTenantMapping = new OrganizationTenantMapping // { // ServiceProviderId = serviceProvider.Id, // SPRID = serviceProvider.SPRID, // IsActive = true, // TenantId = project.TenantId // }; // _context.ServiceProviderTenantMappings.Add(newServiceProviderTenantMapping); // } // var projectServiceProviderMapping = new ProjectOrganizationMapping // { // ProjectServiceId = projectService.Id, // ServiceProviderId = model.ServiceProviderId, // AssignedDate = model.AssignedDate, // CompletionDate = model.CompletionDate, // TenantId = project.TenantId // }; // _context.ProjectServiceProviderMappings.Add(projectServiceProviderMapping); // await _context.SaveChangesAsync(); // var response = _mapper.Map(serviceProvider); // return Ok(ApiResponse.SuccessResponse(response, "Service Provider successfully assigned to the project", 200)); //} #endregion #region =================================================================== Put Functions =================================================================== //[HttpPut("edit/{id}")] //public async Task UpdateServiceProviderAsync(Guid id, [FromBody] UpdateOrganizationDto model) //{ // await using var _context = await _dbContextFactory.CreateDbContextAsync(); // using var scope = _serviceScope.CreateScope(); // var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); // var clientTask = Task.Run(async () => // { // await using var context = await _dbContextFactory.CreateDbContextAsync(); // return await context.Clients.FirstOrDefaultAsync(c => c.PrimaryEmployeeId == loggedInEmployee.Id && c.TenantId == tenantId); // }); // var employeeTask = Task.Run(async () => // { // await using var context = await _dbContextFactory.CreateDbContextAsync(); // return await context.Employees.AsNoTracking().FirstOrDefaultAsync(e => e.ServiceProviderId == id && e.IsPrimary); // }); // await Task.WhenAll(clientTask, employeeTask); // var client = clientTask.Result; // var employee = employeeTask.Result; // if (employee == null) // { // return NotFound(ApiResponse.ErrorResponse("Primary employee for service provider not found", "Primary employee for service provider not found in database", 400)); // } // if (!(loggedInEmployee.ApplicationUser?.IsRootUser ?? false) && !loggedInEmployee.IsPrimary && client == null && employee.Id == loggedInEmployee.Id) // { // return StatusCode(403, ApiResponse.ErrorResponse("Access Denied", "You do not have permission to create new service provider.", 403)); // } // if (model.Id == id) // { // return BadRequest(ApiResponse.ErrorResponse("Invalid Input", "Id from path parameters do not matches with id from model", 400)); // } // var serviceProvider = await _context.ServiceProviders.FirstOrDefaultAsync(sp => sp.Id == model.Id); // if (serviceProvider == null) // { // return NotFound(ApiResponse.ErrorResponse("Service Provider not found", "Service Provider not found in database", 404)); // } // _mapper.Map(model, serviceProvider); // _context.ServiceProviders.Update(serviceProvider); // var fullName = model.ContactPerson.Split(" "); // employee.FirstName = fullName[0]; // employee.LastName = fullName[fullName.Length - 1]; // employee.CurrentAddress = model.Address; // employee.PermanentAddress = model.Address; // employee.PhoneNumber = model.PhoneNumber; // _context.Employees.Update(employee); // await _context.SaveChangesAsync(); // var response = _mapper.Map(serviceProvider); // return Ok(ApiResponse.SuccessResponse(response, "Successfully updated the service provider", 200)); //} #endregion #region =================================================================== Delete Functions =================================================================== //[HttpDelete("delete/{id}")] //public async Task DeleteServiceProviderAsync(Guid id, [FromQuery] bool active) //{ // await using var _context = await _dbContextFactory.CreateDbContextAsync(); // using var scope = _serviceScope.CreateScope(); // var message = active ? "Restore" : "Delete"; // var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); // var client = await _context.Clients.FirstOrDefaultAsync(c => c.PrimaryEmployeeId == loggedInEmployee.Id && c.TenantId == tenantId); // if (!(loggedInEmployee.ApplicationUser?.IsRootUser ?? false) && !loggedInEmployee.IsPrimary && client == null) // { // return StatusCode(403, ApiResponse.ErrorResponse("Access Denied", $"You do not have permission to {message}d service provider.", 403)); // } // var serviceProvider = await _context.ServiceProviders.FirstOrDefaultAsync(sp => sp.Id == id); // if (serviceProvider == null) // { // return NotFound(ApiResponse.ErrorResponse("Service Provider not Found", "Service Provider not Found in database", 404)); // } // if (serviceProvider.IsActive == active) // { // return BadRequest(ApiResponse.ErrorResponse($"Service Provider is already {message}d", $"Service Provider is already {message}d", 400)); // } // var employeeIds = await _context.Employees.Where(e => e.ServiceProviderId == id).Select(e => e.Id).ToListAsync(); // var isPendingTask = await _context.TaskMembers.AnyAsync(tm => employeeIds.Contains(tm.EmployeeId)); // if (isPendingTask && !active) // { // return BadRequest(ApiResponse.ErrorResponse("There is an unfinshed task, Service provider cannot be deleted", "There is an unfinshed task, Service provider cannot be deleted", 400)); // } // serviceProvider.IsActive = active; // if (!active) // { // var servicePeroviderTenant = await _context.ServiceProviderTenantMappings.AsNoTracking().Where(spt => spt.ServiceProviderId == id && spt.IsActive).ToListAsync(); // var newServiceProviderTenant = servicePeroviderTenant.Select(spt => // { // spt.IsActive = false; // return spt; // }).ToList(); // _context.ServiceProviderTenantMappings.UpdateRange(newServiceProviderTenant); // } // await _context.SaveChangesAsync(); // return Ok(ApiResponse.SuccessResponse(new { }, $"Service Provider is {message}d", 200)); //} #endregion #region =================================================================== Helper Functions =================================================================== //private ServicesProviderFilter? TryDeserializeServicesProviderFilter(string? filter) //{ // if (string.IsNullOrWhiteSpace(filter)) // { // return null; // } // var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; // ServicesProviderFilter? documentFilter = null; // try // { // // First, try to deserialize directly. This is the expected case (e.g., from a web client). // documentFilter = JsonSerializer.Deserialize(filter, options); // } // catch (JsonException ex) // { // _logger.LogError(ex, "[{MethodName}] Failed to directly deserialize filter. Attempting to unescape and re-parse. Filter: {Filter}", nameof(TryDeserializeServicesProviderFilter), filter); // // If direct deserialization fails, it might be an escaped string (common with tools like Postman or some mobile clients). // try // { // // Unescape the string first, then deserialize the result. // string unescapedJsonString = JsonSerializer.Deserialize(filter, options) ?? ""; // if (!string.IsNullOrWhiteSpace(unescapedJsonString)) // { // documentFilter = JsonSerializer.Deserialize(unescapedJsonString, options); // } // } // catch (JsonException ex1) // { // // If both attempts fail, log the final error and return null. // _logger.LogError(ex1, "[{MethodName}] All attempts to deserialize the filter failed. Filter will be ignored. Filter: {Filter}", nameof(TryDeserializeServicesProviderFilter), filter); // return null; // } // } // return documentFilter; //} #endregion } }