using Microsoft.EntityFrameworkCore; using SchotzIadKmTracker.Api.Data; using SchotzIadKmTracker.Api.Models.Entities; using SchotzIadKmTracker.Api.Services; namespace SchotzIadKmTracker.Api.Repositories; /// public class TrajetRepository : ITrajetRepository { private readonly AppDbContext _context; private readonly IVehiculeService _vehiculeService; public TrajetRepository(AppDbContext context, IVehiculeService vehiculeService) { _context = context; _vehiculeService = vehiculeService; } /// public async Task GetByIdAsync(int id, CancellationToken cancellationToken = default) { return await _context.Trajets .FirstOrDefaultAsync(t => t.Id == id, cancellationToken); } /// public async Task> GetByDateAsync(DateOnly date, CancellationToken cancellationToken = default) { // SQLite ne sait pas trier (ORDER BY) sur une colonne decimal : on ramène les // trajets du jour puis on trie côté client (volume par journée très réduit). var trajets = await _context.Trajets .Where(t => t.Date == date) .ToListAsync(cancellationToken); return trajets .OrderBy(t => t.Position) .ThenBy(t => t.Id) .ToList(); } /// public async Task AppliquerVehiculeADateAsync(Trajet trajet, CancellationToken cancellationToken = default) { // Résolution du véhicule applicable à la date du trajet (Date_Effet la plus récente <= Date). var vehicule = await _vehiculeService.GetVehiculeADateAsync(trajet.Date, cancellationToken); if (vehicule is null) { // Aucun véhicule applicable à cette date : on laisse la logique métier (TrajetService) décider. return; } // Dénormalisation : copie de la puissance fiscale du véhicule au moment de l'association (Exigence 10.14). trajet.VehiculeId = vehicule.Id; trajet.PuissanceFiscale = vehicule.PuissanceFiscale; } /// public async Task CalculerPositionFinDeJourneeAsync(DateOnly date, CancellationToken cancellationToken = default) { // SQLite ne sait pas appliquer MAX sur une colonne decimal : on ramène les positions // du jour et on calcule le maximum côté client (volume par journée très réduit). var positions = await _context.Trajets .Where(t => t.Date == date) .Select(t => t.Position) .ToListAsync(cancellationToken); if (positions.Count == 0) { return 1.0m; } return positions.Max() + 1.0m; } /// public decimal CalculerPositionEntre(decimal positionPrecedente, decimal positionSuivante) { if (positionPrecedente >= positionSuivante) { throw new ArgumentException( $"La position précédente ({positionPrecedente}) doit être strictement inférieure à la position suivante ({positionSuivante}).", nameof(positionPrecedente)); } return (positionPrecedente + positionSuivante) / 2.0m; } /// public bool NecessiteRenumerotation(decimal positionPrecedente, decimal positionSuivante) { return positionSuivante - positionPrecedente < ITrajetRepository.SeuilRenumerotation; } /// public Task AddAsync(Trajet trajet, CancellationToken cancellationToken = default) { _context.Trajets.Add(trajet); return Task.CompletedTask; } /// public void Update(Trajet trajet) { _context.Trajets.Update(trajet); } /// public void Delete(Trajet trajet) { _context.Trajets.Remove(trajet); } /// public Task SauvegarderAsync(CancellationToken cancellationToken = default) { return _context.SaveChangesAsync(cancellationToken); } /// public async Task ReordonnerAsync( IReadOnlyList<(int Id, decimal Position)> positions, CancellationToken cancellationToken = default) { if (positions.Count == 0) { return; } await using var transaction = await _context.Database.BeginTransactionAsync(cancellationToken); try { var ids = positions.Select(p => p.Id).ToList(); var trajets = await _context.Trajets .Where(t => ids.Contains(t.Id)) .ToListAsync(cancellationToken); var positionParId = positions.ToDictionary(p => p.Id, p => p.Position); foreach (var trajet in trajets) { if (positionParId.TryGetValue(trajet.Id, out var nouvellePosition)) { // Seule la Position est modifiée ; aucun autre champ n'est altéré (Exigence 11.9). trajet.Position = nouvellePosition; trajet.UpdatedAt = DateTime.UtcNow; } } await _context.SaveChangesAsync(cancellationToken); await transaction.CommitAsync(cancellationToken); } catch { await transaction.RollbackAsync(cancellationToken); throw; } } /// public async Task> RenumeroterJourneeAsync(DateOnly date, CancellationToken cancellationToken = default) { await using var transaction = await _context.Database.BeginTransactionAsync(cancellationToken); try { // Préservation de l'ordre relatif courant (Position croissante, Id en départage) — Property 15. var trajets = (await _context.Trajets .Where(t => t.Date == date) .ToListAsync(cancellationToken)) .OrderBy(t => t.Position) .ThenBy(t => t.Id) .ToList(); decimal position = 1.0m; foreach (var trajet in trajets) { trajet.Position = position; trajet.UpdatedAt = DateTime.UtcNow; position += 1.0m; } await _context.SaveChangesAsync(cancellationToken); await transaction.CommitAsync(cancellationToken); return trajets; } catch { await transaction.RollbackAsync(cancellationToken); throw; } } }