package try

import (
	"fmt"
	"time"

	"github.com/jenkinsci/kubernetes-operator/internal/errors"
	time2 "github.com/jenkinsci/kubernetes-operator/internal/time"
)

// ErrTimeout is used when the set timeout has been reached.
type ErrTimeout struct {
	text  string
	cause error
}

func (e *ErrTimeout) Error() string {
	return fmt.Sprintf("%s: %s", e.text, e.cause.Error())
}

// Cause returns the error that caused ErrTimeout.
func (e *ErrTimeout) Cause() error {
	return e.cause
}

// Format implements fmt.Formatter used by Sprint(f) or Fprint(f) etc.
func (e *ErrTimeout) Format(s fmt.State, verb rune) {
	errors.Format(e.cause, s, verb)
}

// Until keeps trying until timeout or there is a result or an error.
func Until(something func() (end bool, err error), tick, timeout time.Duration) error {
	counter := 0
	tickChan := time2.Every(tick)
	timeoutChan := time.After(timeout)
	var lastErr error
	for {
		select {
		case <-tickChan:
			end, err := something()
			lastErr = err
			if end {
				return err
			}
			counter++
		case <-timeoutChan:
			return &ErrTimeout{
				text:  fmt.Sprintf("timed out after: %s, tries: %d", timeout, counter),
				cause: lastErr,
			}
		}
	}
}
