package statistic

import (
	"context"
	"fmt"
	"runtime"

	"go.uber.org/zap"

	entity2 "github.com/tidbcloud/dataflow-service/app/repo/entity"
	"github.com/tidbcloud/dataflow-service/internal/db"
	"github.com/tidbcloud/dataflow-service/internal/db/dbutil"
	"github.com/tidbcloud/dataflow-service/internal/metrics"
)

var (
	backupStatus     = []entity2.BackupStatus{entity2.BackupStatusPending, entity2.BackupStatusRunning, entity2.BackupStatusFailed, entity2.BackupStatusSuccess, entity2.BackupStatusSkipped, entity2.BackupStatusTerminated}
	restoreStatus    = []entity2.RestoreStatus{entity2.RestoreStatusRUNNING, entity2.RestoreStatusFAILED, entity2.RestoreStatusSUCCESS, entity2.RestoreStatusPENDING}
	changefeedStatus = []entity2.BizChangefeedOperationStatus{
		entity2.BizChangefeedOperationStatusCreating,
		entity2.BizChangefeedOperationStatusNormal,
		entity2.BizChangefeedOperationStatusFailed,
		entity2.BizChangefeedOperationStatusPaused,
		entity2.BizChangefeedOperationStatusScaling}
)

// Model is created with NewModel
type Model struct {
	log *zap.Logger
}

type DfsBusinessData struct {
	backup       map[string]int64
	restore      map[string]int64
	changefeed   map[string]int64
	goroutineNum int
}

func initDfsBusinessData() *DfsBusinessData {
	return &DfsBusinessData{
		backup:     make(map[string]int64),
		restore:    make(map[string]int64),
		changefeed: make(map[string]int64),
	}
}

func NewModel(log *zap.Logger) Model {
	return Model{
		log: log,
	}
}

// CollectCountStatisticDFSBusiness statistic dataflow service business
func (m Model) CollectCountStatisticDFSBusiness(ctx context.Context) error {
	data := m.collectCountDataFromDB(ctx)
	for status, num := range data.backup {
		metrics.BackupCounter.WithLabelValues(status, metrics.Dedicate).Observe(float64(num))
	}
	for status, num := range data.restore {
		metrics.RestoreCounter.WithLabelValues(status, metrics.Dedicate).Observe(float64(num))
	}
	for status, num := range data.changefeed {
		metrics.ChangefeedCounter.WithLabelValues(status).Observe(float64(num))
	}
	metrics.GoroutineCounter.WithLabelValues().Observe(float64(data.goroutineNum))
	return nil
}

func (m Model) collectCountDataFromDB(ctx context.Context) *DfsBusinessData {
	data := initDfsBusinessData()
	data.goroutineNum = runtime.NumGoroutine()
	m.log.Info(fmt.Sprintf("statistic goroutine number: %d", data.goroutineNum))
	for _, status := range backupStatus {
		dbIns := db.GetDBWithContext(ctx)
		if num, err := dbutil.CountDedicatedBackupsByStatus(dbIns, status); err == nil {
			data.backup[string(status)] = num
		}
	}
	for _, status := range restoreStatus {
		dbIns := db.GetDBWithContext(ctx)
		if num, err := dbutil.CountDedicatedRestoresByStatus(dbIns, status); err == nil {
			data.restore[string(status)] = num
		}
	}
	for _, status := range changefeedStatus {
		dbIns := db.GetDBWithContext(ctx)
		if num, err := dbutil.CountDedicatedChangefeedsByStatus(dbIns, status); err == nil {
			data.changefeed[string(status)] = num
		}
	}

	return data
}
