From a304c24b32c0327436bbd8c2853e60668e161b42 Mon Sep 17 00:00:00 2001 From: Ariel Costas Guerrero Date: Mon, 29 Dec 2025 00:41:52 +0100 Subject: Rename a lot of stuff, add Santiago real time --- .../Services/FareService.cs | 225 --------------------- 1 file changed, 225 deletions(-) delete mode 100644 src/Costasdev.Busurbano.Backend/Services/FareService.cs (limited to 'src/Costasdev.Busurbano.Backend/Services/FareService.cs') diff --git a/src/Costasdev.Busurbano.Backend/Services/FareService.cs b/src/Costasdev.Busurbano.Backend/Services/FareService.cs deleted file mode 100644 index c08d1d5..0000000 --- a/src/Costasdev.Busurbano.Backend/Services/FareService.cs +++ /dev/null @@ -1,225 +0,0 @@ -using Costasdev.Busurbano.Backend.Configuration; -using Costasdev.Busurbano.Backend.Services.Providers; -using Costasdev.Busurbano.Backend.Types.Planner; -using Microsoft.Extensions.Options; - -namespace Costasdev.Busurbano.Backend.Services; - -public record FareResult(decimal CashFareEuro, bool CashFareIsTotal, decimal CardFareEuro, bool CardFareIsTotal); - -public class FareService -{ - private readonly AppConfiguration _config; - private readonly XuntaFareProvider _xuntaFareProvider; - private readonly ILogger _logger; - - private const decimal VitrasaCashFare = 1.63M; - private const decimal VitrasaCardFare = 0.67M; - - private const decimal CorunaCashFare = 1.30M; - private const decimal CorunaCardFare = 0.45M; - - private const decimal SantiagoCashFare = 1.00M; - private const decimal SantiagoCardFare = 0.36M; - - public FareService( - IOptions config, - XuntaFareProvider xuntaFareProvider, - ILogger logger - ) - { - _config = config.Value; - _xuntaFareProvider = xuntaFareProvider; - _logger = logger; - } - - public FareResult CalculateFare(IEnumerable legs) - { - var transitLegs = legs - .Where(l => l.Mode != null && !l.Mode.Equals("WALK", StringComparison.CurrentCultureIgnoreCase)) - .ToList(); - - if (!transitLegs.Any()) - { - return new FareResult(0, true, 0, true); - } - - var cashResult = CalculateCashTotal(transitLegs); - var cardResult = CalculateCardTotal(transitLegs); - - return new FareResult( - cashResult.Item1, cashResult.Item2, - cardResult.Item1, cardResult.Item2 - ); - } - - private (decimal, bool) CalculateCashTotal(IEnumerable legs) - { - decimal total = 0L; - bool allLegsProcessed = true; - - foreach (var leg in legs) - { - switch (leg.FeedId) - { - case "santiago": - total += SantiagoCashFare; - break; - case "coruna": - total += CorunaCashFare; - break; - case "vitrasa": - total += VitrasaCashFare; - break; - case "xunta": - // TODO: Handle potentiall blow-ups - if (leg.From is not { ZoneId: not null }) - { - _logger.LogInformation("Ignored fare calculation for leg without From ZoneId. {FromStop}", leg.From?.StopId); - } - - if (leg.To is not { ZoneId: not null }) - { - _logger.LogInformation("Ignored fare calculation for leg without To ZoneId. {ToStop}", leg.To?.StopId); - } - - total += _xuntaFareProvider.GetPrice(leg.From!.ZoneId!, leg.To!.ZoneId!)!.PriceCash; - break; - default: - allLegsProcessed = false; - _logger.LogWarning("Unknown FeedId: {FeedId}", leg.FeedId); - break; - } - } - - return (total, allLegsProcessed); - } - - private (decimal, bool) CalculateCardTotal(IEnumerable legs) - { - List wallet = []; - decimal totalCost = 0; - - bool allLegsProcessed = true; - - foreach (var leg in legs) - { - int maxMinutes; - int maxUsages; - string? metroArea = null; - decimal initialFare = 0; - - switch (leg.FeedId) - { - case "vitrasa": - maxMinutes = 45; - maxUsages = 3; - initialFare = VitrasaCardFare; - break; - case "coruna": - maxMinutes = 45; - maxUsages = 2; - initialFare = CorunaCardFare; - break; - case "santiago": - maxMinutes = 60; - maxUsages = 2; - initialFare = SantiagoCardFare; - break; - case "xunta": - if (leg.From?.ZoneId == null || leg.To?.ZoneId == null) - { - _logger.LogWarning("Missing ZoneId for Xunta leg. From: {From}, To: {To}", leg.From?.StopId, leg.To?.StopId); - continue; - } - - var priceRecord = _xuntaFareProvider.GetPrice(leg.From.ZoneId, leg.To.ZoneId); - if (priceRecord == null) - { - _logger.LogWarning("No price record found for Xunta leg from {From} to {To}", leg.From.ZoneId, leg.To.ZoneId); - continue; - } - - metroArea = priceRecord.MetroArea; - initialFare = priceRecord.PriceCard; - maxMinutes = 60; - maxUsages = (metroArea != null && metroArea.StartsWith("ATM", StringComparison.OrdinalIgnoreCase)) ? 3 : 1; - break; - default: - _logger.LogWarning("Unknown FeedId: {FeedId}", leg.FeedId); - allLegsProcessed = false; - continue; - } - - var validTicket = wallet.FirstOrDefault(t => t.FeedId == leg.FeedId && t.IsValid(leg.StartTime, maxMinutes, maxUsages)); - - if (validTicket != null) - { - if (leg.FeedId == "xunta" && maxUsages > 1) // ATM upgrade logic - { - var upgradeRecord = _xuntaFareProvider.GetPrice(validTicket.StartZone, leg.To!.ZoneId!); - if (upgradeRecord != null) - { - decimal upgradeCost = Math.Max(0, upgradeRecord.PriceCard - validTicket.TotalPaid); - totalCost += upgradeCost; - validTicket.TotalPaid += upgradeCost; - validTicket.UsedTimes++; - _logger.LogDebug("Xunta ATM upgrade: added {Cost}€, total paid for ticket: {TotalPaid}€", upgradeCost, validTicket.TotalPaid); - } - else - { - // Fallback: treat as new ticket if upgrade path not found - totalCost += initialFare; - wallet.Add(new TicketPurchased - { - FeedId = leg.FeedId, - PurchasedAt = leg.StartTime, - MetroArea = metroArea, - StartZone = leg.From!.ZoneId!, - TotalPaid = initialFare - }); - } - } - else - { - // Free transfer for city systems or non-ATM Xunta (though non-ATM Xunta has maxUsages=1) - validTicket.UsedTimes++; - _logger.LogDebug("Free transfer for {FeedId}", leg.FeedId); - } - } - else - { - // New ticket - totalCost += initialFare; - wallet.Add(new TicketPurchased - { - FeedId = leg.FeedId!, - PurchasedAt = leg.StartTime, - MetroArea = metroArea, - StartZone = leg.FeedId == "xunta" ? leg.From!.ZoneId! : string.Empty, - TotalPaid = initialFare - }); - _logger.LogDebug("New ticket for {FeedId}: {Cost}€", leg.FeedId, initialFare); - } - } - - return (totalCost, allLegsProcessed); - } -} - -public class TicketPurchased -{ - public required string FeedId { get; set; } - - public DateTime PurchasedAt { get; set; } - public string? MetroArea { get; set; } - public required string StartZone { get; set; } - - public int UsedTimes = 1; - public decimal TotalPaid { get; set; } - - public bool IsValid(DateTime startTime, int maxMinutes, int maxUsagesIncluded) - { - return (startTime - PurchasedAt).TotalMinutes <= maxMinutes && UsedTimes < maxUsagesIncluded; - } -} -- cgit v1.3