using Microsoft.EntityFrameworkCore; using RobotNet10.MapEditor.Shared.DTOs.Requests; using RobotNet10.MapManager.Data; namespace RobotNet10.MapManager.Services; /// /// Service implementation for managing stations /// public class StationService(MapDbContext context) : IStationService { private readonly MapDbContext _context = context; public async Task CreateAsync(CreateStationRequest request) { // Check if station ID already exists in this level var exists = await _context.Stations .AnyAsync(s => s.LevelId == request.LayoutLevelId && s.StationId == request.StationId); if (exists) { throw new InvalidOperationException( $"Station with ID '{request.StationId}' already exists in this layout level"); } using var transaction = await _context.Database.BeginTransactionAsync(); try { var station = new Station { LevelId = request.LayoutLevelId, StationId = request.StationId, StationName = request.StationName, StationDescription = request.StationDescription, StationHeight = request.StationHeight, X = request.X, Y = request.Y, Theta = request.Theta }; _context.Stations.Add(station); await _context.SaveChangesAsync(); // Add interaction nodes if provided if (request.InteractionNodeIds != null && request.InteractionNodeIds.Count != 0) { foreach (var nodeId in request.InteractionNodeIds) { var interactionNode = new StationInteractionNode { StationId = station.Id, NodeId = nodeId }; _context.StationInteractionNodes.Add(interactionNode); } await _context.SaveChangesAsync(); } await transaction.CommitAsync(); // Reload with interaction nodes return (await GetByIdAsync(station.Id, includeInteractionNodes: true))!; } catch { await transaction.RollbackAsync(); throw; } } public async Task> GetStationsByLevelAsync(Guid layoutLevelId, bool includeInteractionNodes = true) { var query = _context.Stations.Where(s => s.LevelId == layoutLevelId); if (includeInteractionNodes) { query = query .Include(s => s.InteractionNodes) .ThenInclude(sin => sin.Node); } return await query.OrderBy(s => s.StationId).ToListAsync(); } public async Task GetByIdAsync(Guid stationId, bool includeInteractionNodes = true) { var query = _context.Stations.Where(s => s.Id == stationId); if (includeInteractionNodes) { query = query .Include(s => s.InteractionNodes) .ThenInclude(sin => sin.Node) .ThenInclude(n => n.VehicleProperties); } return await query.FirstOrDefaultAsync(); } public async Task UpdateAsync(Guid stationId, UpdateStationRequest request) { var station = await GetByIdAsync(stationId, includeInteractionNodes: true) ?? throw new InvalidOperationException($"Station with ID '{stationId}' not found"); // Update properties if (request.StationName != null) station.StationName = request.StationName; if (request.StationDescription != null) station.StationDescription = request.StationDescription; if (request.StationHeight.HasValue) station.StationHeight = request.StationHeight.Value; if (request.X.HasValue) station.X = request.X.Value; if (request.Y.HasValue) station.Y = request.Y.Value; if (request.Theta.HasValue) station.Theta = request.Theta.Value; // Update interaction nodes if provided if (request.InteractionNodeIds != null) { // Remove existing interaction nodes var existingInteractionNodes = await _context.StationInteractionNodes .Where(sin => sin.StationId == stationId) .ToListAsync(); _context.StationInteractionNodes.RemoveRange(existingInteractionNodes); // Add new interaction nodes foreach (var nodeId in request.InteractionNodeIds) { // Verify node exists var nodeExists = await _context.Nodes.AnyAsync(n => n.Id == nodeId); if (!nodeExists) { throw new InvalidOperationException($"Node with ID '{nodeId}' not found"); } var interactionNode = new StationInteractionNode { StationId = stationId, NodeId = nodeId }; _context.StationInteractionNodes.Add(interactionNode); } } await _context.SaveChangesAsync(); // Reload with interaction nodes return (await GetByIdAsync(stationId, includeInteractionNodes: true))!; } public async Task DeleteAsync(Guid stationId) { var station = await GetByIdAsync(stationId, includeInteractionNodes: false); if (station == null) { return false; } // Delete interaction nodes (cascade will handle this, but explicit for clarity) var interactionNodes = await _context.StationInteractionNodes .Where(sin => sin.StationId == stationId) .ToListAsync(); _context.StationInteractionNodes.RemoveRange(interactionNodes); // Delete station _context.Stations.Remove(station); await _context.SaveChangesAsync(); return true; } }