package tidbreclaimer

import (
	"context"
	"os"
	"time"

	"github.com/go-logr/logr"
	"github.com/pingcap/errors"
	"github.com/tidbcloud/aws-shared-provider/pkg/metrics"
	"github.com/tidbcloud/aws-shared-provider/pkg/tidbmanager"
	"github.com/tidbcloud/aws-shared-provider/pkg/types"
	"github.com/tidbcloud/aws-shared-provider/pkg/utils/query"
	"github.com/tidbcloud/aws-shared-provider/pkg/utils/tidb"
	ctrl "sigs.k8s.io/controller-runtime"
	"sigs.k8s.io/controller-runtime/pkg/client"
)

const (
	tidbStatsPort = 10080
)

type TidbReclaimer struct {
	logr.Logger

	tidbPoolNamespace string

	ctx            context.Context
	client         client.Client
	tManager       tidbmanager.TidbManager
	tidbController tidb.Controller
	gcInterval     time.Duration
	o11yQuerier    query.PromQuerier
}

func New(
	tidbPoolNamespace string,
	client client.Client,
	tManager tidbmanager.TidbManager,
	tidbController tidb.Controller,
	gcInterval time.Duration,
) *TidbReclaimer {
	return &TidbReclaimer{
		Logger: ctrl.Log.WithName("tidbreclaimer").WithName(tidbPoolNamespace),

		client:            client,
		tManager:          tManager,
		tidbController:    tidbController,
		gcInterval:        gcInterval,
		tidbPoolNamespace: tidbPoolNamespace,
	}
}

func (tr *TidbReclaimer) Start(ctx context.Context) error {
	tr.ctx = ctx
	tr.Info("start tidbreclaimer")

	queryURL := os.Getenv(types.O11yGlobalReadURL)
	if queryURL == "" {
		return errors.Errorf("env %s is empty", types.O11yGlobalReadURL)
	}
	tr.o11yQuerier = query.NewPromQueryer(nil, queryURL)

	checkTicker := time.NewTicker(tr.gcInterval)
	defer checkTicker.Stop()

	for {
		select {
		case <-checkTicker.C:
			tr.do()
		case <-tr.ctx.Done():
			err := tr.ctx.Err()
			tr.Error(err, "stop tidbreclaimer")
			return err
		}
	}
}

func (tr *TidbReclaimer) do() {
	start := time.Now()
	tr.V(2).Info("start tidbreclaimer check cycle")
	defer func() {
		tr.V(2).Info("finish tidbreclaimer check cycle", "time", time.Since(start))
	}()

	inactivePods, err := tr.QueryInactivePods()
	if err != nil || inactivePods == nil {
		tr.Error(err, "query o11y metrics")
		return
	}

	podsReclaimed := tr.ReclaimPods(inactivePods, tidbStatsPort)
	metrics.PodsNumGauge.WithLabelValues(tr.tidbPoolNamespace).Set(float64(podsReclaimed))
}
