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;
}