package main

import (
	"errors"
	mcpinger "github.com/Raqbit/mc-pinger"
	lpsender "github.com/itzg/line-protocol-sender"
	"go.uber.org/zap"
	"log"
	"strconv"
	"time"
)

const (
	MetricName = "minecraft_status"

	TagHost    = "host"
	TagPort    = "port"
	TagStatus  = "status"
	TagVersion = "version"

	FieldError        = "error"
	FieldOnline       = "online"
	FieldMax          = "max"
	FieldResponseTime = "response_time"

	StatusError   = "error"
	StatusSuccess = "success"
)

type TelegrafGatherer struct {
	host     string
	port     string
	pinger   mcpinger.Pinger
	logger   *zap.Logger
	lpClient lpsender.Client
}

func NewTelegrafGatherer(host string, port uint16, lpClient lpsender.Client, logger *zap.Logger) *TelegrafGatherer {
	return &TelegrafGatherer{
		host:     host,
		port:     strconv.FormatInt(int64(port), 10),
		pinger:   mcpinger.New(host, uint16(port)),
		lpClient: lpClient,
		logger:   logger,
	}
}

func (g *TelegrafGatherer) Gather() {
	g.logger.Debug("gathering", zap.String("host", g.host), zap.String("port", g.port))
	startTime := time.Now()
	info, err := g.pinger.Ping()
	elapsed := time.Now().Sub(startTime)

	if err != nil {
		g.sendFailedMetrics(err, elapsed)
	} else if info.Players.Max == 0 {
		g.sendFailedMetrics(errors.New("server not ready"), elapsed)
	} else {
		err := g.sendInfoMetrics(info, elapsed)
		if err != nil {
			log.Printf("failed to send metrics: %s", err)
		}
	}
}

func (g *TelegrafGatherer) sendInfoMetrics(info *mcpinger.ServerInfo, elapsed time.Duration) error {
	m := lpsender.NewSimpleMetric(MetricName)

	m.AddTag(TagHost, g.host)
	m.AddTag(TagPort, g.port)
	m.AddTag(TagStatus, StatusSuccess)
	m.AddTag(TagVersion, info.Version.Name)

	m.AddField(FieldResponseTime, elapsed.Seconds())
	m.AddField(FieldOnline, uint64(info.Players.Online))
	m.AddField(FieldMax, uint64(info.Players.Max))

	g.lpClient.Send(m)

	return nil
}

func (g *TelegrafGatherer) sendFailedMetrics(err error, elapsed time.Duration) {
	m := lpsender.NewSimpleMetric(MetricName)

	m.AddTag(TagHost, g.host)
	m.AddTag(TagPort, g.port)
	m.AddTag(TagStatus, StatusError)

	m.AddField(FieldError, err.Error())
	m.AddField(FieldResponseTime, elapsed.Seconds())

	g.lpClient.Send(m)

	return
}