aboutsummaryrefslogtreecommitdiff
path: root/src/Costasdev.Busurbano.Backend/VigoController.cs
blob: be0e7c6befbd8ae6037036d56784c9d6d1fa3cb0 (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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
using Costasdev.VigoTransitApi;
using System.Text.Json;

namespace Costasdev.Busurbano.Backend;

[ApiController]
[Route("api/vigo")]
public class VigoController : ControllerBase
{
    private readonly VigoTransitApiClient _api;
    private readonly IMemoryCache _cache;
    private readonly HttpClient _httpClient;

    public VigoController(HttpClient http, IMemoryCache cache)
    {
        _api = new VigoTransitApiClient(http);
        _cache = cache;
        _httpClient = http;
    }

    [HttpGet("GetStopEstimates")]
    public async Task<IActionResult> Run()
    {
        var argumentAvailable = Request.Query.TryGetValue("id", out var requestedStopIdString);
        if (!argumentAvailable)
        {
            return BadRequest("Please provide a stop id as a query parameter with the name 'id'.");
        }

        var argumentNumber = int.TryParse(requestedStopIdString, out var requestedStopId);
        if (!argumentNumber)
        {
            return BadRequest("The provided stop id is not a valid number.");
        }

        try
        {
            var response = await _api.GetStopEstimates(requestedStopId);
            // Return only the estimates array, not the stop metadata
            return new OkObjectResult(response.Estimates);
        }
        catch (InvalidOperationException)
        {
            return new BadRequestObjectResult("Stop not found");
        }
    }

    [HttpGet("GetStopTimetable")]
    public async Task<IActionResult> GetStopTimetable()
    {
        // Get date parameter (default to today if not provided)
        var dateString = Request.Query.TryGetValue("date", out var requestedDate)
            ? requestedDate.ToString()
            : DateTime.Today.ToString("yyyy-MM-dd");

        // Validate date format
        if (!DateTime.TryParseExact(dateString, "yyyy-MM-dd", null, System.Globalization.DateTimeStyles.None, out var parsedDate))
        {
            return BadRequest("Invalid date format. Please use yyyy-MM-dd format.");
        }

        // Get stopId parameter
        if (!Request.Query.TryGetValue("stopId", out var requestedStopIdString))
        {
            return BadRequest("Please provide a stop id as a query parameter with the name 'stopId'.");
        }

        if (!int.TryParse(requestedStopIdString, out var requestedStopId))
        {
            return BadRequest("The provided stop id is not a valid number.");
        }

        // Create cache key
        var cacheKey = $"timetable_{dateString}_{requestedStopId}";

        // Try to get from cache first
        if (_cache.TryGetValue(cacheKey, out var cachedData))
        {
            return new OkObjectResult(cachedData);
        }

        try
        {
            // Fetch data from external API
            var url = $"https://costas.dev/static-storage/vitrasa_svc/stops/{dateString}/{requestedStopId}.json";
            var response = await _httpClient.GetAsync(url);

            if (!response.IsSuccessStatusCode)
            {
                if (response.StatusCode == System.Net.HttpStatusCode.NotFound)
                {
                    return NotFound($"Timetable data not found for stop {requestedStopId} on {dateString}");
                }
                return StatusCode((int)response.StatusCode, "Error fetching timetable data");
            }

            var jsonContent = await response.Content.ReadAsStringAsync();
            var timetableData = JsonSerializer.Deserialize<JsonElement>(jsonContent);

            // Cache the data for 12 hours
            var cacheOptions = new MemoryCacheEntryOptions
            {
                AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(12),
                SlidingExpiration = TimeSpan.FromHours(6), // Refresh cache if accessed within 6 hours of expiry
                Priority = CacheItemPriority.Normal
            };

            _cache.Set(cacheKey, timetableData, cacheOptions);

            return new OkObjectResult(timetableData);
        }
        catch (HttpRequestException ex)
        {
            return StatusCode(500, $"Error fetching timetable data: {ex.Message}");
        }
        catch (JsonException ex)
        {
            return StatusCode(500, $"Error parsing timetable data: {ex.Message}");
        }
        catch (Exception ex)
        {
            return StatusCode(500, $"Unexpected error: {ex.Message}");
        }
    }
}