package measurexlite

//
// Logging support
//

import (
	"fmt"
	"sync"
	"time"

	"github.com/ooni/probe-cli/v3/internal/model"
)

// NewOperationLogger creates a new logger that logs
// about an in-progress operation. If it takes too much
// time to emit the result of the operation, the code
// will emit an interim log message mentioning that the
// operation is currently in progress.
func NewOperationLogger(logger model.Logger, format string, v ...any) *OperationLogger {
	return newOperationLogger(500*time.Millisecond, logger, format, v...)
}

func newOperationLogger(maxwait time.Duration, logger model.Logger, format string, v ...any) *OperationLogger {
	ol := &OperationLogger{
		logger:  logger,
		maxwait: maxwait,
		message: fmt.Sprintf(format, v...),
		once:    &sync.Once{},
		sighup:  make(chan any),
		wg:      &sync.WaitGroup{},
	}
	ol.wg.Add(1)
	go ol.maybeEmitProgress()
	return ol
}

// OperationLogger keeps state required to log about an in-progress
// operation as documented by [NewOperationLogger].
type OperationLogger struct {
	logger  model.Logger
	maxwait time.Duration
	message string
	once    *sync.Once
	sighup  chan any
	wg      *sync.WaitGroup
}

func (ol *OperationLogger) maybeEmitProgress() {
	defer ol.wg.Done()
	timer := time.NewTimer(ol.maxwait)
	defer timer.Stop()
	select {
	case <-timer.C:
		ol.logger.Infof("%s... in progress", ol.message)
	case <-ol.sighup:
		// we'll emit directly in stop
	}
}

// Stop must be called when the operation is done. The [value] argument
// is the result of the operation, which may be nil. This method ensures
// that we log the final result of the now-completed operation.
func (ol *OperationLogger) Stop(value any) {
	ol.once.Do(func() {
		close(ol.sighup)
		ol.wg.Wait()
		if value != nil {
			if err, okay := value.(error); okay {
				ol.logger.Infof("%s... %s", ol.message, err.Error())
				return
			}
			// fallthrough
		} else {
			value = "ok"
		}
		ol.logger.Infof("%s... %+v", ol.message, value)
	})
}
