package csbi

import (
	"time"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promauto"
	log "github.com/sirupsen/logrus"
)

var (
	grpcRequestsTotal = promauto.NewCounterVec(
		prometheus.CounterOpts{
			Name: "grpc_requests_total",
			Help: "Total number of gRPC requests sent to the API",
		},
		[]string{"rpc"},
	)

	grpcAPIErrorsTotal = promauto.NewCounterVec(
		prometheus.CounterOpts{
			Name: "grpc_api_errors_total",
			Help: "Total number of errors returned by the API",
		},
		[]string{"rpc", "error"},
	)

	grpcRequestDurationSecondsTotal = promauto.NewCounterVec(
		prometheus.CounterOpts{
			Name: "grpc_request_duration_seconds_total",
			Help: "Cumulative time required to handle gRPC requests",
		},
		[]string{"rpc"},
	)

	grpcRequestDurationSeconds = promauto.NewHistogramVec(
		prometheus.HistogramOpts{
			Name: "grpc_request_duration_seconds",
			Help: "Histogram of gRPC request handling times",
		},
		[]string{"rpc"},
	)

	buildsTotal = promauto.NewCounterVec(
		prometheus.CounterOpts{
			Name: "builds_total",
			Help: "Total number of builds",
		},
		[]string{"type"},
	)

	buildDurationSecondsTotal = promauto.NewCounterVec(
		prometheus.CounterOpts{
			Name: "build_duration_seconds_total",
			Help: "Total time needed for builds",
		},
		[]string{"type"},
	)

	buildDurationSeconds = promauto.NewHistogramVec(
		prometheus.HistogramOpts{
			Name: "build_duration_seconds",
			Help: "Histogram of build times",
		},
		[]string{"type"},
	)

	deploymentsTotal = promauto.NewGauge(
		prometheus.GaugeOpts{
			Name: "deployments_total",
			Help: "Total number of deployments",
		},
	)

	gcDurationSecondsTotal = promauto.NewCounter(
		prometheus.CounterOpts{
			Name: "garbage_collection_duration_seconds_total",
			Help: "Total time in seconds required for garbage collection",
		},
	)

	gcDurationSeconds = promauto.NewHistogram(
		prometheus.HistogramOpts{
			Name: "garbage_collection_duration_seconds",
			Help: "Histogram for GC runtime",
		},
	)

	gcTimeoutsTotal = promauto.NewCounter(
		prometheus.CounterOpts{
			Name: "garbage_collector_timeouts_total",
			Help: "Total number of timeouts during GC",
		},
	)

	orchestratorCreationsTotal = promauto.NewCounterVec(
		prometheus.CounterOpts{
			Name: "orchestrator_creations_total",
			Help: "Total number of created deployments",
		},
		[]string{"type"},
	)

	orchestratorDestructionsTotal = promauto.NewCounterVec(
		prometheus.CounterOpts{
			Name: "orchestrator_destructions_total",
			Help: "Total number of destroyed deployments",
		},
		[]string{"type"},
	)

	orchestratorErrorsTotal = promauto.NewCounterVec(
		prometheus.CounterOpts{
			Name: "orchestrator_errors_total",
			Help: "Total number of orchestrator errors",
		},
		[]string{"type", "error"},
	)

	orchestratorCreateDurationSeconds = promauto.NewHistogramVec(
		prometheus.HistogramOpts{
			Name: "orchestrator_create_duration_seconds",
			Help: "Histogram of create operation duration",
		},
		[]string{"type"},
	)

	orchestratorCreateDurationSecondsTotal = promauto.NewCounterVec(
		prometheus.CounterOpts{
			Name: "orchestrator_create_duration_seconds_total",
			Help: "Total time required for create operations",
		},
		[]string{"type"},
	)

	orchestratorDestroyDurationSeconds = promauto.NewHistogramVec(
		prometheus.HistogramOpts{
			Name: "orchestrator_destroy_duration_seconds",
			Help: "Histogram of destroy operation duration",
		},
		[]string{"type"},
	)

	orchestratorDestroyDurationSecondsTotal = promauto.NewCounterVec(
		prometheus.CounterOpts{
			Name: "orchestrator_destroy_duration_seconds_total",
			Help: "Total time required for destroy operations",
		},
		[]string{"type"},
	)

	codeGenerationErrorsTotal = promauto.NewCounterVec(
		prometheus.CounterOpts{
			Name: "code_generation_errors_total",
			Help: "Total errors during code generation",
		},
		[]string{"type", "error"},
	)

	codeGenerationsTotal = promauto.NewCounterVec(
		prometheus.CounterOpts{
			Name: "code_generations_total",
			Help: "Total number of code generations",
		},
		[]string{"type"},
	)

	codeGenerationDurationSeconds = promauto.NewHistogramVec(
		prometheus.HistogramOpts{
			Name: "code_generation_duration_seconds",
			Help: "Histogram of code generation duration",
		},
		[]string{"type"},
	)

	codeGenerationDurationSecondsTotal = promauto.NewCounterVec(
		prometheus.CounterOpts{
			Name: "code_generation_duration_seconds_total",
			Help: "Total time required for code generation",
		},
		[]string{"type"},
	)

	codeGenerationNumberOfModels = promauto.NewHistogramVec(
		prometheus.HistogramOpts{
			Name: "code_generation_number_of_models",
			Help: "Histogram of models used for code generation",
		},
		[]string{"type"},
	)
)

func promStartHook(labels prometheus.Labels, counter *prometheus.CounterVec) time.Time {
	counter.With(labels).Inc()
	return time.Now()
}

func promEndHook(labels prometheus.Labels, start time.Time, counter *prometheus.CounterVec, hist *prometheus.HistogramVec) {
	duration := time.Since(start)
	counter.With(labels).Add(duration.Seconds())
	hist.With(labels).Observe(duration.Seconds())
}

func promHandleError(labels prometheus.Labels, err error, counter *prometheus.CounterVec) error {
	log.Error(err)
	errLabels := make(prometheus.Labels)
	for k, v := range labels {
		errLabels[k] = v
	}
	errLabels["error"] = err.Error()
	counter.With(errLabels).Inc()
	return err
}
