package exporter

import (
	"fmt"
	"net/http"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"sigs.k8s.io/controller-runtime/pkg/log"

	"github.com/tidbcloud/infra-provider/pkg/util"
	"github.com/tidbcloud/infra-provider/quota-exporter/config"
)

// Registry is a registry of exporter factory.
type Registry struct {
	Exporters map[string]Factory
}

func NewRegistry() *Registry {
	return &Registry{
		Exporters: make(map[string]Factory),
	}
}

// Merge merges another registry into the current one.
func (r *Registry) Merge(other *Registry) {
	r.Exporters = util.MergeMaps(r.Exporters, other.Exporters)
}

// Expose creates all exporters in the resgitry and registers them to the http server.
//
// Exporters with the same metric path will be registered to the same http handler.
func (r *Registry) Expose(mux *http.ServeMux, cfg config.ExporterConfig, deps *Dependency) error {
	promRegistry := map[string]*prometheus.Registry{} // key: metric path

	for name, factory := range r.Exporters {
		exporter, err := factory(cfg, deps)
		if err != nil {
			return fmt.Errorf("failed to create exporter %s: %w", name, err)
		}
		metricPath := exporter.MetricPath()

		if _, ok := promRegistry[metricPath]; !ok {
			promRegistry[metricPath] = prometheus.NewRegistry()
		}
		err = promRegistry[metricPath].Register(exporter)
		if err != nil {
			return fmt.Errorf("failed to register exporter %s: %w", name, err)
		}
		log.Log.WithName("Setup").Info("created and registered a new exporter", "name", name, "metricPath", exporter.MetricPath())
	}

	for metricPath, registry := range promRegistry {
		mux.Handle(metricPath, promhttp.HandlerFor(registry, promhttp.HandlerOpts{}))
	}

	return nil
}
