package pimport

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"
	"github.com/prometheus/client_golang/prometheus"
	"go.uber.org/zap"
	"gorm.io/gorm"
)

var ImportStatusCount = prometheus.NewDesc(
	prometheus.BuildFQName("dbaas", "import", "status_count"),
	"The execution result count of import",
	append(metrics.CommonLabels, metrics.ProviderTypeLbl, "status", "tenant_plan"),
	nil)

const ImportTimeOut = 8 * time.Hour

type StatusCollector struct{}

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

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

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

func (c *StatusCollector) Collect(ch chan<- prometheus.Metric) {
	var (
		imports  []entity.Import
		usedTime time.Duration
	)

	todayTime := time.Now().Truncate(24 * time.Hour)
	err := db.Transaction(func(tx *gorm.DB) error {
		return entity.NewImportQuerySet(tx.Preload("Cluster.Tenant")).
			CleanUpEq(false).PreloadCluster().
			CreatedAtGte(todayTime).
			All(&imports)
	})
	if err != nil {
		log.Error("Retrieve import job failed", zap.Error(err))
		return
	}

	importMap := make(map[entity.ID]*importObject)
	for _, im := range imports {
		if im.Cluster.Tenant.Kind != entity.TenantKindExternal {
			continue
		}
		if _, exist := importMap[im.ClusterID]; !exist {
			importMap[im.ClusterID] = &importObject{
				clusterName: im.Cluster.Name,
				tenantID:    im.Cluster.TenantID,
				tenantName:  im.Cluster.Tenant.Name,
				devTier:     im.Cluster.Free,
				succNum:     0,
				failNum:     0,
				tenantPlan:  string(im.Cluster.Tenant.Plan),
			}
		}
		if im.FinishedAt != nil {
			usedTime = im.FinishedAt.Sub(im.CreatedAt)
		} else {
			usedTime = time.Since(im.CreatedAt)
		}
		if usedTime > ImportTimeOut {
			importMap[im.ClusterID].failNum++
			continue
		}
		if im.Status == entity.ImportStatusCompleted {
			importMap[im.ClusterID].succNum++
		} else if im.Status == entity.ImportStatusFailed {
			importMap[im.ClusterID].failNum++
		}
	}

	for id, ob := range importMap {
		ch <- prometheus.MustNewConstMetric(
			ImportStatusCount,
			prometheus.CounterValue,
			ob.succNum,
			fmt.Sprintf("%d", ob.tenantID),
			ob.tenantName,
			fmt.Sprintf("%d", id),
			ob.clusterName,
			metrics.GetProviderType(ob.devTier),
			"ok",
			ob.tenantPlan,
		)

		ch <- prometheus.MustNewConstMetric(
			ImportStatusCount,
			prometheus.CounterValue,
			ob.failNum,
			fmt.Sprintf("%d", ob.tenantID),
			ob.tenantName,
			fmt.Sprintf("%d", id),
			ob.clusterName,
			metrics.GetProviderType(ob.devTier),
			"fail",
			ob.tenantPlan,
		)
	}
}
