package prometheus

import (
	"time"

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

type metricsCollector struct {
	maxUseCount               prometheus.Gauge
	maxTasksPerInstance       prometheus.Gauge
	scaleOperations           *prometheus.CounterVec
	taskOperations            *prometheus.CounterVec
	tasks                     *prometheus.GaugeVec
	taskInstanceReadinessTime prometheus.Histogram
	desiredInstances          prometheus.Gauge
}

func New(opts ...Option) *metricsCollector {
	var o options
	loadOptions(&o, opts)

	f := newFactory("fleeting", "taskscaler", o.constLabels)

	return &metricsCollector{
		maxTasksPerInstance:       f.NewGauge("max_tasks_per_instance", "Maximum number of tasks executed on a single instance"),
		maxUseCount:               f.NewGauge("max_use_count_per_instance", "Maximum number of times the instance can be used"),
		scaleOperations:           f.NewCounterVec("scale_operations_total", "Total number of tasks scaling operations", []string{"operation"}),
		taskOperations:            f.NewCounterVec("task_operations_total", "Counter of total operations performed on instances", []string{"operation"}),
		tasks:                     f.NewGaugeVec("tasks", "Current number of tasks in different states", []string{"state"}),
		taskInstanceReadinessTime: f.NewHistogram("task_instance_readiness_time_seconds", "Histogram of task instance readiness times", o.instanceReadinessTimeBuckets),
		desiredInstances:          f.NewGauge("desired_instances", "Current number of desired instances (negative means number of instances that should be requested for deletion)"),
	}
}

func (m *metricsCollector) MaxUseCountSet(v int) {
	m.maxUseCount.Set(float64(v))
}

func (m *metricsCollector) MaxTasksPerInstanceSet(v int) {
	m.maxTasksPerInstance.Set(float64(v))
}

func (m *metricsCollector) ScaleOperationInc(operation string) {
	m.scaleOperations.WithLabelValues(operation).Inc()
}

func (m *metricsCollector) TaskOperationInc(operation string) {
	m.taskOperations.WithLabelValues(operation).Inc()
}

func (m *metricsCollector) TasksCountSet(state string, v int) {
	m.tasks.WithLabelValues(state).Set(float64(v))
}

func (m *metricsCollector) TaskInstanceReadinessTimeObserve(d time.Duration) {
	m.taskInstanceReadinessTime.Observe(d.Seconds())
}

func (m *metricsCollector) DesiredInstancesSet(v int) {
	m.desiredInstances.Set(float64(v))
}

func (m *metricsCollector) Describe(ch chan<- *prometheus.Desc) {
	m.maxTasksPerInstance.Describe(ch)
	m.maxUseCount.Describe(ch)
	m.scaleOperations.Describe(ch)
	m.taskOperations.Describe(ch)
	m.tasks.Describe(ch)
	m.taskInstanceReadinessTime.Describe(ch)
	m.desiredInstances.Describe(ch)
}

func (m *metricsCollector) Collect(ch chan<- prometheus.Metric) {
	m.maxTasksPerInstance.Collect(ch)
	m.maxUseCount.Collect(ch)
	m.scaleOperations.Collect(ch)
	m.taskOperations.Collect(ch)
	m.tasks.Collect(ch)
	m.taskInstanceReadinessTime.Collect(ch)
	m.desiredInstances.Collect(ch)
}
