package metrics

import (
	"os"
	"runtime"
	"strconv"
	"time"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/procfs"
)

const (
	namespace = "walservice"
)

var (
	CommandDurationSeconds = prometheus.NewHistogramVec(
		prometheus.HistogramOpts{
			Namespace: namespace,
			Subsystem: "walservice",
			Name:      "command_duration_seconds",
			Help:      "Bucketed histogram of command duration seconds",
			Buckets:   prometheus.ExponentialBuckets(0.0001, 1.5, 40),
		}, []string{"type"})
	ReadWaitTimeDurationHistogram  = CommandDurationSeconds.WithLabelValues("read")
	WriteWaitTimeDurationHistogram = CommandDurationSeconds.WithLabelValues("write")
	FlushWaitTimeDurationHistogram = CommandDurationSeconds.WithLabelValues("flush")
	FsyncWaitTimeDurationHistogram = CommandDurationSeconds.WithLabelValues("fsync")
	OpenWaitTimeDurationHistogram  = CommandDurationSeconds.WithLabelValues("open")
	CommandSizeBytes               = prometheus.NewHistogramVec(
		prometheus.HistogramOpts{
			Namespace: namespace,
			Subsystem: "walservice",
			Name:      "command_size_bytes",
			Help:      "Bucketed histogram of command size in bytes",
			Buckets:   prometheus.ExponentialBuckets(1, 2, 40),
		}, []string{"type"})
	WriteSizeByteHistogram = CommandSizeBytes.WithLabelValues("write")
	ReadSizeByteHistogram  = CommandSizeBytes.WithLabelValues("read")
	ServerGrpcReqBatchSize = prometheus.NewHistogram(
		prometheus.HistogramOpts{
			Namespace: namespace,
			Subsystem: "server",
			Name:      "grpc_req_batch_size",
			Help:      "grpc batch size of gRPC requests",
			Buckets:   prometheus.ExponentialBuckets(1, 2, 15),
		})
	ServerGrpcRespBatchSize = prometheus.NewHistogram(
		prometheus.HistogramOpts{
			Namespace: namespace,
			Subsystem: "server",
			Name:      "grpc_resp_batch_size",
			Help:      "grpc batch size of gRPC responses",
			Buckets:   prometheus.ExponentialBuckets(1, 2, 15),
		})
	ThreadCPUSecondsTotal = prometheus.NewCounterVec(
		prometheus.CounterOpts{
			Namespace: namespace,
			Subsystem: "thread",
			Name:      "cpu_seconds_total",
			Help:      "Total user and system CPU time spent in seconds by threads.",
		}, []string{"name", "pid"})
)

func init() {
	prometheus.MustRegister(CommandDurationSeconds)
	prometheus.MustRegister(CommandSizeBytes)
	prometheus.MustRegister(ServerGrpcReqBatchSize)
	prometheus.MustRegister(ServerGrpcRespBatchSize)
	prometheus.MustRegister(ThreadCPUSecondsTotal)
	if runtime.GOOS == `linux` {
		go func() {
			var lastCPUTime float64
			pid := os.Getpid()
			rfsThreadCPUSecondsTotal := ThreadCPUSecondsTotal.WithLabelValues("rfs", strconv.FormatInt(int64(pid), 10))
			ticker := time.NewTicker(time.Second * 10)
			for range ticker.C {
				// CPU statistics are available only on Linux
				p, err := procfs.NewProc(pid)
				if err == nil {
					if stat, err := p.Stat(); err == nil {
						CPUTime := stat.CPUTime()
						rfsThreadCPUSecondsTotal.Add(CPUTime - lastCPUTime)
						lastCPUTime = CPUTime
					}
				}
			}
		}()
	}
}
