package events

import (
	"sync"
)

// Drainer is used to gracefully shut down atlantis by waiting for in-progress
// operations to complete.
type Drainer struct {
	status DrainStatus    `validate:"required"`
	mutex  sync.Mutex     `validate:"required"`
	wg     sync.WaitGroup `validate:"required"`
}

type DrainStatus struct {
	// ShuttingDown is whether we are in the progress of shutting down.
	ShuttingDown bool
	// InProgressOps is the number of operations currently in progress.
	InProgressOps int
}

// StartOp tries to start a new operation. It returns false if Atlantis is
// shutting down.
func (d *Drainer) StartOp() bool {
	d.mutex.Lock()
	defer d.mutex.Unlock()

	if d.status.ShuttingDown {
		return false
	}
	d.status.InProgressOps++
	d.wg.Add(1)
	return true
}

// OpDone marks an operation as complete.
func (d *Drainer) OpDone() {
	d.mutex.Lock()
	defer d.mutex.Unlock()

	d.status.InProgressOps--
	d.wg.Done()
	if d.status.InProgressOps < 0 {
		// This would be a bug.
		d.status.InProgressOps = 0
	}
}

// ShutdownBlocking sets "shutting down" to true and blocks until there are no
// in progress operations.
func (d *Drainer) ShutdownBlocking() {
	// Set the shutdown status.
	d.mutex.Lock()
	d.status.ShuttingDown = true
	d.mutex.Unlock()

	// Block until there are no in-progress ops.
	d.wg.Wait()
}

func (d *Drainer) GetStatus() DrainStatus {
	return d.status
}
