// Copyright (c) 2022 Gitpod GmbH. All rights reserved.
// Licensed under the GNU Affero General Public License (AGPL).
// See License.AGPL.txt in the project root for license information.

package ide_metrics

import (
	"fmt"
	"strconv"

	"github.com/gitpod-io/gitpod/ide-metrics-api/config"
	"github.com/gitpod-io/gitpod/installer/pkg/common"
	"github.com/prometheus/client_golang/prometheus"

	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
)

func configmap(ctx *common.RenderContext) ([]runtime.Object, error) {
	var statusCodes []string
	for statusCode := 100; statusCode < 600; statusCode++ {
		statusCodes = append(statusCodes, strconv.Itoa(statusCode))
	}
	statusCodes = append(statusCodes, "unknown")

	counterMetrics := []config.CounterMetricsConfiguration{
		// we could also create a generator later similar to https://github.com/grpc/grpc-go/tree/master/cmd/protoc-gen-go-grpc if there is abuse
		{
			Name: "grpc_server_handled_total",
			Help: "Total number of RPCs completed on the server, regardless of success or failure.",
			Labels: []config.LabelAllowList{
				{
					Name:        "grpc_method",
					AllowValues: []string{"*"},
				},
				{
					Name:        "grpc_service",
					AllowValues: []string{"*"},
				},
				{
					Name:        "grpc_type",
					AllowValues: []string{"*"},
				},
				{
					Name:        "grpc_code",
					AllowValues: []string{"*"},
				},
			},
		},
		{
			Name: "grpc_server_msg_received_total",
			Help: "Total number of RPC stream messages received on the server.",
			Labels: []config.LabelAllowList{
				{
					Name:        "grpc_method",
					AllowValues: []string{"*"},
				},
				{
					Name:        "grpc_service",
					AllowValues: []string{"*"},
				},
				{
					Name:        "grpc_type",
					AllowValues: []string{"*"},
				},
			},
		},
		{
			Name: "grpc_server_msg_sent_total",
			Help: "Total number of gRPC stream messages sent by the server.",
			Labels: []config.LabelAllowList{
				{
					Name:        "grpc_method",
					AllowValues: []string{"*"},
				},
				{
					Name:        "grpc_service",
					AllowValues: []string{"*"},
				},
				{
					Name:        "grpc_type",
					AllowValues: []string{"*"},
				},
			},
		},
		{
			Name: "grpc_server_started_total",
			Help: "Total number of RPCs started on the server",
			Labels: []config.LabelAllowList{
				{
					Name:        "grpc_method",
					AllowValues: []string{"*"},
				},
				{
					Name:        "grpc_service",
					AllowValues: []string{"*"},
				},
				{
					Name:        "grpc_type",
					AllowValues: []string{"*"},
				},
			},
		},
		{
			Name: "gitpod_supervisor_frontend_error_total",
			Help: "Total count of supervisor frontend client errors",
			Labels: []config.LabelAllowList{
				{
					Name: "resource",
					AllowValues: []string{
						"vscode-web-workbench",
						"unknown",
					},
					DefaultValue: "unknown",
				},
				{
					Name: "error",
					AllowValues: []string{
						"LoadError", // js script style of errors
						"Unknown",
					},
					DefaultValue: "Unknown",
				},
			},
		},
		{
			Name: "gitpod_vscode_web_load_total",
			Help: "Total count of attempts to load VS Code Web workbench",
			Labels: []config.LabelAllowList{
				{
					Name:        "status",
					AllowValues: []string{"loading", "failed"},
				},
			},
		},
		{
			Name: "gitpod_supervisor_frontend_client_total",
			Help: "Total count of supervisor frontend client",
		},
		{
			Name: "gitpod_vscode_extension_gallery_operation_total",
			Help: "Total count of extension operations",
			Labels: []config.LabelAllowList{
				{
					Name:         "operation",
					AllowValues:  []string{"install", "update", "uninstall", "unknown"},
					DefaultValue: "unknown",
				},
				{
					Name:         "status",
					AllowValues:  []string{"success", "failure", "unknown"},
					DefaultValue: "unknown",
				},
				{
					Name:        "galleryHost",
					AllowValues: []string{"*"},
				},
				// TODO(ak) errorCode - we should analyze error codes collect in analytics and categotize them here
			},
		},
		{
			Name: "gitpod_vscode_extension_gallery_query_total",
			Help: "Total count of extension gallery queries",
			Labels: []config.LabelAllowList{
				{
					Name:         "status",
					AllowValues:  []string{"success", "failure", "unknown"},
					DefaultValue: "unknown",
				},
				{
					Name:         "statusCode",
					AllowValues:  statusCodes,
					DefaultValue: "unknown",
				},
				{
					Name:         "errorCode",
					AllowValues:  []string{"canceled", "timeout", "failed", "unknown"},
					DefaultValue: "unknown",
				},
				{
					Name:        "galleryHost",
					AllowValues: []string{"*"},
				},
			},
		}, {
			Name: "grpc_client_started_total",
			Help: "Total number of RPCs started on the client.",
			Labels: []config.LabelAllowList{
				{
					Name:        "grpc_method",
					AllowValues: []string{"*"},
				},
				{
					Name:        "grpc_service",
					AllowValues: []string{"*"},
				},
				{
					Name:        "grpc_type",
					AllowValues: []string{"*"},
				},
			},
		}, {
			Name: "grpc_client_handled_total",
			Help: "Total number of RPCs completed by the client, regardless of success or failure.",
			Labels: []config.LabelAllowList{
				{
					Name:        "grpc_method",
					AllowValues: []string{"*"},
				},
				{
					Name:        "grpc_service",
					AllowValues: []string{"*"},
				},
				{
					Name:        "grpc_type",
					AllowValues: []string{"*"},
				},
				{
					Name:        "grpc_code",
					AllowValues: []string{"*"},
				},
			},
		},
	}

	histogramMetrics := []config.HistogramMetricsConfiguration{
		{
			Name: "gitpod_vscode_extension_gallery_operation_duration_seconds",
			Help: "Duration of extension operations in seconds",
			Labels: []config.LabelAllowList{
				{
					Name:         "operation",
					AllowValues:  []string{"install", "update", "uninstall", "unknown"},
					DefaultValue: "unknown",
				},
				{
					Name:        "galleryHost",
					AllowValues: []string{"*"},
				},
			},
			Buckets: []float64{0.1, 0.5, 1, 5, 10, 15, 30},
		}, {
			Name: "gitpod_vscode_extension_gallery_query_duration_seconds",
			Help: "Duration of extension gallery query in seconds",
			Labels: []config.LabelAllowList{
				{
					Name:        "galleryHost",
					AllowValues: []string{"*"},
				},
			},
			Buckets: []float64{0.1, 0.5, 1, 5, 10, 15, 30},
		},
	}

	aggregatedHistogramMetrics := []config.HistogramMetricsConfiguration{
		// we could also create a generator later similar to https://github.com/grpc/grpc-go/tree/master/cmd/protoc-gen-go-grpc if there is abuse
		{
			Name: "grpc_server_handling_seconds",
			Help: "Histogram of response latency (seconds) of gRPC that had been application-level handled by the server.",
			Labels: []config.LabelAllowList{
				{
					Name:        "grpc_method",
					AllowValues: []string{"*"},
				},
				{
					Name:        "grpc_service",
					AllowValues: []string{"*"},
				},
				{
					Name:        "grpc_type",
					AllowValues: []string{"*"},
				},
			},
			Buckets: []float64{.005, .025, .05, .1, .5, 1, 2.5, 5, 30, 60, 120, 240, 600},
		},
		{
			Name:    "supervisor_ide_ready_duration_total",
			Help:    "the IDE startup time",
			Buckets: []float64{0.1, 0.5, 1, 1.5, 2, 2.5, 5, 10},
			Labels: []config.LabelAllowList{
				{
					Name:        "kind",
					AllowValues: []string{"web", "desktop"},
				},
			},
		},
		{
			Name:    "supervisor_initializer_bytes_second",
			Help:    "initializer speed in bytes per second",
			Buckets: prometheus.ExponentialBuckets(1024*1024, 2, 12),
			Labels: []config.LabelAllowList{
				{
					Name:        "kind",
					AllowValues: []string{"*"},
				},
			},
		}, {
			Name: "grpc_client_handling_seconds",
			Help: "Histogram of response latency (seconds) of the gRPC until it is finished by the application.",
			Labels: []config.LabelAllowList{
				{
					Name:        "grpc_type",
					AllowValues: []string{"*"},
				},
				{
					Name:        "grpc_service",
					AllowValues: []string{"*"},
				},
				{
					Name:        "grpc_method",
					AllowValues: []string{"*"},
				},
			},
			Buckets: []float64{0.1, 0.2, 0.5, 1, 2, 5, 10},
		},
	}

	errorReporting := config.ErrorReportingConfiguration{
		AllowComponents: []string{
			"supervisor-frontend",
			"vscode-workbench",
			"vscode-server",
			"vscode-web",
			"gitpod-cli",
		},
	}

	cfg := config.ServiceConfiguration{
		Server: config.MetricsServerConfiguration{
			Port: ContainerPort,
			// RateLimits: , // TODO(pd) ratelimit
			CounterMetrics:             counterMetrics,
			HistogramMetrics:           histogramMetrics,
			AggregatedHistogramMetrics: aggregatedHistogramMetrics,
			ErrorReporting:             errorReporting,
		},
		Prometheus: struct {
			Addr string `json:"addr"`
		}{Addr: common.LocalhostPrometheusAddr()},
	}

	fc, err := common.ToJSONString(cfg)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal ide-metrics config: %w", err)
	}

	res := []runtime.Object{
		&corev1.ConfigMap{
			TypeMeta: common.TypeMetaConfigmap,
			ObjectMeta: metav1.ObjectMeta{
				Name:        Component,
				Namespace:   ctx.Namespace,
				Labels:      common.CustomizeLabel(ctx, Component, common.TypeMetaConfigmap),
				Annotations: common.CustomizeAnnotation(ctx, Component, common.TypeMetaConfigmap),
			},
			Data: map[string]string{
				"config.json": string(fc),
			},
		},
	}
	return res, nil
}
