package restore

import (
	"fmt"
	"time"

	"github.com/pingcap/dbaas/central/db"
	"github.com/pingcap/dbaas/central/db/entity"
	"github.com/pingcap/dbaas/central/metrics"
	"github.com/pingcap/log"
	"gorm.io/gorm"

	"github.com/prometheus/client_golang/prometheus"
	"go.uber.org/zap"
)

var (
	restoreExecutionStatusDurationSeconds = prometheus.NewDesc(
		prometheus.BuildFQName("dbaas", "restore", "execution_status_duration_seconds"),
		"The time taken and status of restore job execution",
		append(metrics.CommonLabels, "restore_id", "restore_name", metrics.ProviderTypeLbl, "status"),
		nil)
)

func RegisterMetrics() {
	prometheus.MustRegister(NewMetricsCollector())
	prometheus.MustRegister(NewRestoreStatusCollector())
}

type MetricsCollector struct {
}

func NewMetricsCollector() prometheus.Collector {
	return &MetricsCollector{}
}

func (c *MetricsCollector) Describe(ch chan<- *prometheus.Desc) {
	ch <- restoreExecutionStatusDurationSeconds
}

func (c *MetricsCollector) Collect(ch chan<- prometheus.Metric) {
	var start = time.Now()
	defer func() {
		log.Info("restore metrics collect time", zap.Int64("time", time.Since(start).Milliseconds()))
	}()
	// Retrieve the last day backup records
	var restores []entity.Restore
	err := db.Transaction(func(tx *gorm.DB) error {
		return entity.NewRestoreQuerySet(tx.Preload("Cluster.Tenant")).PreloadCluster().
			CreatedAtGte(time.Now().Add(-24 * time.Hour)).
			All(&restores)
	})
	if err != nil {
		log.Error("Retrieve restore failed", zap.Error(err))
		return
	}

	for _, restore := range restores {
		clusterName := metrics.Unknown
		if restore.Cluster != nil {
			clusterName = restore.Cluster.Name
		}

		if restore.Cluster.Status.DeletedOrCleared() {
			continue
		}

		// unknows mean restore records created in database, but restore CR status not updated in database.
		status := metrics.Unknown
		if restore.Status != "" {
			status = string(restore.Status)
		}

		timeTaken := time.Since(restore.CreatedAt).Seconds()

		ch <- prometheus.MustNewConstMetric(
			restoreExecutionStatusDurationSeconds,
			prometheus.GaugeValue,
			timeTaken,
			fmt.Sprintf("%d", restore.Cluster.Tenant.ID),
			restore.Cluster.Tenant.Name,
			fmt.Sprintf("%d", restore.ClusterID),
			clusterName,
			fmt.Sprintf("%d", restore.ID),
			restore.Name,
			metrics.GetProviderType(restore.Cluster.Free),
			status,
		)
	}
}
