using System.Net.Http.Json; using System.Text.Json; using System.Text.Json.Nodes; using FsToolbox.Cli.Helper; using NLog; namespace FsToolbox.Cli.Tasks { public static partial class StoreTasks { private static readonly Logger Logger = LoggingService.GetLogger(nameof(StoreTasks)); #region Public Method GetPickupsAsync /// /// Retrieves all pickups for the specified store with authentication handled automatically. /// /// The HTTP client used to send the request. /// The store identifier to query. /// A list of pickups, or an empty list when no data is available. public static async Task> GetPickupsAsync(HttpClient httpClient, int storeId) { await AuthHelper.EnsureAuthenticationAsync(httpClient); var uri = string.Format(Endpoints.StorePickups, storeId); var response = await httpClient.GetAsync(uri); var responseBody = await response.Content.ReadAsStringAsync(); // handle unsuccessful response if (!response.IsSuccessStatusCode) Logger.Error("Pickup retrieval failed ({Status} {Reason}): {Body}", (int)response.StatusCode, response.ReasonPhrase, responseBody); var root = JsonNode.Parse(responseBody); if (root == null) return []; // Deserialize JsonNode to List var opts = new JsonSerializerOptions { PropertyNameCaseInsensitive = true, PropertyNamingPolicy = null // <── WICHTIG }; return root["pickups"].Deserialize>(opts) ?? []; } #endregion #region Public Method GetStoreMembersAsync /// /// Retrieves the members of the specified store, requiring authentication before the request. /// /// The HTTP client used to send the request. /// The store identifier to query. /// A list of store members, or an empty list when the call fails. public static async Task> GetStoreMembersAsync(HttpClient httpClient, int storeId) { await AuthHelper.EnsureAuthenticationAsync(httpClient); var uri = string.Format(Endpoints.StoreMembers, storeId); var response = await httpClient.GetAsync(uri); var responseBody = await response.Content.ReadAsStringAsync(); // handle unsuccessful response if (!response.IsSuccessStatusCode) { Logger.Error("Store members retrieval failed ({Status} {Reason}): {Body}", (int)response.StatusCode, response.ReasonPhrase, responseBody); return []; } var root = JsonNode.Parse(responseBody); if (root == null) return []; // Deserialize JsonNode to List var opts = new JsonSerializerOptions { PropertyNameCaseInsensitive = true, PropertyNamingPolicy = null // <── WICHTIG }; return root.Deserialize>(opts) ?? []; } #endregion #region Public Method GetStoreInformationAsync /// /// Retrieves store information including the calendar interval. /// /// The HTTP client used to send the request. /// The store identifier to query. /// The store information, or null when the call fails. public static async Task GetStoreInformationAsync(HttpClient httpClient, int storeId) { await AuthHelper.EnsureAuthenticationAsync(httpClient); var uri = string.Format(Endpoints.StoreInformation, storeId); var response = await httpClient.GetAsync(uri); var responseBody = await response.Content.ReadAsStringAsync(); if (!response.IsSuccessStatusCode) { Logger.Error("Store information retrieval failed ({Status} {Reason}): {Body}", (int)response.StatusCode, response.ReasonPhrase, responseBody); return null; } var opts = new JsonSerializerOptions { PropertyNameCaseInsensitive = true, PropertyNamingPolicy = null }; try { return JsonSerializer.Deserialize(responseBody, opts); } catch (JsonException ex) { Logger.Error(ex, "Failed to parse store information response."); return null; } } #endregion #region Public Method GetStoreLogAsync /// /// Retrieves the store log for a specified store and date range. /// /// The HTTP client used to send the request. /// The store identifier to query. /// Start date (formatted for the API). /// End date (formatted for the API). /// Comma-separated action IDs. /// The raw response body from the API. public static async Task GetStoreLogAsync(HttpClient httpClient, int storeId, string fromDate, string toDate, string storeLogActionIds) { await AuthHelper.EnsureAuthenticationAsync(httpClient); var uri = string.Format(Endpoints.StoreLog, storeId, fromDate, toDate, storeLogActionIds); var response = await httpClient.GetAsync(uri); var responseBody = await response.Content.ReadAsStringAsync(); // handle unsuccessful response if (!response.IsSuccessStatusCode) Logger.Error("Store log retrieval failed ({Status} {Reason}): {Body}", (int)response.StatusCode, response.ReasonPhrase, responseBody); return responseBody; } #endregion #region Public Method GetStoreLogEntriesAsync /// /// Retrieves and deserializes store log entries for a specified store and date range. /// /// The HTTP client used to send the request. /// The store identifier to query. /// Start date (formatted for the API). /// End date (formatted for the API). /// Comma-separated action IDs. /// A list of store log entries, or an empty list when parsing fails. public static async Task> GetStoreLogEntriesAsync(HttpClient httpClient, int storeId, string fromDate, string toDate, string storeLogActionIds) { var responseBody = await GetStoreLogAsync(httpClient, storeId, fromDate, toDate, storeLogActionIds); var opts = new JsonSerializerOptions { PropertyNameCaseInsensitive = true, PropertyNamingPolicy = null }; try { return JsonSerializer.Deserialize>(responseBody, opts) ?? []; } catch (JsonException ex) { Logger.Error(ex, "Failed to parse store log response."); return []; } } #endregion #region Public Method PatchPickupAsync /// /// Marks a pickup slot as confirmed for the specified store, date, and user. /// /// The HTTP client used to send the request. /// The store identifier containing the pickup. /// The date of the pickup to patch. /// The Foodsharing user identifier associated with the slot. /// A task representing the asynchronous operation. public static async Task PatchPickupAsync(HttpClient httpClient, int storeId, string pickupDate, int fsId) { await AuthHelper.EnsureAuthenticationAsync(httpClient); var uri = string.Format(Endpoints.StorePickupsSlot, storeId, pickupDate, fsId); var payload = new JsonObject { ["isConfirmed"] = true }; var response = await httpClient.PatchAsync(uri, JsonContent.Create(payload)); var responseBody = await response.Content.ReadAsStringAsync(); // handle unsuccessful response if (!response.IsSuccessStatusCode) Logger.Error("Pickup patch failed ({Status} {Reason}): {Body}", (int)response.StatusCode, response.ReasonPhrase, responseBody); else Logger.Info("Pickup patch succeeded {FsId} on {PickupDate}", fsId, pickupDate); } #endregion } }