package laozhang

import (
	"errors"
	"fmt"
)

// Sentinel errors for common error types
var (
	// ErrInvalidConfig indicates configuration validation failed
	ErrInvalidConfig = errors.New("invalid configuration")

	// ErrRateLimit indicates rate limit exceeded (HTTP 429)
	ErrRateLimit = errors.New("rate limit exceeded")

	// ErrServerError indicates server-side error (HTTP 5xx)
	ErrServerError = errors.New("server error")

	// ErrInvalidRequest indicates client error (HTTP 4xx except 429, 401)
	ErrInvalidRequest = errors.New("invalid request")

	// ErrTimeout indicates request timeout
	ErrTimeout = errors.New("request timeout")

	// ErrUnauthorized indicates authentication failed (HTTP 401)
	ErrUnauthorized = errors.New("unauthorized")
)

// APIError represents an error returned by the LaoZhang API
type APIError struct {
	StatusCode int               // HTTP status code
	Message    string            // Human-readable error message
	Type       string            // Error type from API response
	Code       string            // Error code from API response
	err        error             // Underlying error for wrapping
	Details    map[string]string // Additional error details
}

// Error implements the error interface
func (e *APIError) Error() string {
	if e.Code != "" {
		return fmt.Sprintf("laozhang api error (status %d): [%s] %s", e.StatusCode, e.Code, e.Message)
	}
	return fmt.Sprintf("laozhang api error (status %d): %s", e.StatusCode, e.Message)
}

// Unwrap implements the errors.Unwrap interface for error chains
func (e *APIError) Unwrap() error {
	return e.err
}

// NewAPIError creates a new APIError with the given parameters
func NewAPIError(statusCode int, message, errorType, code string, underlying error) *APIError {
	return &APIError{
		StatusCode: statusCode,
		Message:    message,
		Type:       errorType,
		Code:       code,
		err:        underlying,
		Details:    make(map[string]string),
	}
}

// WithDetails adds additional details to the error
func (e *APIError) WithDetails(key, value string) *APIError {
	if e.Details == nil {
		e.Details = make(map[string]string)
	}
	e.Details[key] = value
	return e
}

// IsRateLimitError checks if the error is a rate limit error
func IsRateLimitError(err error) bool {
	if err == nil {
		return false
	}

	// Check for sentinel error
	if errors.Is(err, ErrRateLimit) {
		return true
	}

	// Check for APIError with 429 status
	var apiErr *APIError
	if errors.As(err, &apiErr) {
		return apiErr.StatusCode == 429
	}

	return false
}

// IsServerError checks if the error is a server error (5xx)
func IsServerError(err error) bool {
	if err == nil {
		return false
	}

	// Check for sentinel error
	if errors.Is(err, ErrServerError) {
		return true
	}

	// Check for APIError with 5xx status
	var apiErr *APIError
	if errors.As(err, &apiErr) {
		return apiErr.StatusCode >= 500 && apiErr.StatusCode < 600
	}

	return false
}

// IsRetryableError checks if the error should trigger a retry
// Retryable errors include: rate limits (429), server errors (5xx), and timeouts
func IsRetryableError(err error) bool {
	if err == nil {
		return false
	}

	// Check sentinel errors
	if errors.Is(err, ErrRateLimit) || errors.Is(err, ErrServerError) || errors.Is(err, ErrTimeout) {
		return true
	}

	// Check APIError status codes
	var apiErr *APIError
	if errors.As(err, &apiErr) {
		// Retry on rate limits and server errors
		return apiErr.StatusCode == 429 || (apiErr.StatusCode >= 500 && apiErr.StatusCode < 600)
	}

	return false
}

// IsUnauthorizedError checks if the error is an unauthorized error (401)
func IsUnauthorizedError(err error) bool {
	if err == nil {
		return false
	}

	// Check for sentinel error
	if errors.Is(err, ErrUnauthorized) {
		return true
	}

	// Check for APIError with 401 status
	var apiErr *APIError
	if errors.As(err, &apiErr) {
		return apiErr.StatusCode == 401
	}

	return false
}

// IsTimeoutError checks if the error is a timeout error
func IsTimeoutError(err error) bool {
	if err == nil {
		return false
	}

	return errors.Is(err, ErrTimeout)
}

// IsInvalidRequestError checks if the error is a client error (4xx except 429, 401)
func IsInvalidRequestError(err error) bool {
	if err == nil {
		return false
	}

	// Check for sentinel error
	if errors.Is(err, ErrInvalidRequest) {
		return true
	}

	// Check for APIError with 4xx status (excluding 429 and 401)
	var apiErr *APIError
	if errors.As(err, &apiErr) {
		return apiErr.StatusCode >= 400 && apiErr.StatusCode < 500 &&
			apiErr.StatusCode != 429 && apiErr.StatusCode != 401
	}

	return false
}

// classifyHTTPError classifies an HTTP status code into a domain error type
func classifyHTTPError(statusCode int) error {
	switch {
	case statusCode == 401:
		return ErrUnauthorized
	case statusCode == 429:
		return ErrRateLimit
	case statusCode >= 400 && statusCode < 500:
		return ErrInvalidRequest
	case statusCode >= 500 && statusCode < 600:
		return ErrServerError
	default:
		return nil
	}
}
