aboutsummaryrefslogtreecommitdiff
path: root/src/Enmarcha.Backend/Services/Processors/SantiagoRealTimeProcessor.cs
blob: 929e4398995db6696c422c37c7c0f06f4be3374b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
using Enmarcha.Backend.Helpers;
using Enmarcha.Backend.Types.Arrivals;
using Enmarcha.Sources.Tussa;
using Arrival = Enmarcha.Backend.Types.Arrivals.Arrival;

namespace Enmarcha.Backend.Services.Processors;

public class SantiagoRealTimeProcessor : AbstractRealTimeProcessor
{
    private readonly SantiagoRealtimeEstimatesProvider _realtime;
    private readonly FeedService _feedService;
    private readonly ILogger<SantiagoRealTimeProcessor> _logger;

    public SantiagoRealTimeProcessor(
        HttpClient http,
        FeedService feedService,
        ILogger<SantiagoRealTimeProcessor> logger)
    {
        _realtime = new SantiagoRealtimeEstimatesProvider(http);
        _feedService = feedService;
        _logger = logger;
    }

    public override async Task ProcessAsync(ArrivalsContext context)
    {
        if (!context.StopId.StartsWith("tussa:")) return;

        var normalizedCode = _feedService.NormalizeStopCode("tussa", context.StopCode);
        if (!int.TryParse(normalizedCode, out var numericStopId)) return;

        try
        {
            var realtime = await _realtime.GetEstimatesForStop(numericStopId);

            var usedTripIds = new HashSet<string>();

            foreach (var estimate in realtime)
            {
                var bestMatch = context.Arrivals
                    .Where(a => !usedTripIds.Contains(a.TripId))
                    .Where(a => a.Route.RouteIdInGtfs.Trim() == estimate.Id.ToString())
                    .Select(a => new
                    {
                        Arrival = a,
                        TimeDiff = estimate.MinutesToArrive - a.Estimate.Minutes, // RealTime - Schedule
                        RouteMatch = true
                    })
                    .Where(x => x.RouteMatch) // Strict route matching
                    .Where(x => x.TimeDiff is >= -5 and <= 25) // Allow 2m early (RealTime < Schedule) or 25m late (RealTime > Schedule)
                    .OrderBy(x => Math.Abs(x.TimeDiff)) // Best time fit
                    .FirstOrDefault();

                if (bestMatch is null)
                {
                    context.Arrivals.Add(new Arrival
                    {
                        TripId = $"tussa:rt:{estimate.Id}:{estimate.MinutesToArrive}",
                        Route = new RouteInfo
                        {
                            GtfsId = $"tussa:{estimate.Id}",
                            ShortName = estimate.Sinoptico,
                            Colour = estimate.Colour,
                            TextColour = ContrastHelper.GetBestTextColour(estimate.Colour)
                        },
                        Headsign = new HeadsignInfo
                        {
                            Badge = "T.REAL",
                            Destination = estimate.Name
                        },
                        Estimate = new ArrivalDetails
                        {
                            Minutes = estimate.MinutesToArrive,
                            Precision = ArrivalPrecision.Confident
                        }
                    });
                    continue;
                }

                var arrival = bestMatch.Arrival;

                arrival.Estimate.Minutes = estimate.MinutesToArrive;
                arrival.Estimate.Precision = ArrivalPrecision.Confident;

                usedTripIds.Add(arrival.TripId);
            }
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error fetching Santiago real-time data for stop {StopId}", context.StopId);
        }
    }

}