namespace TaxBaik.Web.Services; using System.Net.Http; using System.Net.Http.Json; using TaxBaik.Application.DTOs; using TaxBaik.Domain.Entities; /// /// Client API Client for Admin Blazor /// SOLID: Single Responsibility - Client API calls only /// public interface IClientBrowserClient { Task<(IEnumerable Items, int Total)> GetPagedAsync( int page = 1, int pageSize = 20, string? status = null, string? search = null, CancellationToken ct = default); Task GetByIdAsync(int id, CancellationToken ct = default); Task CreateAsync(CreateClientDto dto, CancellationToken ct = default); Task UpdateAsync(int id, CreateClientDto dto, CancellationToken ct = default); Task DeleteAsync(int id, CancellationToken ct = default); } public class ClientBrowserClient : IClientBrowserClient { private readonly HttpClient _http; private readonly ILogger _logger; private readonly ITokenStore _tokenStore; public ClientBrowserClient(HttpClient http, ILogger logger, ITokenStore tokenStore) { _http = http; _logger = logger; _tokenStore = tokenStore; } private void EnsureAuthHeader() { if (!string.IsNullOrEmpty(_tokenStore.AccessToken) && !_http.DefaultRequestHeaders.Contains("Authorization")) { _http.DefaultRequestHeaders.Authorization = new("Bearer", _tokenStore.AccessToken); } } public async Task<(IEnumerable Items, int Total)> GetPagedAsync( int page = 1, int pageSize = 20, string? status = null, string? search = null, CancellationToken ct = default) { try { EnsureAuthHeader(); var query = $"client?page={page}&pageSize={pageSize}"; if (!string.IsNullOrEmpty(status)) query += $"&status={status}"; if (!string.IsNullOrEmpty(search)) query += $"&search={Uri.EscapeDataString(search)}"; var result = await _http.GetFromJsonAsync(query, cancellationToken: ct); return result != null ? (result.Data, result.Total) : ([], 0); } catch (HttpRequestException ex) { _logger.LogError(ex, "Failed to fetch clients"); throw; } } public async Task GetByIdAsync(int id, CancellationToken ct = default) { try { EnsureAuthHeader(); return await _http.GetFromJsonAsync($"client/{id}", cancellationToken: ct); } catch (HttpRequestException ex) { _logger.LogError(ex, "Failed to fetch client {ClientId}", id); throw; } } public async Task CreateAsync(CreateClientDto dto, CancellationToken ct = default) { try { EnsureAuthHeader(); var response = await _http.PostAsJsonAsync("client", dto, cancellationToken: ct); if (!response.IsSuccessStatusCode) return null; var content = await response.Content.ReadAsStringAsync(ct); return System.Text.Json.JsonSerializer.Deserialize( content, new System.Text.Json.JsonSerializerOptions { PropertyNameCaseInsensitive = true }); } catch (HttpRequestException ex) { _logger.LogError(ex, "Failed to create client"); throw; } } public async Task UpdateAsync(int id, CreateClientDto dto, CancellationToken ct = default) { try { EnsureAuthHeader(); var response = await _http.PutAsJsonAsync($"client/{id}", dto, cancellationToken: ct); if (!response.IsSuccessStatusCode) return null; var content = await response.Content.ReadAsStringAsync(ct); return System.Text.Json.JsonSerializer.Deserialize( content, new System.Text.Json.JsonSerializerOptions { PropertyNameCaseInsensitive = true }); } catch (HttpRequestException ex) { _logger.LogError(ex, "Failed to update client {ClientId}", id); throw; } } public async Task DeleteAsync(int id, CancellationToken ct = default) { try { EnsureAuthHeader(); var response = await _http.DeleteAsync($"client/{id}", cancellationToken: ct); return response.IsSuccessStatusCode; } catch (HttpRequestException ex) { _logger.LogError(ex, "Failed to delete client {ClientId}", id); throw; } } private class ClientPagedResponse { public List Data { get; set; } = []; public int Total { get; set; } } }