package metrics

import (
	"strconv"
	"time"

	"git.frostfs.info/TrueCloudLab/frostfs-observability/metrics"
	"github.com/prometheus/client_golang/prometheus"
)

type GCMetrics interface {
	AddRunDuration(shardID string, d time.Duration, success bool)
	AddDeletedCount(shardID string, deleted, failed uint64)
	AddExpiredObjectCollectionDuration(shardID string, d time.Duration, success bool, objectType string)
	AddInhumedObjectCount(shardID string, count uint64, objectType string)
}

type gcMetrics struct {
	runDuration        *prometheus.CounterVec
	deletedCounter     *prometheus.CounterVec
	expCollectDuration *prometheus.CounterVec
	inhumedCounter     *prometheus.CounterVec
}

func newGCMetrics() *gcMetrics {
	return &gcMetrics{
		runDuration: metrics.NewCounterVec(prometheus.CounterOpts{
			Namespace: namespace,
			Subsystem: gcSubsystem,
			Name:      "delete_duration_seconds",
			Help:      "The total time of GC runs to delete objects from disk",
		}, []string{shardIDLabel, successLabel}),
		deletedCounter: metrics.NewCounterVec(prometheus.CounterOpts{
			Namespace: namespace,
			Subsystem: gcSubsystem,
			Name:      "deleted_objects_total",
			Help:      "Total count of objects GC deleted or failed to delete from disk",
		}, []string{shardIDLabel, statusLabel}),
		expCollectDuration: metrics.NewCounterVec(prometheus.CounterOpts{
			Namespace: namespace,
			Subsystem: gcSubsystem,
			Name:      "marking_duration_seconds",
			Help:      "The total time of GC runs to mark expired objects as removed",
		}, []string{shardIDLabel, successLabel, objectTypeLabel}),
		inhumedCounter: metrics.NewCounterVec(prometheus.CounterOpts{
			Namespace: namespace,
			Subsystem: gcSubsystem,
			Name:      "marked_for_removal_objects_total",
			Help:      "Total count of expired objects GC marked to remove",
		}, []string{shardIDLabel, objectTypeLabel}),
	}
}

func (m *gcMetrics) AddRunDuration(shardID string, d time.Duration, success bool) {
	m.runDuration.With(prometheus.Labels{
		shardIDLabel: shardID,
		successLabel: strconv.FormatBool(success),
	}).Add(d.Seconds())
}

func (m *gcMetrics) AddDeletedCount(shardID string, deleted, failed uint64) {
	m.deletedCounter.With(
		prometheus.Labels{
			shardIDLabel: shardID,
			statusLabel:  deletedStatus,
		}).Add(float64(deleted))
	m.deletedCounter.With(
		prometheus.Labels{
			shardIDLabel: shardID,
			statusLabel:  failedToDeleteStatus,
		}).Add(float64(failed))
}

func (m *gcMetrics) AddExpiredObjectCollectionDuration(shardID string, d time.Duration, success bool, objectType string) {
	m.expCollectDuration.With(prometheus.Labels{
		shardIDLabel:    shardID,
		successLabel:    strconv.FormatBool(success),
		objectTypeLabel: objectType,
	}).Add(d.Seconds())
}

func (m *gcMetrics) AddInhumedObjectCount(shardID string, count uint64, objectType string) {
	m.inhumedCounter.With(
		prometheus.Labels{
			shardIDLabel:    shardID,
			objectTypeLabel: objectType,
		}).Add(float64(count))
}