namespace TaxBaik.Web.Services.AdminClients; using System.Text.Json; using TaxBaik.Domain.Entities; public interface IContractBrowserClient { Task> GetAllAsync(CancellationToken ct = default); Task GetByIdAsync(int id, CancellationToken ct = default); Task> GetByClientIdAsync(int clientId, CancellationToken ct = default); Task> GetActiveContractsAsync(CancellationToken ct = default); Task> GetExpiringContractsAsync(int daysAhead = 30, CancellationToken ct = default); Task GetMonthlyRecurringRevenueAsync(CancellationToken ct = default); Task CreateAsync(int clientId, string contractNumber, string serviceType, DateTime startDate, decimal? monthlyFee = null, decimal? totalAmount = null, CancellationToken ct = default); Task DeleteAsync(int id, CancellationToken ct = default); } public class ContractBrowserClient(HttpClient httpClient, ITokenStore tokenStore, ILogger logger) : IContractBrowserClient { private const string BaseUrl = "/api/contract"; private void EnsureAuthHeader() { if (!string.IsNullOrEmpty(tokenStore.AccessToken)) httpClient.DefaultRequestHeaders.Authorization = new("Bearer", tokenStore.AccessToken); else httpClient.DefaultRequestHeaders.Authorization = null; } public async Task> GetAllAsync(CancellationToken ct = default) { try { EnsureAuthHeader(); return await httpClient.GetFromJsonAsync>($"{BaseUrl}", ct) ?? []; } catch (Exception ex) { logger.LogError(ex, "Failed to get contracts"); return []; } } public async Task GetByIdAsync(int id, CancellationToken ct = default) { try { EnsureAuthHeader(); return await httpClient.GetFromJsonAsync($"{BaseUrl}/{id}", ct); } catch (Exception ex) { logger.LogError(ex, "Failed to get contract {Id}", id); return null; } } public async Task> GetByClientIdAsync(int clientId, CancellationToken ct = default) { try { EnsureAuthHeader(); return await httpClient.GetFromJsonAsync>($"{BaseUrl}/client/{clientId}", ct) ?? []; } catch (Exception ex) { logger.LogError(ex, "Failed to get contracts for client {ClientId}", clientId); return []; } } public async Task> GetActiveContractsAsync(CancellationToken ct = default) { try { EnsureAuthHeader(); var response = await httpClient.GetFromJsonAsync($"{BaseUrl}/active", ct); if (response.TryGetProperty("data", out var data)) return System.Text.Json.JsonSerializer.Deserialize>(data.GetRawText()) ?? []; return []; } catch (Exception ex) { logger.LogError(ex, "Failed to get active contracts"); return []; } } public async Task> GetExpiringContractsAsync(int daysAhead = 30, CancellationToken ct = default) { try { EnsureAuthHeader(); var response = await httpClient.GetFromJsonAsync($"{BaseUrl}/expiring?daysAhead={daysAhead}", ct); if (response.TryGetProperty("data", out var data)) return System.Text.Json.JsonSerializer.Deserialize>(data.GetRawText()) ?? []; return []; } catch (Exception ex) { logger.LogError(ex, "Failed to get expiring contracts"); return []; } } public async Task GetMonthlyRecurringRevenueAsync(CancellationToken ct = default) { try { EnsureAuthHeader(); var response = await httpClient.GetFromJsonAsync($"{BaseUrl}/mrr", ct); if (response.TryGetProperty("mrr", out var mrrValue)) return System.Text.Json.JsonSerializer.Deserialize(mrrValue.GetRawText()); return 0; } catch (Exception ex) { logger.LogError(ex, "Failed to get MRR"); return 0; } } public async Task CreateAsync(int clientId, string contractNumber, string serviceType, DateTime startDate, decimal? monthlyFee = null, decimal? totalAmount = null, CancellationToken ct = default) { try { EnsureAuthHeader(); var request = new { clientId, contractNumber, serviceType, startDate, monthlyFee, totalAmount }; var response = await httpClient.PostAsJsonAsync(BaseUrl, request, ct); response.EnsureSuccessStatusCode(); var result = await response.Content.ReadFromJsonAsync(cancellationToken: ct); return result.TryGetProperty("id", out var idProp) ? idProp.GetInt32() : 0; } catch (Exception ex) { logger.LogError(ex, "Failed to create contract"); return 0; } } public async Task DeleteAsync(int id, CancellationToken ct = default) { try { EnsureAuthHeader(); var response = await httpClient.DeleteAsync($"{BaseUrl}/{id}", ct); response.EnsureSuccessStatusCode(); } catch (Exception ex) { logger.LogError(ex, "Failed to delete contract {Id}", id); } } }