package packer

import (
	"context"
	"fmt"
	"time"

	packersdk "github.com/hashicorp/packer/packer-plugin-sdk/packer"
)

// TimeoutProvisioner is a Provisioner implementation that can timeout after a
// duration
type TimeoutProvisioner struct {
	packersdk.Provisioner
	Timeout time.Duration
}

func (p *TimeoutProvisioner) Provision(ctx context.Context, ui packersdk.Ui, comm packersdk.Communicator, generatedData map[string]interface{}) error {
	ctx, cancel := context.WithTimeout(ctx, p.Timeout)
	defer cancel()

	// Use a select to determine if we get cancelled during the wait
	ui.Say(fmt.Sprintf("Setting a %s timeout for the next provisioner...", p.Timeout))

	errC := make(chan interface{})

	go func() {
		select {
		case <-errC:
			// all good
		case <-ctx.Done():
			switch ctx.Err() {
			case context.DeadlineExceeded:
				ui.Error("Cancelling provisioner after a timeout...")
			default:
				// the context also gets cancelled when the provisioner is
				// successful
			}
		}
	}()

	err := p.Provisioner.Provision(ctx, ui, comm, generatedData)
	close(errC)
	return err
}
