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 (
	restoreStatusCount = prometheus.NewDesc(
		prometheus.BuildFQName("dbaas", "restore", "status_total"),
		"The status of finished restore job",
		append(metrics.CommonLabels, metrics.ProviderTypeLbl, "status"),
		nil)
)

const RestoreTimeOut = 8 * time.Hour

type StatusCollector struct{}

type RestoreObject struct {
	clusterName string
	tenantID    entity.ID
	tenantName  string
	devTier     bool
	succNum     float64
	failNum     float64
}

func NewRestoreStatusCollector() prometheus.Collector {
	return &StatusCollector{}
}

func (c *StatusCollector) Describe(ch chan<- *prometheus.Desc) {
	ch <- restoreStatusCount
}

func (c *StatusCollector) Collect(ch chan<- prometheus.Metric) {
	// Retrieve today restore records
	var restores []entity.Restore
	err := db.Transaction(func(tx *gorm.DB) error {
		return entity.NewRestoreQuerySet(tx.Preload("Cluster.Tenant")).PreloadCluster().
			CreatedAtGte(time.Now().Truncate(24 * time.Hour)).
			All(&restores)
	})
	if err != nil {
		log.Error("Retrieve restore failed", zap.Error(err))
		return
	}
	var usedTime time.Duration
	restoreMap := make(map[entity.ID]*RestoreObject)
	for _, restore := range restores {
		if restore.Cluster.Tenant.Kind != entity.TenantKindExternal {
			continue
		}
		if _, exist := restoreMap[restore.ClusterID]; !exist {
			restoreMap[restore.ClusterID] = &RestoreObject{
				clusterName: restore.Cluster.Name,
				tenantID:    restore.Cluster.TenantID,
				tenantName:  restore.Cluster.Tenant.Name,
				devTier:     restore.Cluster.Free,
				succNum:     0,
				failNum:     0,
			}
		}
		if restore.Status == entity.RestoreStatus_SUCCESS && restore.FinishedAt != nil {
			usedTime = restore.FinishedAt.Sub(restore.CreatedAt)
		} else if restore.Status != entity.RestoreStatus_SUCCESS {
			usedTime = time.Since(restore.CreatedAt)
		}
		if usedTime > RestoreTimeOut {
			restoreMap[restore.ClusterID].failNum++
			continue
		}
		if restore.Status == entity.RestoreStatus_SUCCESS {
			restoreMap[restore.ClusterID].succNum++
		} else if restore.Status == entity.RestoreStatus_FAILED {
			restoreMap[restore.ClusterID].failNum++
		}
	}
	for id, r := range restoreMap {
		ch <- prometheus.MustNewConstMetric(
			restoreStatusCount,
			prometheus.CounterValue,
			r.succNum,
			fmt.Sprintf("%d", r.tenantID),
			r.tenantName,
			fmt.Sprintf("%d", id),
			r.clusterName,
			metrics.GetProviderType(r.devTier),
			"ok",
		)
		ch <- prometheus.MustNewConstMetric(
			restoreStatusCount,
			prometheus.CounterValue,
			r.succNum,
			fmt.Sprintf("%d", r.tenantID),
			r.tenantName,
			fmt.Sprintf("%d", id),
			r.clusterName,
			metrics.GetProviderType(r.devTier),
			"fail",
		)
	}

}
