package metrics

import (
	"fmt"
	"time"

	"code.justin.tv/websocket-edge/server/internal/logs"
	"code.justin.tv/websocket-edge/server/internal/util"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

// Log statter-- useful for local development.
type LogStatter struct {
	logs.Logger
}

func (s *LogStatter) observeMethod(method string, metricName string, value float64, dimensions ...string) {
	if len(dimensions)%2 != 0 {
		return
	}
	fields := make([]zapcore.Field, len(dimensions)/2)
	for i := 0; i < len(dimensions)/2; i += 1 {
		fields[i] = zap.String(dimensions[i*2], dimensions[i*2+1])
	}

	s.Info(fmt.Sprintf("%s: %s(%.3f)", method, metricName, value), fields...)
}

func (s *LogStatter) observeMethodErr(method string, metricName string, value float64, dimensions ...string) {
	if len(dimensions)%2 != 0 {
		return
	}
	fields := make([]zapcore.Field, len(dimensions)/2)
	for i := 0; i < len(dimensions)/2; i += 1 {
		fields[i] = zap.String(dimensions[i*2], dimensions[i*2+1])
	}

	s.Error(fmt.Sprintf("%s: %s(%.3f)", method, metricName, value), fields...)
}

func (s *LogStatter) Increment(metricName string, dimensions ...string) {
	s.observeMethod("Increment", metricName, 1, dimensions...)
}

func (s *LogStatter) IncrementAt(metricName string, ts time.Time, dimensions ...string) {
	dims := util.PrependStrings(dimensions, "ts", ts.String())
	s.observeMethod("IncrementAt", metricName, 1, dims...)
}

func (s *LogStatter) IncrementErr(metricName string, errDimension string) {
	s.observeMethodErr("Increment", metricName, 1, dimensionErrorCause, errDimension)
}

func (s *LogStatter) IncrementErrAt(metricName string, errDimension string, ts time.Time) {
	s.observeMethodErr("IncrementAt", metricName, 1, "ts", ts.String(), dimensionErrorCause, errDimension)
}

func (s *LogStatter) Timing(metricName string, duration time.Duration, dimensions ...string) {
	s.observeMethod("Timing", metricName, duration.Seconds(), dimensions...)
}

func (s *LogStatter) TimingAt(metricName string, duration time.Duration, ts time.Time, dimensions ...string) {
	dims := util.PrependStrings(dimensions, "ts", ts.String())
	s.observeMethod("TimingAt", metricName, duration.Seconds(), dims...)
}

func (s *LogStatter) StartPoller(metricName string, valueFn func() float64, interval time.Duration) {
	ticker := time.NewTicker(interval)
	go func() {
		for range ticker.C {
			s.observeMethod("PolledSample", metricName, valueFn())
		}
	}()
}

func (s *LogStatter) Size(metricName string, size int, dimensions ...string) {
	s.observeMethod("Size", metricName, float64(size), dimensions...)
}

func (s *LogStatter) Close() error {
	s.Info("Closing LogStatter.")
	return nil
}
