package retrypolicy

import (
	
	

	
	
	
	
	
)

// executor is a policy.Executor that handles failures according to a RetryPolicy.
type executor[ any] struct {
	*policy.BaseExecutor[]
	*retryPolicy[]

	// Mutable state
	failedAttempts  int
	retriesExceeded bool
	lastDelay       time.Duration // The last backoff delay time
}

var _ policy.Executor[any] = &executor[any]{}

func ( *executor[]) ( func(failsafe.Execution[]) *common.PolicyResult[]) func(failsafe.Execution[]) *common.PolicyResult[] {
	return func( failsafe.Execution[]) *common.PolicyResult[] {
		 := .(policy.ExecutionInternal[])

		for {
			 := ()
			if ,  := .IsCanceledWithResult();  {
				return 
			}
			if .retriesExceeded {
				return 
			}

			 = .PostExecute(, )
			if .Done {
				return 
			}

			// Record result
			if  := .RecordResult();  != nil {
				return 
			}

			// Delay
			 := .getDelay()
			if .onRetryScheduled != nil {
				.onRetryScheduled(failsafe.ExecutionScheduledEvent[]{
					ExecutionAttempt: .CopyWithResult(),
					Delay:            ,
				})
			}
			 := time.NewTimer()
			select {
			case <-.C:
			case <-.Canceled():
				.Stop()
			}

			// Prepare for next iteration
			if  := .InitializeRetry();  != nil {
				return 
			}

			// Call retry listener
			if .onRetry != nil {
				.onRetry(failsafe.ExecutionEvent[]{ExecutionAttempt: .CopyWithResult()})
			}
		}
	}
}

// OnFailure updates failedAttempts and retriesExceeded, and calls event listeners
func ( *executor[]) ( policy.ExecutionInternal[],  *common.PolicyResult[]) *common.PolicyResult[] {
	.BaseExecutor.OnFailure(, )

	.failedAttempts++
	 := .maxRetries != -1 && .failedAttempts > .maxRetries
	 := .maxDuration != 0 && .ElapsedTime() > .maxDuration
	.retriesExceeded =  || 
	 := .IsAbortable(.Result, .Error)
	 := ! && !.retriesExceeded && .allowsRetries()
	 :=  || !

	// Call listeners
	if  && .onAbort != nil {
		.onAbort(failsafe.ExecutionEvent[]{ExecutionAttempt: .CopyWithResult()})
	}
	if .retriesExceeded {
		if ! && .onRetriesExceeded != nil {
			.onRetriesExceeded(failsafe.ExecutionEvent[]{ExecutionAttempt: .CopyWithResult()})
		}
		if !.returnLastFailure {
			return internal.FailureResult[](ExceededError{
				LastResult: .Result,
				LastError:  .Error,
			})
		}
	}
	return .WithDone(, false)
}

// getDelay updates lastDelay and returns the new delay
func ( *executor[]) ( failsafe.ExecutionAttempt[]) time.Duration {
	var  time.Duration
	if  := .ComputeDelay();  != -1 {
		 = 
	} else {
		 = .getFixedOrRandomDelay()
	}
	if  != 0 {
		 = .adjustForJitter()
	}
	 = .adjustForMaxDuration(, .ElapsedTime())
	return 
}

func ( *executor[]) ( failsafe.ExecutionAttempt[]) time.Duration {
	if .Delay != 0 {
		// Adjust for backoffs
		if .lastDelay != 0 && .Retries() >= 1 && .maxDelay != 0 {
			 := time.Duration(float32(.lastDelay) * .delayFactor)
			.lastDelay = min(, .maxDelay)
		} else {
			.lastDelay = .Delay
		}
		return .lastDelay
	}
	if .delayMin != 0 && .delayMax != 0 {
		return time.Duration(util.RandomDelayInRange(.delayMin.Nanoseconds(), .delayMax.Nanoseconds(), rand.Float64()))
	}
	return 0
}

func ( *executor[]) ( time.Duration) time.Duration {
	if .jitter != 0 {
		 = util.RandomDelay(, .jitter, rand.Float64())
	} else if .jitterFactor != 0 {
		 = util.RandomDelayFactor(, .jitterFactor, rand.Float32())
	}
	return 
}

func ( *executor[]) ( time.Duration,  time.Duration) time.Duration {
	if .maxDuration != 0 {
		 = min(, .maxDuration-)
	}
	return max(0, )
}