using Manager.DTOs; using ManagerService.Data; using ManagerService.Data.SubSection; using ManagerService.DTOs; using ManagerService.Helpers; using ManagerService.Services; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using NSwag.Annotations; using System; using System.Collections.Generic; using System.Linq; namespace ManagerService.Controllers { [Authorize(Policy = ManagerService.Service.Security.Policies.ContentEditor)] [ApiController, Route("api/[controller]")] [OpenApiTag("Section parcours", Description = "Section parcours management")] public class SectionParcoursController : ControllerBase { private readonly MyInfoMateDbContext _myInfoMateDbContext; private readonly ILogger _logger; private readonly IConfiguration _configuration; IHexIdGeneratorService idService = new HexIdGeneratorService(); public SectionParcoursController(IConfiguration configuration, ILogger logger, MyInfoMateDbContext myInfoMateDbContext) { _logger = logger; _configuration = configuration; _myInfoMateDbContext = myInfoMateDbContext; } /// /// Get all guided paths from a parcours section /// [AllowAnonymous] [ProducesResponseType(typeof(List), 200)] [ProducesResponseType(typeof(string), 404)] [ProducesResponseType(typeof(string), 500)] [HttpGet("{sectionParcoursId}/guided-path")] public ObjectResult GetAllGuidedPathFromSection(string sectionParcoursId) { try { List guidedPaths = _myInfoMateDbContext.GuidedPaths .Include(gp => gp.ImageResource) .Include(gp => gp.Steps).ThenInclude(s => s.QuizQuestions).ThenInclude(q => q.PuzzleImage) .Where(gp => gp.SectionParcoursId == sectionParcoursId) .OrderBy(gp => gp.Order) .ToList(); return new OkObjectResult(guidedPaths.Select(gp => gp.ToDTO())); } catch (KeyNotFoundException ex) { return new NotFoundObjectResult(ex.Message); } catch (Exception ex) { return new ObjectResult(ex.Message) { StatusCode = 500 }; } } /// /// Create a new guided path in a parcours section /// [ProducesResponseType(typeof(GuidedPathDTO), 200)] [ProducesResponseType(typeof(string), 400)] [ProducesResponseType(typeof(string), 404)] [ProducesResponseType(typeof(string), 500)] [HttpPost("{sectionParcoursId}/guided-path")] public ObjectResult CreateGuidedPath(string sectionParcoursId, [FromBody] GuidedPathDTO guidedPathDTO) { try { if (sectionParcoursId == null) throw new ArgumentNullException("SectionParcoursId param is null"); if (guidedPathDTO == null) throw new ArgumentNullException("GuidedPath param is null"); var sectionParcours = _myInfoMateDbContext.Sections.OfType().FirstOrDefault(sp => sp.Id == sectionParcoursId); if (sectionParcours == null) throw new KeyNotFoundException("SectionParcours does not exist"); GuidedPath guidedPath = new GuidedPath().FromDTO(guidedPathDTO); guidedPath.Id = idService.GenerateHexId(); guidedPath.SectionParcoursId = sectionParcoursId; guidedPath.Order = _myInfoMateDbContext.GuidedPaths.Count(gp => gp.SectionParcoursId == sectionParcoursId); guidedPath.Steps = new List(); foreach (var stepDTO in guidedPathDTO.steps ?? new List()) { GuidedStep step = new GuidedStep().FromDTO(stepDTO); step.Id = idService.GenerateHexId(); step.GuidedPathId = guidedPath.Id; guidedPath.Steps.Add(step); } _myInfoMateDbContext.GuidedPaths.Add(guidedPath); _myInfoMateDbContext.SaveChanges(); return new OkObjectResult(guidedPath.ToDTO()); } catch (ArgumentNullException ex) { return new BadRequestObjectResult(ex.Message); } catch (KeyNotFoundException ex) { return new NotFoundObjectResult(ex.Message); } catch (Exception ex) { return new ObjectResult(ex.Message) { StatusCode = 500 }; } } /// /// Update a guided path /// [ProducesResponseType(typeof(GuidedPathDTO), 200)] [ProducesResponseType(typeof(string), 400)] [ProducesResponseType(typeof(string), 404)] [ProducesResponseType(typeof(string), 500)] [HttpPut("guided-path")] public ObjectResult UpdateGuidedPath([FromBody] GuidedPathDTO guidedPathDTO) { try { if (guidedPathDTO == null) throw new ArgumentNullException("GuidedPath param is null"); var existingGuidedPath = _myInfoMateDbContext.GuidedPaths .Include(gp => gp.Steps).ThenInclude(s => s.QuizQuestions) .FirstOrDefault(gp => gp.Id == guidedPathDTO.id); if (existingGuidedPath == null) throw new KeyNotFoundException("GuidedPath does not exist"); existingGuidedPath.InstanceId = guidedPathDTO.instanceId; existingGuidedPath.Title = guidedPathDTO.title ?? new List(); existingGuidedPath.Description = guidedPathDTO.description ?? new List(); existingGuidedPath.IsLinear = guidedPathDTO.isLinear; existingGuidedPath.RequireSuccessToAdvance = guidedPathDTO.requireSuccessToAdvance; existingGuidedPath.HideNextStepsUntilComplete = guidedPathDTO.hideNextStepsUntilComplete; existingGuidedPath.EstimatedDurationMinutes = guidedPathDTO.estimatedDurationMinutes; existingGuidedPath.ImageResourceId = guidedPathDTO.imageResourceId; existingGuidedPath.Order = guidedPathDTO.order.GetValueOrDefault(); existingGuidedPath.IsGameMode = guidedPathDTO.isGameMode; existingGuidedPath.GameMessageDebut = guidedPathDTO.gameMessageDebut ?? new List(); existingGuidedPath.GameMessageFin = guidedPathDTO.gameMessageFin ?? new List(); // Sync steps var dtoStepIds = (guidedPathDTO.steps ?? new List()) .Where(s => s.id != null) .Select(s => s.id) .ToHashSet(); var stepsToRemove = existingGuidedPath.Steps .Where(s => !dtoStepIds.Contains(s.Id)) .ToList(); foreach (var s in stepsToRemove) existingGuidedPath.Steps.Remove(s); foreach (var stepDTO in guidedPathDTO.steps ?? new List()) { var existing = existingGuidedPath.Steps.FirstOrDefault(s => s.Id == stepDTO.id); if (existing != null) { existing.FromDTO(stepDTO); existing.GuidedPathId = existingGuidedPath.Id; } else { var newStep = new GuidedStep().FromDTO(stepDTO); newStep.Id = idService.GenerateHexId(); newStep.GuidedPathId = existingGuidedPath.Id; existingGuidedPath.Steps.Add(newStep); } } _myInfoMateDbContext.SaveChanges(); return new OkObjectResult(existingGuidedPath.ToDTO()); } catch (ArgumentNullException ex) { return new BadRequestObjectResult(ex.Message); } catch (KeyNotFoundException ex) { return new NotFoundObjectResult(ex.Message); } catch (Exception ex) { return new ObjectResult(ex.Message) { StatusCode = 500 }; } } /// /// Delete a guided path /// [ProducesResponseType(typeof(string), 202)] [ProducesResponseType(typeof(string), 404)] [ProducesResponseType(typeof(string), 500)] [HttpDelete("guided-path/{guidedPathId}")] public ObjectResult DeleteGuidedPath(string guidedPathId) { try { var guidedPath = _myInfoMateDbContext.GuidedPaths.Include(gp => gp.Steps).FirstOrDefault(gp => gp.Id == guidedPathId); if (guidedPath == null) throw new KeyNotFoundException("GuidedPath does not exist"); _myInfoMateDbContext.RemoveRange(guidedPath.Steps); _myInfoMateDbContext.Remove(guidedPath); _myInfoMateDbContext.SaveChanges(); return new ObjectResult("The guidedPath has been deleted") { StatusCode = 202 }; } catch (KeyNotFoundException ex) { return new NotFoundObjectResult(ex.Message); } catch (Exception ex) { return new ObjectResult(ex.Message) { StatusCode = 500 }; } } /// /// Get all steps from a guided path /// [AllowAnonymous] [ProducesResponseType(typeof(List), 200)] [ProducesResponseType(typeof(string), 404)] [ProducesResponseType(typeof(string), 500)] [HttpGet("guided-path/{guidedPathId}/guided-step")] public ObjectResult GetAllGuidedStepFromGuidedPath(string guidedPathId) { try { List guidedSteps = _myInfoMateDbContext.GuidedSteps .Include(gs => gs.QuizQuestions) .Where(gs => gs.GuidedPathId == guidedPathId) .OrderBy(gs => gs.Order) .ToList(); return new OkObjectResult(guidedSteps.Select(gs => gs.ToDTO())); } catch (KeyNotFoundException ex) { return new NotFoundObjectResult(ex.Message); } catch (Exception ex) { return new ObjectResult(ex.Message) { StatusCode = 500 }; } } /// /// Create a new step in a guided path /// [ProducesResponseType(typeof(GuidedStepDTO), 200)] [ProducesResponseType(typeof(string), 400)] [ProducesResponseType(typeof(string), 404)] [ProducesResponseType(typeof(string), 500)] [HttpPost("guided-path/{guidedPathId}/guided-step")] public ObjectResult CreateGuidedStep(string guidedPathId, [FromBody] GuidedStepDTO guidedStepDTO) { try { if (guidedPathId == null) throw new ArgumentNullException("GuidedPathId param is null"); if (guidedStepDTO == null) throw new ArgumentNullException("GuidedStep param is null"); var guidedPath = _myInfoMateDbContext.GuidedPaths.FirstOrDefault(gp => gp.Id == guidedPathId); if (guidedPath == null) throw new KeyNotFoundException("GuidedPath does not exist"); GuidedStep guidedStep = new GuidedStep().FromDTO(guidedStepDTO); guidedStep.Id = idService.GenerateHexId(); guidedStep.GuidedPathId = guidedPathId; guidedStep.Order = _myInfoMateDbContext.GuidedSteps.Count(gs => gs.GuidedPathId == guidedPathId); _myInfoMateDbContext.GuidedSteps.Add(guidedStep); _myInfoMateDbContext.SaveChanges(); return new OkObjectResult(guidedStep.ToDTO()); } catch (ArgumentNullException ex) { return new BadRequestObjectResult(ex.Message); } catch (KeyNotFoundException ex) { return new NotFoundObjectResult(ex.Message); } catch (Exception ex) { return new ObjectResult(ex.Message) { StatusCode = 500 }; } } /// /// Update a guided step /// [ProducesResponseType(typeof(GuidedStepDTO), 200)] [ProducesResponseType(typeof(string), 400)] [ProducesResponseType(typeof(string), 404)] [ProducesResponseType(typeof(string), 500)] [HttpPut("guided-step")] public ObjectResult UpdateGuidedStep([FromBody] GuidedStepDTO guidedStepDTO) { try { if (guidedStepDTO == null) throw new ArgumentNullException("GuidedStep param is null"); var existingGuidedStep = _myInfoMateDbContext.GuidedSteps .Include(gs => gs.QuizQuestions) .FirstOrDefault(gs => gs.Id == guidedStepDTO.id); if (existingGuidedStep == null) throw new KeyNotFoundException("GuidedStep does not exist"); existingGuidedStep.FromDTO(guidedStepDTO); _myInfoMateDbContext.SaveChanges(); return new OkObjectResult(existingGuidedStep.ToDTO()); } catch (ArgumentNullException ex) { return new BadRequestObjectResult(ex.Message); } catch (KeyNotFoundException ex) { return new NotFoundObjectResult(ex.Message); } catch (Exception ex) { return new ObjectResult(ex.Message) { StatusCode = 500 }; } } /// /// Delete a guided step /// [ProducesResponseType(typeof(string), 202)] [ProducesResponseType(typeof(string), 404)] [ProducesResponseType(typeof(string), 500)] [HttpDelete("guided-step/{guidedStepId}")] public ObjectResult DeleteGuidedStep(string guidedStepId) { try { var guidedStep = _myInfoMateDbContext.GuidedSteps.Include(gs => gs.QuizQuestions).FirstOrDefault(gs => gs.Id == guidedStepId); if (guidedStep == null) throw new KeyNotFoundException("GuidedStep does not exist"); guidedStep.QuizQuestions?.Clear(); _myInfoMateDbContext.Remove(guidedStep); _myInfoMateDbContext.SaveChanges(); return new ObjectResult("The guidedStep has been deleted") { StatusCode = 202 }; } catch (KeyNotFoundException ex) { return new NotFoundObjectResult(ex.Message); } catch (Exception ex) { return new ObjectResult(ex.Message) { StatusCode = 500 }; } } } }