package notify

import (
	"context"
	"fmt"

	"github.com/tidbcloud/infra-provider/infra-inspector/api"
	"github.com/tidbcloud/infra-provider/infra-inspector/pkg/config"

	"github.com/patrickmn/go-cache"
	"github.com/prometheus/client_golang/prometheus"
)

var (
	descForViolation = prometheus.NewDesc(
		prometheus.BuildFQName("inspect", "violation", "details"),
		"Details about a violation",
		[]string{"target", "resource_api_version", "resource_kind", "resource_name", "resource_namespace", "rule_name", "rule_level"},
		nil,
	)
	descForViolationCount = prometheus.NewDesc(
		prometheus.BuildFQName("inspect", "violation", "count"),
		"Count of violations",
		[]string{"target", "rule_name"},
		nil,
	)
)

var _ prometheus.Collector = &MetricReporter{}
var _ Reporter = &MetricReporter{}

type MetricReporter struct {
	cache *cache.Cache
}

func NewMetricReporter(config *config.MetricReporterConfig) (*MetricReporter, error) {
	err := config.Complete()
	if err != nil {
		return nil, fmt.Errorf("failed to complete config: %v", err)
	}

	r := &MetricReporter{
		cache: cache.New(config.TTL.Duration, config.CleanupInterval.Duration),
	}

	err = prometheus.Register(r)
	if err != nil {
		return nil, fmt.Errorf("failed to register metric exporter: %v", err)
	}

	return r, nil
}

func (r *MetricReporter) Report(ctx context.Context, result *api.Result) error {
	r.cache.SetDefault(result.Target, result)
	return nil
}

func (r *MetricReporter) Describe(ch chan<- *prometheus.Desc) {
	ch <- descForViolation
	ch <- descForViolationCount
}

func (r *MetricReporter) Collect(ch chan<- prometheus.Metric) {
	items := r.cache.Items()

	for _, item := range items {
		if item.Expired() {
			continue
		}

		result := item.Object.(*api.Result)
		counter := map[string]int{} // key:rule name

		for _, violation := range result.Violations {
			for _, rule := range violation.ViolatedRules {
				ch <- prometheus.MustNewConstMetric(
					descForViolation,
					prometheus.GaugeValue,
					1,
					result.Target,
					violation.Resource.APIVersion,
					violation.Resource.Kind,
					violation.Resource.Name,
					violation.Resource.Namespace,
					rule.Name,
					string(rule.Level),
				)
				counter[rule.Name] += 1
			}
		}

		for ruleName, count := range counter {
			ch <- prometheus.MustNewConstMetric(
				descForViolationCount,
				prometheus.GaugeValue,
				float64(count),
				result.Target,
				ruleName,
			)
		}
	}

}
