// Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.

package retry

import (
	"errors"
	"net/http"
	"strconv"
	"time"
)

// BackoffStrategy defines the parameters for exponential backoff. This can be
// used to drive a retry loop for example.
type BackoffStrategy struct {
	InitialInterval int
	MaxInterval     int
	Exponent        float64
	MaxElapsedTime  int
}

// Config configures a retry policy.
type Config struct {
	// Strategy sets the algorithm to use for a retry loop. It can be one of:
	//   - "backoff": retry with exponential backoff and random jitter.
	//   - "none" or "": disables retries.
	Strategy              string
	Backoff               *BackoffStrategy
	RetryConnectionErrors bool
}

// PermanentError is an error that signals that some operation has terminally
// failed and should not be retried.
type PermanentError struct {
	cause error
}

// Permanent creates a PermanentError that signals to a retry loop that it
// should stop retrying an operation and return the underlying error.
func Permanent(cause error) error {
	if IsPermanentError(cause) {
		return cause
	}

	return &PermanentError{
		cause: cause,
	}
}

func (e *PermanentError) Error() string {
	return e.cause.Error()
}

func (e *PermanentError) Unwrap() error {
	return e.cause
}

// TemporaryError represents a retryable error and signals to a retry loop that
// an operation may be retried with an optional wait interval.
type TemporaryError struct {
	wait    time.Duration
	message string
}

// Temporary creates a TemporaryError that signals to a retry loop that an
// operation can be retried. The error may also carry details about how long to
// wait before retrying. This wait interval may be used to override the retry
// policy in use.
func Temporary(message string) error {
	return &TemporaryError{
		message: message,
	}
}

// TemporaryFromResponse creates a TemporaryError similar to Temporary but
// additionally parses the Retry-After header from a response to determine the
// wait interval before the next retry attempt.
func TemporaryFromResponse(message string, res *http.Response) error {
	return &TemporaryError{
		wait:    retryIntervalFromResponse(res),
		message: message,
	}
}

func (e *TemporaryError) Error() string {
	return e.message
}

// RetryAfter returns the time to wait before retrying the request. The zero
// value should be interpreted by retry loops to mean they should fallback on
// their default policy whether expenonential, constant backoff or something
// else. It does not mean that an operation should be retried immediately.
func (e *TemporaryError) RetryAfter() time.Duration {
	return e.wait
}

func retryIntervalFromResponse(res *http.Response) time.Duration {
	if res == nil {
		return 0
	}

	retryVal := res.Header.Get("retry-after")
	if retryVal == "" {
		return 0
	}

	parsedNumber, err := strconv.ParseInt(retryVal, 10, 64)
	if err == nil {
		if parsedNumber < 0 {
			return 0
		} else {
			return time.Duration(parsedNumber) * time.Second
		}
	}

	parsedDate, err := time.Parse(time.RFC1123, retryVal)
	if err == nil {
		delta := parsedDate.Sub(time.Now())
		if delta < 0 {
			return 0
		} else {
			return delta
		}
	}

	return 0
}

// IsPermanentError returns true if an error value is or contains a
// PermanentError in its chain of errors.
func IsPermanentError(err error) bool {
	if err == nil {
		return false
	}

	var pe *PermanentError
	return errors.As(err, &pe)
}

// IsTemporaryError returns true if an error value is or contains a
// TemporaryError in its chain of errors.
func IsTemporaryError(err error) bool {
	if err == nil {
		return false
	}

	var pe *TemporaryError
	return errors.As(err, &pe)
}
