using System.Text; namespace MarketAlly.AIPlugin.ClaudeCode; /// /// HTTP client interface with built-in rate limiting awareness /// public interface IRateLimitAwareHttpClient { /// /// Sends a GET request with rate limit handling /// /// API endpoint /// Cancellation token /// HTTP response with rate limit info Task GetAsync(string endpoint, CancellationToken cancellationToken = default); /// /// Sends a POST request with rate limit handling /// /// API endpoint /// Request content /// Cancellation token /// HTTP response with rate limit info Task PostAsync(string endpoint, HttpContent? content = null, CancellationToken cancellationToken = default); /// /// Sends a POST request with JSON content /// /// API endpoint /// Data to serialize as JSON /// Cancellation token /// HTTP response with rate limit info Task PostAsJsonAsync(string endpoint, T data, CancellationToken cancellationToken = default); /// /// Sends a PUT request with rate limit handling /// /// API endpoint /// Request content /// Cancellation token /// HTTP response with rate limit info Task PutAsync(string endpoint, HttpContent? content = null, CancellationToken cancellationToken = default); /// /// Sends a DELETE request with rate limit handling /// /// API endpoint /// Cancellation token /// HTTP response with rate limit info Task DeleteAsync(string endpoint, CancellationToken cancellationToken = default); /// /// Gets current rate limit status /// /// Cancellation token /// Current rate limit information Task GetRateLimitStatusAsync(CancellationToken cancellationToken = default); /// /// Checks if a request can be made without hitting rate limits /// /// Optional endpoint to check /// True if request can be made Task CanMakeRequestAsync(string? endpoint = null); /// /// Waits for rate limit reset if necessary /// /// Maximum time to wait /// Cancellation token /// True if reset occurred, false if timeout Task WaitForRateLimitResetAsync(TimeSpan? maxWaitTime = null, CancellationToken cancellationToken = default); /// /// Configures the HTTP client with authentication and base settings /// /// Base URL for the API /// API key for authentication /// Optional tenant ID void Configure(string baseUrl, string apiKey, string? tenantId = null); } /// /// HTTP response with rate limit information /// public class RateLimitAwareResponse { public bool IsSuccessStatusCode { get; set; } public int StatusCode { get; set; } public string? Content { get; set; } public RateLimitInfo? RateLimitInfo { get; set; } public Dictionary Headers { get; set; } = new(); public string? ErrorMessage { get; set; } public bool IsRateLimited { get; set; } public TimeSpan? RetryAfter { get; set; } /// /// Deserializes the response content as JSON /// /// Type to deserialize to /// Deserialized object public T? DeserializeJson() { if (string.IsNullOrEmpty(Content)) return default; try { return System.Text.Json.JsonSerializer.Deserialize(Content, new System.Text.Json.JsonSerializerOptions { PropertyNameCaseInsensitive = true }); } catch { return default; } } /// /// Ensures the response was successful, throwing an exception if not /// /// This response for chaining /// Thrown if the request was not successful public RateLimitAwareResponse EnsureSuccessStatusCode() { if (!IsSuccessStatusCode) { var message = !string.IsNullOrEmpty(ErrorMessage) ? ErrorMessage : $"HTTP request failed with status code {StatusCode}"; if (IsRateLimited) { throw new RateLimitExceededException(message, RateLimitInfo, RetryAfter); } throw new HttpRequestException(message); } return this; } /// /// Creates a successful response /// /// Response content /// Rate limit information /// Successful response public static RateLimitAwareResponse Success(string content, RateLimitInfo? rateLimitInfo = null) { return new RateLimitAwareResponse { IsSuccessStatusCode = true, StatusCode = 200, Content = content, RateLimitInfo = rateLimitInfo }; } /// /// Creates an error response /// /// HTTP status code /// Error message /// Rate limit information /// Error response public static RateLimitAwareResponse Error(int statusCode, string errorMessage, RateLimitInfo? rateLimitInfo = null) { return new RateLimitAwareResponse { IsSuccessStatusCode = false, StatusCode = statusCode, ErrorMessage = errorMessage, RateLimitInfo = rateLimitInfo, IsRateLimited = statusCode == 429 }; } } /// /// Exception thrown when rate limits are exceeded /// public class RateLimitExceededException : Exception { public RateLimitInfo? RateLimitInfo { get; } public TimeSpan? RetryAfter { get; } public RateLimitExceededException(string message, RateLimitInfo? rateLimitInfo = null, TimeSpan? retryAfter = null) : base(message) { RateLimitInfo = rateLimitInfo; RetryAfter = retryAfter; } public RateLimitExceededException(string message, Exception innerException, RateLimitInfo? rateLimitInfo = null, TimeSpan? retryAfter = null) : base(message, innerException) { RateLimitInfo = rateLimitInfo; RetryAfter = retryAfter; } } /// /// HTTP client configuration options /// public class HttpClientOptions { public string BaseUrl { get; set; } = string.Empty; public string ApiKey { get; set; } = string.Empty; public string? TenantId { get; set; } public TimeSpan Timeout { get; set; } = TimeSpan.FromMinutes(5); public int MaxRetries { get; set; } = 3; public TimeSpan BaseRetryDelay { get; set; } = TimeSpan.FromSeconds(1); public double BackoffMultiplier { get; set; } = 2.0; public Dictionary DefaultHeaders { get; set; } = new(); public bool EnableDetailedLogging { get; set; } = false; }