package consumer

import (
	"time"
	"strconv"
	"amuz.es/src/infra/goutils/handler"
	"amuz.es/src/infra/cpu_ctrl/producer"
	"github.com/influxdata/influxdb/client/v2"
	zlog "amuz.es/src/infra/goutils/logger/zap"
	"go.uber.org/zap"
)

type data struct {
	Tempeture float64
	FanSpeed  int
}

type influxMetric struct {
	host              string
	processorCount    int
	handler           *handler.Handler
	fanSpeedConsumer  chan producer.FanspeedInfo
	tempetureConsumer chan producer.TempetureInfo
	logger            *zap.SugaredLogger
}

type InfluxMetric interface {
	FanSpeedConsumer() chan<- producer.FanspeedInfo
	TempetureConsumer() chan<- producer.TempetureInfo
	StartLogging()
}

func NewInfluxMetric(host string, processorCount int, handler *handler.Handler) InfluxMetric {
	return &influxMetric{
		host:              host,
		processorCount:    processorCount,
		handler:           handler,
		fanSpeedConsumer:  make(chan producer.FanspeedInfo, processorCount),
		tempetureConsumer: make(chan producer.TempetureInfo, processorCount),
		logger:            zlog.New(nil, "influx"),
	}
}

func (m *influxMetric) FanSpeedConsumer() chan<- producer.FanspeedInfo   { return m.fanSpeedConsumer }
func (m *influxMetric) TempetureConsumer() chan<- producer.TempetureInfo { return m.tempetureConsumer }

func (m *influxMetric) StartLogging() {
	defer m.handler.DecreaseWait()

	defer func() {
		if err := recover(); err != nil {
			m.handler.NotifyError(err.(error))
		}
	}()

	defer m.logger.Info("Metric logging stopped")
	m.logger.Info("Metric logging started")

	var influxDbConn client.Client
	for {
		conn, err := client.NewUDPClient(client.UDPConfig{Addr: m.host,})
		if err != nil {
			m.logger.Error(err)
		} else {
			influxDbConn = conn
			break
		}
		cont := time.After(1 * time.Second)
		select {
		case <-cont:
			continue
		case <-m.handler.Done():
			return
		}
	}

	ticker := time.Tick(time.Second)
	metricData := make([]data, m.processorCount)
	sendData := make([]data, m.processorCount)

	go func() {
		for changedSpeed := range m.fanSpeedConsumer {
			metricData[changedSpeed.Id].FanSpeed = changedSpeed.FanSpeed
		}
	}()

	go func() {
		for changedTempeture := range m.tempetureConsumer {
			metricData[changedTempeture.Id].Tempeture = changedTempeture.Tempeture
		}
	}()

	for {
		select {
		case <-ticker:
			copy(sendData, metricData)
			go m.sendPoint(influxDbConn, sendData)
		case <-m.handler.Done():
			return
		}
	}
}
func (m *influxMetric) sendPoint(
	influxDbConn client.Client,
	datas []data) {
	pointList := make([]*client.Point, 0, 0)
	at := time.Now()
	for id, data := range datas {
		if point, err := m.getPoint(id, data, at); err == nil {
			pointList = append(pointList, point)
		} else {
			m.logger.Debugf("id %d err %s", id, err)
		}
	}

	if len(pointList) == 0 {
		return
	}

	// Create a new point batch
	batchPoint, err := client.NewBatchPoints(client.BatchPointsConfig{
		Database:  "core",
		Precision: "s",
	})
	if err != nil {
		m.logger.Warn(err)
		return
	}
	batchPoint.AddPoints(pointList)
	if err := influxDbConn.Write(batchPoint); err != nil {
		m.logger.Warn(err)
	}

}

func (m *influxMetric) getPoint(id int, data data, at time.Time) (*client.Point, error) {

	// Create a point and add to batch
	tags := map[string]string{"processor": strconv.Itoa(id)}
	fields := map[string]interface{}{
		"tempeture": data.Tempeture,
		"fan":       data.FanSpeed,
	}

	return client.NewPoint("processor", tags, fields, at)
}
