using System.Net.Http.Headers; using System.Net.Http.Json; using System.Text.Json; using System.Text.Json.Serialization; using Model; namespace Web.Services; public class UserDecksService { private static readonly JsonSerializerOptions JsonOptions = new() { PropertyNameCaseInsensitive = true }; private readonly HttpClient _http; private readonly AuthService _authService; private readonly NetworkStatusService _networkStatus; private readonly string _graphqlEndpoint; private readonly string _restEndpoint; public UserDecksService(HttpClient http, AuthService authService, NetworkStatusService networkStatus) { _http = http; _authService = authService; _networkStatus = networkStatus; _graphqlEndpoint = http.BaseAddress != null && http.BaseAddress.Port == 7266 ? "/graphql" : "https://localhost:7266/graphql"; _restEndpoint = http.BaseAddress != null && http.BaseAddress.Port == 7266 ? "/api/decks" : "https://localhost:7266/api/decks"; } public async Task> GetDecksAsync() { if (!_networkStatus.IsOnline) return []; try { var request = new GraphQLRequest { Query = @"query GetDecks { decks { id name cards divers notes season updatedAt } }" }; var response = await SendGraphQLAsync(request); if (response?.Data?.Decks != null) return response.Data.Decks; } catch (Exception ex) { Console.WriteLine($"[WARNING] GraphQL GetDecks failed: {ex.Message}. Trying REST."); } // Fallback to REST try { var req = new HttpRequestMessage(HttpMethod.Get, _restEndpoint); AddAuthHeader(req); var res = await _http.SendAsync(req); if (res.IsSuccessStatusCode) { var decks = await res.Content.ReadFromJsonAsync>(JsonOptions); return decks ?? []; } } catch (Exception ex) { Console.WriteLine($"[ERROR] REST GetDecks failed: {ex.Message}"); } return []; } public async Task GetDeckAsync(Guid id) { if (!_networkStatus.IsOnline) return null; try { var request = new GraphQLRequest { Query = @"query GetDeck($id: UUID!) { deck(id: $id) { id name cards divers notes season updatedAt } }", Variables = new Dictionary { ["id"] = id } }; var response = await SendGraphQLAsync(request); if (response?.Data?.Deck != null) return response.Data.Deck; } catch (Exception ex) { Console.WriteLine($"[WARNING] GraphQL GetDeck failed: {ex.Message}. Trying REST."); } // Fallback to REST try { var req = new HttpRequestMessage(HttpMethod.Get, $"{_restEndpoint}/{id}"); AddAuthHeader(req); var res = await _http.SendAsync(req); if (res.IsSuccessStatusCode) { return await res.Content.ReadFromJsonAsync(JsonOptions); } } catch (Exception ex) { Console.WriteLine($"[ERROR] REST GetDeck failed: {ex.Message}"); } return null; } public async Task SaveDeckAsync(UserDeck deck) { if (!_networkStatus.IsOnline) return null; try { var request = new GraphQLRequest { Query = @"mutation SaveDeck($input: UserDeckInput!) { saveDeck(input: $input) { id name cards divers notes season updatedAt } }", Variables = new Dictionary { ["input"] = new Dictionary { ["id"] = deck.Id, ["name"] = deck.Name, ["cards"] = deck.Cards, ["divers"] = deck.Divers, ["notes"] = deck.Notes, ["season"] = deck.Season } } }; var response = await SendGraphQLAsync(request); if (response?.Data?.SaveDeck != null) return response.Data.SaveDeck; } catch (Exception ex) { Console.WriteLine($"[WARNING] GraphQL SaveDeck failed: {ex.Message}. Trying REST."); } // Fallback to REST try { var req = new HttpRequestMessage(HttpMethod.Post, _restEndpoint) { Content = JsonContent.Create(deck) }; AddAuthHeader(req); var res = await _http.SendAsync(req); if (res.IsSuccessStatusCode) { return await res.Content.ReadFromJsonAsync(JsonOptions); } } catch (Exception ex) { Console.WriteLine($"[ERROR] REST SaveDeck failed: {ex.Message}"); } return null; } public async Task DeleteDeckAsync(Guid id) { if (!_networkStatus.IsOnline) return false; try { var request = new GraphQLRequest { Query = @"mutation DeleteDeck($id: UUID!) { deleteDeck(id: $id) }", Variables = new Dictionary { ["id"] = id } }; var response = await SendGraphQLAsync(request); if (response?.Data?.DeleteDeck == true) return true; } catch (Exception ex) { Console.WriteLine($"[WARNING] GraphQL DeleteDeck failed: {ex.Message}. Trying REST."); } // Fallback to REST try { var req = new HttpRequestMessage(HttpMethod.Delete, $"{_restEndpoint}/{id}"); AddAuthHeader(req); var res = await _http.SendAsync(req); return res.IsSuccessStatusCode; } catch (Exception ex) { Console.WriteLine($"[ERROR] REST DeleteDeck failed: {ex.Message}"); } return false; } private void AddAuthHeader(HttpRequestMessage request) { if (!string.IsNullOrEmpty(_authService.AuthToken)) { request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _authService.AuthToken); } } private async Task?> SendGraphQLAsync(GraphQLRequest request) { var httpRequest = new HttpRequestMessage(HttpMethod.Post, _graphqlEndpoint) { Content = JsonContent.Create(request) }; AddAuthHeader(httpRequest); try { var httpResponse = await _http.SendAsync(httpRequest); if (httpResponse.IsSuccessStatusCode) return await httpResponse.Content.ReadFromJsonAsync>(JsonOptions); if (_graphqlEndpoint == "/graphql" || _graphqlEndpoint.StartsWith("https://")) { var fallbackUrl = _graphqlEndpoint == "/graphql" ? "https://localhost:7266/graphql" : "http://localhost:7266/graphql"; var fallbackReq = new HttpRequestMessage(HttpMethod.Post, fallbackUrl) { Content = JsonContent.Create(request) }; AddAuthHeader(fallbackReq); var fallbackRes = await _http.SendAsync(fallbackReq); if (fallbackRes.IsSuccessStatusCode) return await fallbackRes.Content.ReadFromJsonAsync>(JsonOptions); } } catch { try { var fallbackUrl = _graphqlEndpoint.StartsWith("http://") ? "https://localhost:7266/graphql" : "http://localhost:7266/graphql"; var fallbackReq = new HttpRequestMessage(HttpMethod.Post, fallbackUrl) { Content = JsonContent.Create(request) }; AddAuthHeader(fallbackReq); var fallbackRes = await _http.SendAsync(fallbackReq); if (fallbackRes.IsSuccessStatusCode) return await fallbackRes.Content.ReadFromJsonAsync>(JsonOptions); } catch { if (_graphqlEndpoint == "/graphql") { try { var fallbackReq = new HttpRequestMessage(HttpMethod.Post, "http://localhost:7266/graphql") { Content = JsonContent.Create(request) }; AddAuthHeader(fallbackReq); var fallbackRes = await _http.SendAsync(fallbackReq); if (fallbackRes.IsSuccessStatusCode) return await fallbackRes.Content.ReadFromJsonAsync>(JsonOptions); } catch { } } } } return null; } private class GraphQLRequest { [JsonPropertyName("query")] public string Query { get; set; } = string.Empty; [JsonPropertyName("variables")] public Dictionary? Variables { get; set; } } private class GraphQLResponse { [JsonPropertyName("data")] public T? Data { get; set; } [JsonPropertyName("errors")] public List? Errors { get; set; } } private class GraphQLError { [JsonPropertyName("message")] public string Message { get; set; } = string.Empty; } private class GetDecksResponseData { [JsonPropertyName("decks")] public List? Decks { get; set; } } private class GetDeckResponseData { [JsonPropertyName("deck")] public UserDeck? Deck { get; set; } } private class SaveDeckResponseData { [JsonPropertyName("saveDeck")] public UserDeck? SaveDeck { get; set; } } private class DeleteDeckResponseData { [JsonPropertyName("deleteDeck")] public bool DeleteDeck { get; set; } } }