package consumer

import (
	"time"
	zlog "amuz.es/src/infra/goutils/logger/zap"
	"amuz.es/src/infra/goutils/handler"
	"amuz.es/src/infra/cpu_ctrl/producer"
	"go.uber.org/zap"
	"sync/atomic"
	"unsafe"
)

type sampleOSLogger struct {
	handler          *handler.Handler
	osMetricConsumer chan producer.OSMetricInfo
	sampleDuration   time.Duration
	logger           *zap.SugaredLogger
}

type SampleOSLogger interface {
	Consumer() chan<- producer.OSMetricInfo
	StartControl()
}

func NewSampleOSLogger(sampleDuration time.Duration, handler *handler.Handler) SampleOSLogger {
	return &sampleOSLogger{
		handler:          handler,
		osMetricConsumer: make(chan producer.OSMetricInfo, 1),
		sampleDuration:   sampleDuration,
		logger:           zlog.New(nil, "sample_os_log"),
	}
}

func (c *sampleOSLogger) Consumer() chan<- producer.OSMetricInfo { return c.osMetricConsumer }

func (c *sampleOSLogger) StartControl() {
	defer c.handler.DecreaseWait()
	defer func() {
		if err := recover(); err != nil {
			c.handler.NotifyError(err.(error))
		}
	}()
	defer c.logger.Info("os metric sampler stopped")
	c.logger.Info("os metric sampler started")

	var (
		ticker         = time.Tick(c.sampleDuration)
		newOSMetricPtr unsafe.Pointer
	)
	go func() {
		for sampledOsMetric := range c.osMetricConsumer {
			atomic.StorePointer(&newOSMetricPtr, unsafe.Pointer(&sampledOsMetric))
		}
	}()

	for {
		select {
		case <-ticker:
			swapped := (*producer.OSMetricInfo)(atomic.SwapPointer(&newOSMetricPtr, nil))
			if swapped == nil {
				continue
			}
			for _, applier := range swapped.Applier() {
				name, tagsSlice, fieldsSlice := applier()
				for i := 0; i < len(tagsSlice); i++ {
					tags, fields := tagsSlice[i], fieldsSlice[i]

					c.logger.Info("os metric")
					c.logger.Info("-> name: ", name)
					c.logger.Info("-> tags: ", tags)
					c.logger.Info("-> fields: ", fields)
					c.logger.Info("-> at: ", swapped.At)
				}
			}
		case <-c.handler.Done():
			return
		}
	}
}
