package resourcepool

import (
	"context"
	"fmt"
	"time"

	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"

	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/repository/rcuquota"
	resourcepoolrepo "github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/repository/resourcepool"
	resourcepoolsvc "github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/service/resourcepool"
	"github.com/tidbcloud/tidb-management-service/utilities/idgenerator"
)

type Worker interface {
	HandleIdleResourcePools(ctx context.Context) error
	HandleAutomaticResumeResourcePools(ctx context.Context) error
	HandleExpiredPauseAdmissions(ctx context.Context) error
}

type worker struct {
	resourcePoolRepo resourcepoolrepo.Repo
	resourcePoolSvc  resourcepoolsvc.Service
	rcuQuotaRepo     rcuquota.Repo
	idGenerator      idgenerator.IDGenerator
}

func (w *worker) HandleExpiredPauseAdmissions(ctx context.Context) error {
	if berr := w.resourcePoolSvc.ExpirePauseAdmissions(ctx); berr != nil {
		tilog.Error(ctx, "failed to expire resource pool pause admissions", zap.Error(berr))
		return fmt.Errorf("failed to expire resource pool pause admissions: %w", berr)
	}
	return nil
}

func NewWorker(
	resourcePoolRepo resourcepoolrepo.Repo,
	resourcePoolSvc resourcepoolsvc.Service,
	rcuQuotaRepo rcuquota.Repo,
	idGenerator idgenerator.IDGenerator,
) Worker {
	return &worker{
		resourcePoolRepo: resourcePoolRepo,
		resourcePoolSvc:  resourcePoolSvc,
		rcuQuotaRepo:     rcuQuotaRepo,
		idGenerator:      idGenerator,
	}
}

func (w *worker) HandleIdleResourcePools(ctx context.Context) error {
	CheckIdleWorkerRunsTotal.Inc()
	IdleSeconds.Reset()

	tilog.Info(ctx, "checking idle resource pools")
	now := time.Now().UTC()

	idlePools, bizErr := w.resourcePoolRepo.ListIdle(ctx)
	if bizErr != nil {
		CheckIdleErrorTotal.WithLabelValues(CheckIdleErrorTypeListIdle).Inc()
		tilog.Error(ctx, "failed to list idle resource pools", zap.Error(bizErr))
		return fmt.Errorf("failed to list idle resource pools: %w", bizErr)
	}

	tilog.Info(ctx, "found idle resource pools",
		zap.Int("number", len(idlePools)),
		zap.Any("idle_pools_data", idlePools))

	for _, pool := range idlePools {
		idleDurationSeconds := now.Sub(pool.IdleStartTime).Seconds()

		IdleSeconds.WithLabelValues(
			fmt.Sprintf("%d", pool.ResourcePoolId),
			fmt.Sprintf("%d", pool.OwnerId),
		).Set(idleDurationSeconds)

		tilog.Debug(
			ctx,
			"recorded idle resource pool metric",
			zap.Uint64("resource_pool_id", pool.ResourcePoolId),
			zap.Uint64("owner_id", pool.OwnerId),
			zap.Time("idle_start_time", pool.IdleStartTime),
			zap.Float64("idle_duration_s", idleDurationSeconds),
		)

		tryToDelete, err := w.deleteIfOverIdleTtl(ctx, pool)

		if tryToDelete {
			DeleteIdleResourcePoolRunsTotal.Inc()
			if err != nil {
				DeleteIdleResourcePoolFailedTotal.Inc()
			}
		}
	}

	tilog.Info(ctx, "finished checking idle resource pools successfully")
	return nil
}
