package autoscaler

import (
	"context"
	"errors"
	"fmt"
	"time"

	"gitlab.com/gitlab-org/gitlab-runner/common"
)

type executor struct {
	common.Executor

	provider *provider
	build    *common.Build
	config   common.RunnerConfig
}

func (e *executor) Prepare(options common.ExecutorPrepareOptions) (err error) {
	e.build = options.Build
	e.config = *options.Config

	e.build.Log().Infoln("Preparing instance...")

	acqRef, ok := options.Build.ExecutorData.(*acquisitionRef)
	if !ok {
		return fmt.Errorf("no acquisition ref data")
	}

	// if we already have an acquisition just retry preparing it
	if acqRef.acq != nil {
		return e.Executor.Prepare(options)
	}

	// The acqTimeout defines how long we are willing to wait for an instance to be acquired.
	// It defaults to 15 minutes, as cloud providers can take several minutes to provision instances,
	// especially for certain operating systems like Windows. This value can be configured
	// through the Autoscaler configuration (InstanceAcquireTimeout) to better suit the user's environment.
	acqTimeout := 15 * time.Minute
	if options.Config.Autoscaler != nil && options.Config.Autoscaler.InstanceAcquireTimeout > 0 {
		acqTimeout = options.Config.Autoscaler.InstanceAcquireTimeout
	}

	ctx, cancel := context.WithTimeout(options.Context, acqTimeout)
	defer cancel()

	acq, err := e.provider.getRunnerTaskscaler(options.Config).Acquire(ctx, acqRef.key)
	if err != nil {
		// Check if the error is due to the context timeout
		if errors.Is(err, context.DeadlineExceeded) {
			return fmt.Errorf("unable to acquire instance within the configured timeout of %s: %w", acqTimeout, err)
		}

		return fmt.Errorf("unable to acquire instance: %w", err)
	}

	e.build.Log().WithField("key", acqRef.key).Trace("Acquired capacity...")

	acqRef.acq = acq

	return e.Executor.Prepare(options)
}

func (e *executor) Cleanup() {
	e.Executor.Cleanup()
}
