package monitoring

import (
	"fmt"
	"net"

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

type listenerFactory func() (net.Listener, error)

type optionsConfig struct {
	listenerFactory      listenerFactory
	buildInfoGaugeLabels prometheus.Labels
}

// Option is used to pass options to NewListener.
type Option func(*optionsConfig)

func defaultOptions() optionsConfig {
	return optionsConfig{
		listenerFactory:      defaultListenerFactory,
		buildInfoGaugeLabels: prometheus.Labels{},
	}
}

func applyOptions(opts []Option) optionsConfig {
	config := defaultOptions()

	for _, v := range opts {
		v(&config)
	}

	return config
}

// WithListener will configure the health check endpoint to use the provided listener.
func WithListener(listener net.Listener) Option {
	return func(config *optionsConfig) {
		config.listenerFactory = func() (net.Listener, error) {
			return listener, nil
		}
	}
}

// WithListenerAddress will configure the health check endpoint to use the provided listener.
func WithListenerAddress(addr string) Option {
	return func(config *optionsConfig) {
		config.listenerFactory = func() (net.Listener, error) {
			return net.Listen("tcp", addr)
		}
	}
}

// WithBuildInformation will configure the `gitlab_build_info` metric with appropriate labels
func WithBuildInformation(version string, buildTime string) Option {
	return func(config *optionsConfig) {
		config.buildInfoGaugeLabels[buildInfoVersionLabel] = version
		config.buildInfoGaugeLabels[buildInfoBuildTimeLabel] = buildTime
	}
}

// WithBuildExtraLabels will configure extra labels on the `gitlab_build_info` metric
func WithBuildExtraLabels(labels map[string]string) Option {
	return func(config *optionsConfig) {
		for key, v := range labels {
			config.buildInfoGaugeLabels[key] = v
		}
	}
}

// By default throw an error
func defaultListenerFactory() (net.Listener, error) {
	return nil, fmt.Errorf("no listener option provided for health check")
}
