package api

import (
	"context"
	"errors"
	"io"
	"net"
	"strings"
	"time"
)

func isRetryable(err error) bool {
	if err == nil {
		return false
	}
	var apiErr *APIError
	if errors.As(err, &apiErr) && (apiErr.Status == 503 || apiErr.Status == 429) {
		return true
	}
	if errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) {
		return true
	}
	var netErr net.Error
	if errors.As(err, &netErr) && netErr.Timeout() {
		return true
	}
	msg := strings.ToLower(err.Error())
	return strings.Contains(msg, "eof") ||
		strings.Contains(msg, "connection reset") ||
		strings.Contains(msg, "broken pipe") ||
		strings.Contains(msg, "timeout") ||
		strings.Contains(msg, "connection refused") ||
		strings.Contains(msg, "no such host")
}

func retryPause(attempt int) {
	d := time.Duration(1<<attempt) * time.Second
	if d > 30*time.Second {
		d = 30 * time.Second
	}
	time.Sleep(d)
}

func doWithRetry(ctx context.Context, max int, fn func() error) error {
	var last error
	for attempt := 0; attempt < max; attempt++ {
		if err := ctx.Err(); err != nil {
			return err
		}
		last = fn()
		if last == nil {
			return nil
		}
		if !isRetryable(last) {
			return last
		}
		if attempt+1 < max {
			retryPause(attempt)
		}
	}
	return last
}
