package client

import (
	"context"
	"errors"
	"math/rand"
	"net"
	"net/http"
	"time"

	"github.com/hermanschaaf/hackernews"
)

// RetryOnError will run the given resolver function and retry on rate limit exceeded errors
// or other retryable errors (like internal server errors) after waiting some amount of time.
func (c *Client) RetryOnError(ctx context.Context, tableName string, f func() error) error {
	retries := 0
	for err := f(); err != nil; err = f() {
		if isRetryable(err) {
			retryAfter := time.Duration(rand.Float64() * float64(c.backoff))
			retries++
			c.logger.Info().Str("table", tableName).Msgf("Got retryable error (%v), retrying in %.2fs (%d/%d)", err, retryAfter.Seconds(), retries, c.maxRetries)
			select {
			case <-ctx.Done():
				return ctx.Err()
			case <-time.After(retryAfter):
				continue
			}
		}
		return err
	}
	return nil
}

func isRetryable(err error) bool {
	var httpErr hackernews.HTTPError
	if errors.As(err, &httpErr) {
		return httpErr.Code >= http.StatusInternalServerError || httpErr.Code == http.StatusTooManyRequests
	}
	var opErr *net.OpError
	return errors.As(err, &opErr)
}
