using System.ComponentModel.DataAnnotations; using System.Text.Json.Serialization; namespace HCFS.SDK; /// /// Base exception for all HCFS SDK errors. /// public class HCFSException : Exception { /// /// Gets the error code associated with this exception. /// public string? ErrorCode { get; } /// /// Gets additional error details. /// public IReadOnlyDictionary? Details { get; } /// /// Gets the HTTP status code if applicable. /// public int? StatusCode { get; } /// /// Initializes a new instance of the class. /// /// The error message. public HCFSException(string message) : base(message) { } /// /// Initializes a new instance of the class. /// /// The error message. /// The error code. public HCFSException(string message, string errorCode) : base(message) { ErrorCode = errorCode; } /// /// Initializes a new instance of the class. /// /// The error message. /// The error code. /// Additional error details. /// HTTP status code. public HCFSException(string message, string? errorCode, IReadOnlyDictionary? details, int? statusCode) : base(message) { ErrorCode = errorCode; Details = details; StatusCode = statusCode; } /// /// Initializes a new instance of the class. /// /// The error message. /// The inner exception. public HCFSException(string message, Exception innerException) : base(message, innerException) { } /// /// Checks if this error should trigger a retry. /// /// True if the error is retryable. public virtual bool IsRetryable() { return StatusCode >= 500 || StatusCode == 429 || this is HCFSConnectionException || this is HCFSTimeoutException; } /// /// Checks if this error is temporary. /// /// True if the error is temporary. public virtual bool IsTemporary() { return StatusCode == 429 || StatusCode == 502 || StatusCode == 503 || StatusCode == 504 || this is HCFSTimeoutException || this is HCFSConnectionException; } } /// /// Thrown when connection to HCFS API fails. /// public class HCFSConnectionException : HCFSException { /// /// Initializes a new instance of the class. /// /// The error message. public HCFSConnectionException(string message = "Failed to connect to HCFS API") : base(message, "CONNECTION_FAILED") { } /// /// Initializes a new instance of the class. /// /// The error message. /// The inner exception. public HCFSConnectionException(string message, Exception innerException) : base(message, innerException) { } } /// /// Thrown when authentication fails. /// public class HCFSAuthenticationException : HCFSException { /// /// Initializes a new instance of the class. /// /// The error message. public HCFSAuthenticationException(string message = "Authentication failed") : base(message, "AUTH_FAILED", null, 401) { } } /// /// Thrown when user lacks permissions for an operation. /// public class HCFSAuthorizationException : HCFSException { /// /// Initializes a new instance of the class. /// /// The error message. public HCFSAuthorizationException(string message = "Insufficient permissions") : base(message, "INSUFFICIENT_PERMISSIONS", null, 403) { } } /// /// Thrown when a requested resource is not found. /// public class HCFSNotFoundException : HCFSException { /// /// Gets the type of resource that was not found. /// public string? ResourceType { get; } /// /// Gets the ID of the resource that was not found. /// public string? ResourceId { get; } /// /// Initializes a new instance of the class. /// /// The error message. public HCFSNotFoundException(string message = "Resource not found") : base(message, "NOT_FOUND", null, 404) { } /// /// Initializes a new instance of the class. /// /// The error message. /// The type of resource. /// The resource ID. public HCFSNotFoundException(string message, string? resourceType, string? resourceId) : base(message, "NOT_FOUND", null, 404) { ResourceType = resourceType; ResourceId = resourceId; } /// /// Gets the error message with resource details. /// public override string Message { get { var message = base.Message; if (!string.IsNullOrEmpty(ResourceType)) { message += $" (type: {ResourceType})"; } if (!string.IsNullOrEmpty(ResourceId)) { message += $" (id: {ResourceId})"; } return message; } } } /// /// Thrown when request validation fails. /// public class HCFSValidationException : ValidationException { /// /// Gets the validation error details. /// public IReadOnlyList? ValidationErrors { get; } /// /// Initializes a new instance of the class. /// /// The error message. public HCFSValidationException(string message = "Request validation failed") : base(message) { } /// /// Initializes a new instance of the class. /// /// The error message. /// The validation error details. public HCFSValidationException(string message, IReadOnlyList validationErrors) : base(message) { ValidationErrors = validationErrors; } /// /// Gets the error message with validation details. /// public override string Message { get { var message = base.Message; if (ValidationErrors != null && ValidationErrors.Count > 0) { message += $" ({ValidationErrors.Count} validation issues)"; } return message; } } } /// /// Validation error detail. /// public record ValidationErrorDetail { /// /// Gets the field name that failed validation. /// [JsonPropertyName("field")] public string? Field { get; init; } /// /// Gets the validation error message. /// [JsonPropertyName("message")] public string Message { get; init; } = string.Empty; /// /// Gets the validation error code. /// [JsonPropertyName("code")] public string? Code { get; init; } } /// /// Thrown when rate limit is exceeded. /// public class HCFSRateLimitException : HCFSException { /// /// Gets the time to wait before retrying. /// public double? RetryAfterSeconds { get; } /// /// Initializes a new instance of the class. /// /// The error message. public HCFSRateLimitException(string message = "Rate limit exceeded") : base(message, "RATE_LIMIT_EXCEEDED", null, 429) { } /// /// Initializes a new instance of the class. /// /// The error message. /// Seconds to wait before retrying. public HCFSRateLimitException(string message, double? retryAfterSeconds) : base(BuildMessage(message, retryAfterSeconds), "RATE_LIMIT_EXCEEDED", null, 429) { RetryAfterSeconds = retryAfterSeconds; } private static string BuildMessage(string message, double? retryAfterSeconds) { if (retryAfterSeconds.HasValue) { return $"{message}. Retry after {retryAfterSeconds.Value} seconds"; } return message; } } /// /// Thrown for server-side errors (5xx status codes). /// public class HCFSServerException : HCFSException { /// /// Initializes a new instance of the class. /// /// The error message. /// The HTTP status code. public HCFSServerException(string message = "Internal server error", int statusCode = 500) : base(message, "SERVER_ERROR", null, statusCode) { } /// /// Gets the error message with status code. /// public override string Message => $"Server error (HTTP {StatusCode}): {base.Message}"; } /// /// Thrown when a request times out. /// public class HCFSTimeoutException : HCFSException { /// /// Gets the timeout duration that was exceeded. /// public TimeSpan? Timeout { get; } /// /// Initializes a new instance of the class. /// /// The error message. public HCFSTimeoutException(string message = "Request timed out") : base(message, "TIMEOUT") { } /// /// Initializes a new instance of the class. /// /// The error message. /// The timeout duration. public HCFSTimeoutException(string message, TimeSpan timeout) : base($"{message} after {timeout.TotalMilliseconds}ms", "TIMEOUT") { Timeout = timeout; } } /// /// Thrown for cache-related errors. /// public class HCFSCacheException : HCFSException { /// /// Gets the cache operation that failed. /// public string? Operation { get; } /// /// Initializes a new instance of the class. /// /// The error message. public HCFSCacheException(string message = "Cache operation failed") : base(message, "CACHE_ERROR") { } /// /// Initializes a new instance of the class. /// /// The error message. /// The cache operation. public HCFSCacheException(string message, string operation) : base(message, "CACHE_ERROR") { Operation = operation; } /// /// Gets the error message with operation details. /// public override string Message { get { if (!string.IsNullOrEmpty(Operation)) { return $"Cache error during {Operation}: {base.Message}"; } return $"Cache error: {base.Message}"; } } } /// /// Thrown for batch operation errors. /// public class HCFSBatchException : HCFSException { /// /// Gets the items that failed in the batch operation. /// public IReadOnlyList? FailedItems { get; } /// /// Initializes a new instance of the class. /// /// The error message. public HCFSBatchException(string message = "Batch operation failed") : base(message, "BATCH_ERROR") { } /// /// Initializes a new instance of the class. /// /// The error message. /// The failed items. public HCFSBatchException(string message, IReadOnlyList failedItems) : base(message, "BATCH_ERROR") { FailedItems = failedItems; } /// /// Gets the error message with failure details. /// public override string Message { get { var message = base.Message; if (FailedItems != null && FailedItems.Count > 0) { message += $" ({FailedItems.Count} failed items)"; } return message; } } } /// /// Batch operation failure item. /// public record BatchFailureItem { /// /// Gets the index of the failed item. /// [JsonPropertyName("index")] public int Index { get; init; } /// /// Gets the error message for the failed item. /// [JsonPropertyName("error")] public string Error { get; init; } = string.Empty; /// /// Gets the item data that failed. /// [JsonPropertyName("item")] public object? Item { get; init; } } /// /// Thrown for search operation errors. /// public class HCFSSearchException : HCFSException { /// /// Gets the search query that failed. /// public string? Query { get; } /// /// Gets the search type that was used. /// public string? SearchType { get; } /// /// Initializes a new instance of the class. /// /// The error message. public HCFSSearchException(string message = "Search failed") : base(message, "SEARCH_ERROR") { } /// /// Initializes a new instance of the class. /// /// The error message. /// The search query. /// The search type. public HCFSSearchException(string message, string? query, string? searchType) : base(message, "SEARCH_ERROR") { Query = query; SearchType = searchType; } /// /// Gets the error message with search details. /// public override string Message { get { var message = $"Search error: {base.Message}"; if (!string.IsNullOrEmpty(SearchType)) { message += $" (type: {SearchType})"; } if (!string.IsNullOrEmpty(Query)) { message += $" (query: '{Query}')"; } return message; } } } /// /// Thrown for streaming/WebSocket errors. /// public class HCFSStreamException : HCFSException { /// /// Initializes a new instance of the class. /// /// The error message. public HCFSStreamException(string message = "Stream operation failed") : base(message, "STREAM_ERROR") { } /// /// Initializes a new instance of the class. /// /// The error message. /// The inner exception. public HCFSStreamException(string message, Exception innerException) : base(message, innerException) { } } /// /// Error response from the API. /// internal record ApiErrorResponse { [JsonPropertyName("error")] public string? Error { get; init; } [JsonPropertyName("message")] public string? Message { get; init; } [JsonPropertyName("details")] public Dictionary? Details { get; init; } }