using Dapper; using Microsoft.Extensions.Logging; using Npgsql; using SatelliteProvider.DataAccess.Models; namespace SatelliteProvider.DataAccess.Repositories; public class RouteRepository : IRouteRepository { private readonly string _connectionString; private readonly ILogger _logger; public RouteRepository(string connectionString, ILogger logger) { _connectionString = connectionString; _logger = logger; } public async Task GetByIdAsync(Guid id) { using var connection = new NpgsqlConnection(_connectionString); const string sql = @" SELECT id, name, description, region_size_meters as RegionSizeMeters, zoom_level as ZoomLevel, total_distance_meters as TotalDistanceMeters, total_points as TotalPoints, request_maps as RequestMaps, maps_ready as MapsReady, create_tiles_zip as CreateTilesZip, csv_file_path as CsvFilePath, summary_file_path as SummaryFilePath, stitched_image_path as StitchedImagePath, tiles_zip_path as TilesZipPath, created_at as CreatedAt, updated_at as UpdatedAt FROM routes WHERE id = @Id"; return await connection.QuerySingleOrDefaultAsync(sql, new { Id = id }); } public async Task> GetRoutePointsAsync(Guid routeId) { using var connection = new NpgsqlConnection(_connectionString); const string sql = @" SELECT id, route_id as RouteId, sequence_number as SequenceNumber, latitude, longitude, point_type as PointType, segment_index as SegmentIndex, distance_from_previous as DistanceFromPrevious, created_at as CreatedAt FROM route_points WHERE route_id = @RouteId ORDER BY sequence_number"; var points = (await connection.QueryAsync(sql, new { RouteId = routeId })).ToList(); return points; } public async Task InsertRouteAsync(RouteEntity route) { using var connection = new NpgsqlConnection(_connectionString); const string sql = @" INSERT INTO routes (id, name, description, region_size_meters, zoom_level, total_distance_meters, total_points, request_maps, maps_ready, create_tiles_zip, csv_file_path, summary_file_path, stitched_image_path, tiles_zip_path, created_at, updated_at) VALUES (@Id, @Name, @Description, @RegionSizeMeters, @ZoomLevel, @TotalDistanceMeters, @TotalPoints, @RequestMaps, @MapsReady, @CreateTilesZip, @CsvFilePath, @SummaryFilePath, @StitchedImagePath, @TilesZipPath, @CreatedAt, @UpdatedAt) RETURNING id"; return await connection.ExecuteScalarAsync(sql, route); } public async Task InsertRoutePointsAsync(IEnumerable points) { using var connection = new NpgsqlConnection(_connectionString); const string sql = @" INSERT INTO route_points (id, route_id, sequence_number, latitude, longitude, point_type, segment_index, distance_from_previous, created_at) VALUES (@Id, @RouteId, @SequenceNumber, @Latitude, @Longitude, @PointType, @SegmentIndex, @DistanceFromPrevious, @CreatedAt)"; var pointsList = points.ToList(); await connection.ExecuteAsync(sql, pointsList); } public async Task UpdateRouteAsync(RouteEntity route) { using var connection = new NpgsqlConnection(_connectionString); const string sql = @" UPDATE routes SET name = @Name, description = @Description, region_size_meters = @RegionSizeMeters, zoom_level = @ZoomLevel, total_distance_meters = @TotalDistanceMeters, total_points = @TotalPoints, request_maps = @RequestMaps, maps_ready = @MapsReady, create_tiles_zip = @CreateTilesZip, csv_file_path = @CsvFilePath, summary_file_path = @SummaryFilePath, stitched_image_path = @StitchedImagePath, tiles_zip_path = @TilesZipPath, updated_at = @UpdatedAt WHERE id = @Id"; return await connection.ExecuteAsync(sql, route); } public async Task DeleteRouteAsync(Guid id) { using var connection = new NpgsqlConnection(_connectionString); const string sql = "DELETE FROM routes WHERE id = @Id"; return await connection.ExecuteAsync(sql, new { Id = id }); } public async Task LinkRouteToRegionAsync(Guid routeId, Guid regionId, bool isGeofence = false, int? geofencePolygonIndex = null) { using var connection = new NpgsqlConnection(_connectionString); const string sql = @" INSERT INTO route_regions (route_id, region_id, is_geofence, geofence_polygon_index, created_at) VALUES (@RouteId, @RegionId, @IsGeofence, @GeofencePolygonIndex, @CreatedAt) ON CONFLICT (route_id, region_id) DO NOTHING"; await connection.ExecuteAsync(sql, new { RouteId = routeId, RegionId = regionId, IsGeofence = isGeofence, GeofencePolygonIndex = geofencePolygonIndex, CreatedAt = DateTime.UtcNow }); } public async Task> GetRegionIdsByRouteAsync(Guid routeId) { using var connection = new NpgsqlConnection(_connectionString); const string sql = @" SELECT region_id FROM route_regions WHERE route_id = @RouteId AND (is_geofence = false OR is_geofence IS NULL)"; return await connection.QueryAsync(sql, new { RouteId = routeId }); } public async Task> GetGeofenceRegionIdsByRouteAsync(Guid routeId) { using var connection = new NpgsqlConnection(_connectionString); const string sql = @" SELECT region_id FROM route_regions WHERE route_id = @RouteId AND is_geofence = true"; return await connection.QueryAsync(sql, new { RouteId = routeId }); } public async Task> GetRoutesWithPendingMapsAsync() { using var connection = new NpgsqlConnection(_connectionString); const string sql = @" SELECT id, name, description, region_size_meters as RegionSizeMeters, zoom_level as ZoomLevel, total_distance_meters as TotalDistanceMeters, total_points as TotalPoints, request_maps as RequestMaps, maps_ready as MapsReady, create_tiles_zip as CreateTilesZip, csv_file_path as CsvFilePath, summary_file_path as SummaryFilePath, stitched_image_path as StitchedImagePath, tiles_zip_path as TilesZipPath, created_at as CreatedAt, updated_at as UpdatedAt FROM routes WHERE request_maps = true AND maps_ready = false"; return await connection.QueryAsync(sql); } public async Task>> GetGeofenceRegionsByPolygonAsync(Guid routeId) { using var connection = new NpgsqlConnection(_connectionString); const string sql = @" SELECT region_id, geofence_polygon_index FROM route_regions WHERE route_id = @RouteId AND is_geofence = true AND geofence_polygon_index IS NOT NULL ORDER BY geofence_polygon_index"; var results = await connection.QueryAsync<(Guid RegionId, int PolygonIndex)>(sql, new { RouteId = routeId }); var grouped = new Dictionary>(); foreach (var (regionId, polygonIndex) in results) { if (!grouped.ContainsKey(polygonIndex)) { grouped[polygonIndex] = new List(); } grouped[polygonIndex].Add(regionId); } return grouped; } }