package util

import (
	"fmt"
	"time"

	perrors "github.com/pingcap/errors"
)

// IsRealError checks if the given error is a real error.
// If err is the type of `RequeueAfterError` but its Caused is nil,
// it means that the given error is not an error, actually.
func IsRealError(err error) bool {
	if err == nil {
		return false
	}

	if requeueErr, ok := err.(*RequeueAfterError); ok && requeueErr.Caused == nil {
		return false
	}

	return true
}

type RequeueAfterError struct {
	Caused error
	// RequeueAfter is the duration after which the request should be enqueued again.
	RequeueAfter time.Duration
}

func New(requeueAfter time.Duration, cause error) *RequeueAfterError {
	return &RequeueAfterError{
		Caused:       cause,
		RequeueAfter: requeueAfter,
	}
}

func (e *RequeueAfterError) Cause() error {
	return e.Caused
}

func (e *RequeueAfterError) Error() string {
	if e.Caused == nil {
		return fmt.Sprintf("requeue in %s", e.RequeueAfter)
	}

	return fmt.Sprintf("requeue in %s due to %s", e.RequeueAfter, perrors.Cause(e.Caused))
}

// MaxBodySize is the maximum number of bytes to retain from an HTTP error response body.
const MaxBodySize = 4 * 1024

// StatusError is returned when HTTP APIs respond with non-2xx status codes.
// Body may contain sensitive data and should only be used for internal logging/debugging.
type StatusError struct {
	StatusCode int
	Status     string
	Body       string
}

func (e *StatusError) Error() string {
	if e == nil {
		return "<nil>"
	}
	if e.Status != "" {
		return fmt.Sprintf("http request failed: status=%d %s", e.StatusCode, e.Status)
	}
	return fmt.Sprintf("http request failed: status=%d", e.StatusCode)
}

// TruncatedBody returns up to max bytes of the response body.
func (e *StatusError) TruncatedBody(max int) string {
	if e == nil || max <= 0 {
		return ""
	}
	if len(e.Body) <= max {
		return e.Body
	}
	return e.Body[:max]
}

// TruncateBody truncates a byte slice to MaxBodySize and returns it as a string.
func TruncateBody(body []byte) string {
	if len(body) > MaxBodySize {
		body = body[:MaxBodySize]
	}
	return string(body)
}
