// Filename: internal/errors/upstream_errors.go
package errors

import (
	"strings"
)

// TODO: [Future Evolution] This file establishes the new, granular error classification framework.
// The next step is to refactor the handleKeyUsageEvent method in APIKeyService to utilize these new
// classifiers and implement the corresponding actions:
//
// 1. On IsPermanentUpstreamError:
//    - Set mapping status to models.StatusBanned.
//    - Set the master APIKey's status to models.MasterStatusRevoked.
//    - This is a "one-strike, you're out" policy for definitively invalid keys.
//
// 2. On IsTemporaryUpstreamError:
//    - Increment mapping.ConsecutiveErrorCount.
//    - Check against the blacklist threshold to potentially set status to models.StatusDisabled.
//    - This is for recoverable errors that are the key's fault (e.g., quota limits).
//
// 3. On ALL other upstream errors (that are not Permanent or Temporary):
//    - These are treated as "Truly Ignorable" from the key's perspective (e.g., 503 Service Unavailable).
//    - Do NOT increment the error count. Only update LastUsedAt.
//    - This prevents good keys from being punished for upstream service instability.

// --- 1. Permanent Errors ---
// Errors that indicate the API Key itself is permanently invalid.
// Action: Ban mapping, Revoke Master Key.
var permanentErrorSubstrings = []string{
	"invalid api key",
	"api key not valid",
	"api key suspended",
	"API Key not found",
	"api key expired",
	"permission denied", // Often indicates the key lacks permissions for the target model/service.
	"permission_denied", // Catches the 'status' field in Google's JSON error, e.g., "status": "PERMISSION_DENIED".
	"service_disabled",  // Catches the 'reason' field for disabled APIs, e.g., "reason": "SERVICE_DISABLED".
	"api has not been used",
}

// --- 2. Temporary Errors ---
// Errors that are attributable to the key's state but are recoverable over time.
// Action: Increment consecutive error count, potentially disable the key.
var temporaryErrorSubstrings = []string{
	"quota",
	"limit reached",
	"insufficient",
	"billing",
	"exceeded",
	"too many requests",
}

// --- 3. Unretryable Request Errors ---
// Errors indicating a problem with the user's request, not the key. Retrying with a new key is pointless.
// Action: Abort the retry loop immediately in ProxyHandler.
var unretryableRequestErrorSubstrings = []string{
	"invalid content",
	"invalid argument",
	"malformed",
	"unsupported",
	"invalid model",
}

// --- 4. Ignorable Client/Network Errors ---
// Network-level errors, typically caused by the client disconnecting.
// Action: Ignore for logging and metrics purposes.
var clientNetworkErrorSubstrings = []string{
	"context canceled",
	"connection reset by peer",
	"broken pipe",
	"use of closed network connection",
	"request canceled",
	"invalid query parameters", // 参数解析错误，归类为客户端错误
}

// IsPermanentUpstreamError checks if an upstream error indicates the key is permanently invalid.
func IsPermanentUpstreamError(msg string) bool {
	return containsSubstring(msg, permanentErrorSubstrings)
}

// IsTemporaryUpstreamError checks if an upstream error is due to temporary, key-specific limits.
func IsTemporaryUpstreamError(msg string) bool {
	return containsSubstring(msg, temporaryErrorSubstrings)
}

// IsUnretryableRequestError checks if an upstream error is due to a malformed user request.
func IsUnretryableRequestError(msg string) bool {
	return containsSubstring(msg, unretryableRequestErrorSubstrings)
}

// IsClientNetworkError checks if an error is a common, ignorable client-side network issue.
func IsClientNetworkError(err error) bool {
	if err == nil {
		return false
	}
	return containsSubstring(err.Error(), clientNetworkErrorSubstrings)
}

// containsSubstring is a helper function to avoid code repetition.
func containsSubstring(s string, substrings []string) bool {
	if s == "" {
		return false
	}
	lowerS := strings.ToLower(s)
	for _, sub := range substrings {
		if strings.Contains(lowerS, sub) {
			return true
		}
	}
	return false
}
