using System.Net.Http.Json; using System.Text.Json.Serialization; using Microsoft.JSInterop; namespace Web.Services; public class AuthService { private readonly string _graphqlEndpoint; private readonly HttpClient _http; private readonly IJSRuntime? _jsRuntime; private readonly NetworkStatusService _networkStatus; public AuthService(HttpClient http, NetworkStatusService networkStatus, IServiceProvider serviceProvider) { _http = http; _networkStatus = networkStatus; _jsRuntime = serviceProvider.GetService(typeof(IJSRuntime)) as IJSRuntime; _graphqlEndpoint = http.BaseAddress != null && http.BaseAddress.Port == 5256 ? "/graphql" : "http://localhost:5256/graphql"; } public bool IsLoggedIn => !string.IsNullOrEmpty(AuthToken); public string? CurrentUsername { get; private set; } public string? AuthToken { get; private set; } public event Action? AuthStateChanged; public async Task InitializeAsync() { if (_jsRuntime != null) try { var storedToken = await _jsRuntime.InvokeAsync("localStorage.getItem", "chrono_auth_token"); var storedUser = await _jsRuntime.InvokeAsync("localStorage.getItem", "chrono_auth_user"); if (!string.IsNullOrEmpty(storedToken) && !string.IsNullOrEmpty(storedUser)) { AuthToken = storedToken; CurrentUsername = storedUser; AuthStateChanged?.Invoke(); } } catch { // LocalStorage not available in testing or SSR } } public async Task LoginAsync(string username, string password) { if (!_networkStatus.IsOnline) return new AuthResult { Success = false, Message = "Cannot login while offline." }; try { var request = new GraphQLRequest { Query = @"mutation Login($username: String!, $password: String!) { login(username: $username, password: $password) { success token username message } }", Variables = new Dictionary { ["username"] = username, ["password"] = password } }; var response = await SendGraphQLAsync(request); var payload = response?.Data?.Login; if (payload != null && payload.Success && !string.IsNullOrEmpty(payload.Token)) { AuthToken = payload.Token; CurrentUsername = payload.Username ?? username; if (_jsRuntime != null) try { await _jsRuntime.InvokeVoidAsync("localStorage.setItem", "chrono_auth_token", AuthToken); await _jsRuntime.InvokeVoidAsync("localStorage.setItem", "chrono_auth_user", CurrentUsername); } catch { } AuthStateChanged?.Invoke(); return new AuthResult { Success = true, Message = payload.Message ?? "Login successful" }; } return new AuthResult { Success = false, Message = payload?.Message ?? "Invalid username or password." }; } catch (Exception ex) { return new AuthResult { Success = false, Message = $"Login error: {ex.Message}" }; } } public async Task LogoutAsync() { AuthToken = null; CurrentUsername = null; if (_jsRuntime != null) try { await _jsRuntime.InvokeVoidAsync("localStorage.removeItem", "chrono_auth_token"); await _jsRuntime.InvokeVoidAsync("localStorage.removeItem", "chrono_auth_user"); } catch { } AuthStateChanged?.Invoke(); } public void SetAuth(string username, string token) { CurrentUsername = username; AuthToken = token; AuthStateChanged?.Invoke(); } private async Task?> SendGraphQLAsync(GraphQLRequest request) { try { var httpResponse = await _http.PostAsJsonAsync(_graphqlEndpoint, request); if (httpResponse.IsSuccessStatusCode) return await httpResponse.Content.ReadFromJsonAsync>(); if (_graphqlEndpoint == "/graphql") { var fallbackResponse = await _http.PostAsJsonAsync("http://localhost:5256/graphql", request); if (fallbackResponse.IsSuccessStatusCode) return await fallbackResponse.Content.ReadFromJsonAsync>(); } } catch { try { var fallbackResponse = await _http.PostAsJsonAsync("http://localhost:5256/graphql", request); if (fallbackResponse.IsSuccessStatusCode) return await fallbackResponse.Content.ReadFromJsonAsync>(); } catch { } } return null; } public class AuthResult { public bool Success { get; set; } public string Message { get; set; } = string.Empty; } 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 LoginResponseData { [JsonPropertyName("login")] public LoginPayloadData? Login { get; set; } } private class LoginPayloadData { [JsonPropertyName("success")] public bool Success { get; set; } [JsonPropertyName("token")] public string? Token { get; set; } [JsonPropertyName("username")] public string? Username { get; set; } [JsonPropertyName("message")] public string? Message { get; set; } } }