package cluster

import (
	"fmt"
	"time"

	"github.com/pingcap/log"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/tidbcloud/metrics-server/central/db"
	"github.com/tidbcloud/metrics-server/central/db/entity"
	"go.uber.org/zap"
	"gorm.io/gorm"
)

var (
	TiDBScaleStatusCount = prometheus.NewDesc(
		prometheus.BuildFQName("dbaas", "tidb_cluster", "scale_status_count"),
		"Duration in seconds to wait for cluster ready",
		[]string{"cluster_id", "tenant_id", "status"},
		nil)
)

const expireTime = 20 * time.Minute

type ScaleObject struct {
	tenantID  entity.ID
	succNum   float64
	expireNum float64
}

type ScaleCollector struct{}

func newScaleCollector() *ScaleCollector {
	return &ScaleCollector{}
}

func (c *ScaleCollector) Describe(ch chan<- *prometheus.Desc) {
	ch <- TiDBScaleStatusCount
}

func (c *ScaleCollector) Collect(ch chan<- prometheus.Metric) {
	todayTime := time.Now().Truncate(24 * time.Hour)
	var (
		usedTime time.Duration
		ops      []entity.ClusterOperation
	)
	err := db.Transaction(func(tx *gorm.DB) error {
		return entity.NewClusterOperationQuerySet(tx).PreloadCluster().
			CreatedAtGte(todayTime).
			TypeEq(entity.ClusterOperationTypeScale).
			All(&ops)
	})
	if err != nil {
		log.Error("Retrieve ClusterOperation failed", zap.Error(err))
		return
	}
	opMap := make(map[entity.ID]*ScaleObject)
	for _, op := range ops {
		if _, ok := opMap[op.ClusterID]; !ok {
			opMap[op.ClusterID] = &ScaleObject{
				tenantID:  op.Cluster.TenantID,
				succNum:   0,
				expireNum: 0,
			}
		}
		if op.CompletedAt != nil {
			usedTime = op.CompletedAt.Sub(op.CreatedAt)
		} else {
			usedTime = time.Since(op.CreatedAt)
		}
		if usedTime > expireTime {
			opMap[op.ClusterID].expireNum++
		} else {
			opMap[op.ClusterID].succNum++
		}
	}
	for id, object := range opMap {
		ch <- prometheus.MustNewConstMetric(
			TiDBScaleStatusCount,
			prometheus.CounterValue,
			object.succNum,
			fmt.Sprintf("%d", id),
			fmt.Sprintf("%d", object.tenantID),
			"succ",
		)
		ch <- prometheus.MustNewConstMetric(
			TiDBScaleStatusCount,
			prometheus.CounterValue,
			object.expireNum,
			fmt.Sprintf("%d", id),
			fmt.Sprintf("%d", object.tenantID),
			"expire",
		)
	}
}
