using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using eprintServer.Services; using eprintServer.DTOs; using eprintServer.Entities; using eprintServer.Requests; using eprintServer.Interfaces; using System.Security.Claims; namespace eprintServer.Controllers; [ApiController] [Route("api/[controller]")] [Authorize(Roles = "ADMIN,SUB_ADMIN,USER")] public class UserCustomizationsController : ControllerBase { private readonly IUserCustomizationService _customizationService; private readonly UserService _userService; private readonly ILogger _logger; public UserCustomizationsController(IUserCustomizationService customizationService, ILogger logger, UserService userService) { _customizationService = customizationService; _logger = logger; _userService = userService; } /// /// Create a new customization and save single or multiple steps at once /// [HttpPost("create-costomization")] public async Task> CreateCustomization( [FromBody] CreateAndSaveStepsRequest request) { try { var username = User.FindFirstValue(ClaimTypes.Name); if (string.IsNullOrEmpty(username)) { return Unauthorized("Username claim not found in token."); } var user = await _userService.FindByUsernameAsync(username); if (user == null) { return NotFound("User not found."); } if (request.ProductId <= 0) return BadRequest("ProductId is required and must be greater than 0"); if (request.Steps == null || !request.Steps.Any()) return BadRequest("At least one step is required"); long userId = user.Id; var customization = await _customizationService.CreateAndSaveStepsAsync(userId, request.ProductId, request.Steps); return CreatedAtAction(nameof(GetCustomizationById), new { customizationId = customization.Id }, MapToDto(customization)); } catch (KeyNotFoundException ex) { _logger.LogWarning($"Product not found: {ex.Message}"); return NotFound(ex.Message); } catch (Exception ex) { _logger.LogError($"Error creating customization: {ex.Message}"); return StatusCode(500, "An error occurred while creating the customization"); } } [HttpGet("user/last-customization-in-progress/{productId}")] public async Task> GetLastInProgressCustomization(long productId) { try { var username = User.FindFirstValue(ClaimTypes.Name); if (string.IsNullOrEmpty(username)) { return Unauthorized("Username claim not found in token."); } var user = await _userService.FindByUsernameAsync(username); if (user == null) { return NotFound("User not found."); } long userId = user.Id; var customization = await _customizationService.GetLastInProgressCustomizationByUserAsync(userId, productId); if (customization == null) return BadRequest("No in-progress customization found for this user"); return Ok(MapToDto(customization)); } catch (Exception ex) { _logger.LogError($"Error fetching last in-progress customization: {ex.Message}"); return StatusCode(500, "An error occurred while fetching the customization"); } } /// /// Save a customization step to the database /// [HttpPost("save-new-step-in-customization/{customizationId}")] public async Task> SaveStep( Guid customizationId, [FromBody] SaveCustomizationStepRequest request) { try { if (string.IsNullOrWhiteSpace(request.StepName)) return BadRequest("StepName is required"); var customization = await _customizationService.SaveStepAsync(customizationId, request); return Ok(MapToDto(customization)); } catch (KeyNotFoundException ex) { _logger.LogWarning($"Customization not found: {ex.Message}"); return NotFound(ex.Message); } catch (InvalidOperationException ex) { _logger.LogWarning($"Invalid operation: {ex.Message}"); return BadRequest(ex.Message); } catch (Exception ex) { _logger.LogError($"Error saving step: {ex.Message}"); return StatusCode(500, "An error occurred while saving the step"); } } /// /// Complete a customization /// [HttpPost("make-customization-status-compleate/{customizationId}")] public async Task> CompleteCustomization(Guid customizationId) { try { var customization = await _customizationService.CompleteCustomizationAsync(customizationId); return Ok(MapToDto(customization)); } catch (KeyNotFoundException ex) { _logger.LogWarning($"Customization not found: {ex.Message}"); return NotFound(ex.Message); } catch (InvalidOperationException ex) { _logger.LogWarning($"Invalid operation: {ex.Message}"); return BadRequest(ex.Message); } catch (Exception ex) { _logger.LogError($"Error completing customization: {ex.Message}"); return StatusCode(500, "An error occurred while completing the customization"); } } /// /// Get a customization by ID /// [HttpGet("get-customization-by-id/{customizationId}")] public async Task> GetCustomizationById(Guid customizationId) { try { var customization = await _customizationService.GetUserCustomizationByIdAsync(customizationId); return Ok(MapToDto(customization)); } catch (KeyNotFoundException ex) { return NotFound(ex.Message); } catch (Exception ex) { _logger.LogError($"Error fetching customization: {ex.Message}"); return StatusCode(500, "An error occurred while fetching the customization"); } } /// /// Get all completed customizations for the current user /// [HttpGet("user-all-compleate-customizations/customizations")] public async Task>> GetUserCustomizations() { try { var username = User.FindFirstValue(ClaimTypes.Name); if (string.IsNullOrEmpty(username)) { return Unauthorized("Username claim not found in token."); } var user = await _userService.FindByUsernameAsync(username); if (user == null) { return NotFound("User not found."); } long userId = user.Id; var customizations = await _customizationService.GetUserCustomizationsByUserIdAsync(userId); return Ok(customizations.Select(MapToDto)); } catch (Exception ex) { _logger.LogError($"Error fetching user customizations: {ex.Message}"); return StatusCode(500, "An error occurred while fetching customizations"); } } /// /// Get all completed customizations for a specific product and user /// [HttpGet("user/all-compleate-customizations-product/{productId}")] public async Task>> GetUserCustomizationsByProduct(long productId) { try { var username = User.FindFirstValue(ClaimTypes.Name); if (string.IsNullOrEmpty(username)) { return Unauthorized("Username claim not found in token."); } var user = await _userService.FindByUsernameAsync(username); if (user == null) { return NotFound("User not found."); } long userId = user.Id; var customizations = await _customizationService.GetUserCustomizationsByProductAsync(userId, productId); return Ok(customizations.Select(MapToDto)); } catch (Exception ex) { _logger.LogError($"Error fetching customizations by product: {ex.Message}"); return StatusCode(500, "An error occurred while fetching customizations"); } } /// /// Get all completed customizations across all users (Admin only) /// [HttpGet("admin/all")] [Authorize(Roles = "Admin")] public async Task>> GetAllCompletedCustomizations() { try { var customizations = await _customizationService.GetAllCompletedCustomizationsAsync(); return Ok(customizations.Select(MapToDto)); } catch (Exception ex) { _logger.LogError($"Error fetching all customizations: {ex.Message}"); return StatusCode(500, "An error occurred while fetching customizations"); } } /// /// Update a customization (only null properties are skipped) /// [HttpPut("update-customization/{customizationId}")] public async Task> UpdateCustomization( Guid customizationId, [FromBody] UpdateCustomizationRequest request) { try { var customization = await _customizationService.UpdateCustomizationAsync(customizationId, request); return Ok(MapToDto(customization)); } catch (KeyNotFoundException ex) { return NotFound(ex.Message); } catch (Exception ex) { _logger.LogError($"Error updating customization: {ex.Message}"); return StatusCode(500, "An error occurred while updating the customization"); } } /// /// Delete a customization /// [HttpDelete("delete-customization/{customizationId}")] public async Task DeleteCustomization(Guid customizationId) { try { await _customizationService.DeleteCustomizationAsync(customizationId); return NoContent(); } catch (KeyNotFoundException ex) { return NotFound(ex.Message); } catch (Exception ex) { _logger.LogError($"Error deleting customization: {ex.Message}"); return StatusCode(500, "An error occurred while deleting the customization"); } } private UserCustomizationDto MapToDto(UserCustomization customization) { return new UserCustomizationDto { Id = customization.Id, UserId = customization.UserId, ProductId = customization.ProductId, ProgressId = customization.ProgressId, Status = customization.Status.ToString(), StartedAt = customization.StartedAt, CompletedAt = customization.CompletedAt, TotalPrice = customization.TotalPrice, OrderId = customization.OrderId, CreatedAt = customization.CreatedAt, UpdatedAt = customization.UpdatedAt, IsBranchingEnabled = customization.Product?.IsBranchingEnabled ?? false, Steps = customization.Steps?.Select(s => MapStepToDto(s, customization.Product)).ToList() }; } private UserCustomizationStepDto MapStepToDto(UserCustomizationStep step, CustomizableProduct productConfig) { // Find the Admin-defined step config to get the default nextStepId var stepConfig = productConfig?.Steps?.FirstOrDefault(s => s.Id == step.StepId); return new UserCustomizationStepDto { Id = step.Id, UserCustomizationId = step.UserCustomizationId, StepId = step.StepId, StepOrder = step.StepOrder, StepName = step.StepName, IsCompleted = step.IsCompleted, CompletedAt = step.CompletedAt, CreatedAt = step.CreatedAt, UpdatedAt = step.UpdatedAt, // ADDED: Default Next Step for this Node level NextStepId = stepConfig?.NextStepId, // Pass the step configuration to the field values mapper FieldValues = step.FieldValues?.Select(fv => MapFieldValueToDto(fv, stepConfig)).ToList(), UserMockup = step.UserMockup != null ? MapMockupToDto(step.UserMockup) : null }; } private UserCustomizationFieldValueDto MapFieldValueToDto(UserCustomizationFieldValue fv, Step stepConfig) { // 1. Find the FormElement config var elementConfig = stepConfig?.FormElements?.FirstOrDefault(fe => fe.Id == fv.FormElementId); // 2. Find the selected Option config // We use StringComparison.OrdinalIgnoreCase and check if the saved value contains or matches the option value var selectedOption = elementConfig?.Options?.FirstOrDefault(o => !string.IsNullOrEmpty(fv.FieldValue) && !string.IsNullOrEmpty(o.OptionValue) && (fv.FieldValue.Equals(o.OptionValue, StringComparison.OrdinalIgnoreCase) || fv.FieldValue.EndsWith(":" + o.OptionValue, StringComparison.OrdinalIgnoreCase) || o.OptionValue.EndsWith(":" + fv.FieldValue, StringComparison.OrdinalIgnoreCase)) ); return new UserCustomizationFieldValueDto { Id = fv.Id, UserCustomizationStepId = fv.UserCustomizationStepId, FormElementId = fv.FormElementId, FieldName = fv.FieldName, FieldLabel = fv.FieldLabel, InputType = fv.InputType.ToString(), FieldValue = fv.FieldValue, // If a match is found, this will now be 18 instead of null NextStepId = selectedOption?.NextStepId, CreatedAt = fv.CreatedAt, UpdatedAt = fv.UpdatedAt }; } private UserMockupDto MapMockupToDto(UserMockup mockup) { return new UserMockupDto { Id = mockup.Id, UserCustomizationStepId = mockup.UserCustomizationStepId, PrintWidth = mockup.PrintWidth, PrintHeight = mockup.PrintHeight, FileId = mockup.FileId, File = mockup.File != null ? MapFileToDto(mockup.File) : null, ThumbnailFileId = mockup.ThumbnailFileId, Thumbnail = mockup.Thumbnail != null ? MapFileToDto(mockup.Thumbnail) : null, Timestamp = mockup.Timestamp }; } private FileModelDto MapFileToDto(FileModel file) { return new FileModelDto { Id = file.Id, Name = file.Name, Description = file.Description, Url = file.Url, Type = file.Type, Size = file.Size }; } [HttpPost("upload-files")] [Consumes("multipart/form-data")] public async Task UploadFile( [FromQuery] IFormFile file) { if (file == null || file.Length == 0) return BadRequest("File is required"); var result = await _userService.ProcessFileAsync(file, "UserCustomizationMockup"); if (result == null) return StatusCode(StatusCodes.Status500InternalServerError, "File upload failed"); return Ok(result); } } /* [ApiController] [Route("api/[controller]")] [Authorize] public class UserCustomizationController : ControllerBase { private readonly UserCustomizationService _service; private readonly UserService _userService; public UserCustomizationController(UserCustomizationService service, UserService userService) { _service = service; _userService = userService; } private int GetUserId() { var userIdClaim = User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value; return int.Parse(userIdClaim); } [HttpPost("start")] public async Task StartCustomization([FromBody] StartCustomizationRequest request) { try { var userId = GetUserId(); var result = await _service.StartCustomizationAsync(userId, request); return Ok(result); } catch (Exception ex) { return BadRequest(new { message = ex.Message }); } } [HttpPost("save-step")] public async Task SaveStepData([FromBody] SaveStepDataRequest request) { try { var userId = GetUserId(); var result = await _service.SaveStepDataAsync(userId, request); return Ok(result); } catch (Exception ex) { return BadRequest(new { message = ex.Message }); } } [HttpPost("complete")] public async Task CompleteCustomization([FromBody] CompleteCustomizationRequest request) { try { var userId = GetUserId(); var result = await _service.CompleteCustomizationAsync(userId, request); return Ok(result); } catch (Exception ex) { return BadRequest(new { message = ex.Message }); } } [HttpGet("progress/{progressId}")] public async Task GetProgress(Guid progressId) { try { var userId = GetUserId(); var result = await _service.GetUserProgressAsync(userId, progressId); return Ok(result); } catch (Exception ex) { return NotFound(new { message = ex.Message }); } } [HttpGet("my-customizations")] public async Task GetMyCustomizations() { try { var userId = GetUserId(); var result = await _service.GetUserCustomizationsAsync(userId); return Ok(result); } catch (Exception ex) { return BadRequest(new { message = ex.Message }); } } [HttpGet("my-latest-filled-data/{productId}")] public async Task GetLatestCustomizationData(long productId) { try { var username = User.FindFirstValue(ClaimTypes.Name); if (string.IsNullOrEmpty(username)) { return Unauthorized("Username claim not found in token."); } var user = await _userService.FindByUsernameAsync(username); if (user == null) { return NotFound("User not found."); } var result = await _service.GetLatestProductProgressAsync(user.Id, productId); return Ok(result); } catch (Exception ex) { return NotFound(new { message = ex.Message }); } } [HttpPost("upload-files")] [Consumes("multipart/form-data")] public async Task UploadFile( [FromQuery] IFormFile file) { if (file == null || file.Length == 0) return BadRequest("File is required"); var result = await _service.ProcessFileAsync(file, "UserCustomizationMockup"); if (result == null) return StatusCode(StatusCodes.Status500InternalServerError, "File upload failed"); return Ok(result); } } */