package main

import (
	"context"
	"flag"
	"os"
	"os/signal"
	"syscall"
	"time"

	"code.justin.tv/eventbus/controlplane/infrastructure"
	"code.justin.tv/eventbus/controlplane/internal/environment"

	"code.justin.tv/eventbus/controlplane/internal/logger"
	"go.uber.org/zap"
)

const (
	// How long to wait between converge runs
	defaultConvergeInterval = 2 * time.Minute

	// How long to allow an in-progress converge to keep running after stop signal before shutting it down
	convergeGracePeriod = 10 * time.Second
)

func main() {
	var once bool
	flag.BoolVar(&once, "once", false, "tells the converger to run once and exit")
	flag.Parse()

	log := logger.FromContext(context.Background())

	convergeInterval := environment.Duration("CONVERGE_INTERVAL", defaultConvergeInterval)

	// Allow for semi-graceful shutdown mid-converge
	stopCtx, stopCancel := context.WithCancel(context.Background())
	defer stopCancel()

	loggerCtx := log.AddToContext(context.Background())
	_, convergeCancel := context.WithCancel(loggerCtx)
	defer convergeCancel()

	startSignaler(stopCancel, convergeCancel)

	for {
		err := infrastructure.Converge()
		if err != nil {
			log.Error("Converge failed", zap.Error(err))
		} else {
			log.Info("Converged successfully")
		}

		// Check for 'one-off' mode
		if once {
			if err != nil {
				os.Exit(1)
			}
			return
		}

		select {
		case <-stopCtx.Done():
			return
		case <-time.After(convergeInterval):
			// do nothing
		}
	}
}

// support signal-based graceful shutdown of this app.
func startSignaler(stopCancel, convergeCancel func()) {
	sighandler := make(chan os.Signal, 1)

	signal.Notify(sighandler, os.Interrupt, syscall.SIGTERM)

	go func() {
		for range sighandler {
			stopCancel()
			// Wait 10 seconds before cancelling the context in the chance it might stop earlier
			time.AfterFunc(convergeGracePeriod, convergeCancel)
		}
	}()
}
