package workers

import (
	"math/rand"
	"reflect"
	"runtime"
	"time"

	"github.com/go-co-op/gocron"
	"github.com/pingcap/dbaas/billing2/config"
	"github.com/pingcap/dbaas/billing2/utils/log"
	"github.com/pingcap/dbaas/billing2/utils/panicIf"
	"github.com/pingcap/dbaas/billing2/utils/wrapworker"
	"go.uber.org/zap"
)

func RunWorker(maxStartDuration time.Duration) {
	workerConfigs := []struct {
		JobFunc          interface{}
		Interval         string
		runInPersonalEnv bool
	}{
		{CreateInvoiceWorker, config.Get().GetString(config.InvoiceWorkerCheckInterval), false},
		{SyncTenantPayableWorker, config.Get().GetString(config.SyncPayableWorkerInterval), true},
		{RemoveUnusedRawCosts, config.Get().GetString(config.RemoveUnusedCostsInterval), false},
		{RemoveUnusedCosts, config.Get().GetString(config.RemoveMergedCostsInterval), false},
		{MergeSmallCosts, config.Get().GetString(config.MergeSmallCostsInterval), false},
		{SendSupportPlanMetering, config.Get().GetString(config.SupportPlanMeteringInterval), false},
		{CloseExhaustedCreditsPackage, config.Get().GetString(config.CreditsWorkerInterval), false},
		{CloseExpiredCreditsPackage, config.Get().GetString(config.CreditsWorkerInterval), false},
		{OpenNeedOpenCreditsPackage, config.Get().GetString(config.CreditsWorkerInterval), false},
		{CreateCostQuotaInvoice, config.Get().GetString(config.InvoiceWorkerCheckInterval), false},
		{RefundForCreditCardVerify, config.Get().GetString(config.RefundWorkerInterval), false},
		{RefundCheckForCreditCardVerify, config.Get().GetString(config.RefundCheckWorkerInterval), false},
		{ProgressTenantCreditRiskClose, config.Get().GetString(config.TenantCreditRiskCloseInterval), false},
		{POCSyncCredits, config.Get().GetString(config.PlanWorkerInterval), false},
		{POCCleanup, config.Get().GetString(config.PlanWorkerInterval), false},
		{POCCreditsEmailReminder, config.Get().GetString(config.PlanWorkerInterval), false},
		{CheckAllSpendLimit, config.Get().GetString(config.CheckSpendLimitInterval), false},
		{RechargeCreditWorker, config.Get().GetString(config.RechargeCreditWorkerInterval), false},
		{ExpireCreditRechargePlan, config.Get().GetString(config.RechargeCreditWorkerInterval), false},
		{InactiveTimeoutDiscountWorker, config.Get().GetString(config.InactiveDiscountWorkerInterval), false},
		{InactiveTimeoutBindedPackagesWorker, config.Get().GetString(config.InactiveBindedPackageWorkerInterval), false},
		{CheckSupportPlanAccuracy, config.Get().GetString(config.CheckSupportPlanAccuracyWorkerInterval), false},
		{SyncMarketOrgBills, config.Get().GetString(config.SyncMarketBillsWorkerInterval), false},
		{GenerateAWSDailyReportWorker, config.Get().GetString(config.AWSMarketplaceDailyReportGenerateInterval), false},
		{ReconciliationDailyWorker, config.Get().GetString(config.ReconciliationDailyWorkerInterval), false},
		{ReconciliationMonthlyWorker, config.Get().GetString(config.ReconciliationMonthlyWorkerInterval), false},
		{BillingConfigScheduledExecution, config.Get().GetString(config.BillingConfigWorkerInterval), false},
		{MarketplacePaymentWorker, config.Get().GetString(config.MarketplacePaymentInterval), false},
		{NoTrafficDedicatedOrgCheckWorker, config.Get().GetString(config.NoTrafficCheckerRunInterval), false},
		{CostExplorerCacheWorker, config.Get().GetString(config.CostExplorerCacheWorkerInterval), false},
		{SendSupportPlanFreeTrialReminder, config.Get().GetString(config.SupportPlanFreeTrialReminderWorkerInterval), false},
		{AREstimatedWorker, config.Get().GetString(config.ARTableEstimatedWorkerInterval), false},
		{ARActualWorker, config.Get().GetString(config.ARTableActualWorkerInterval), false},
		{S3BillsDetailExportWorker, config.Get().GetString(config.S3BillsDetailWorkerInterval), false},
		{SendLakeCloudServiceFeeMetering, config.Get().GetString(config.LakeCloudServiceFeeInterval), false},
		{SendAgenticPlanMetering, config.Get().GetString(config.AgenticPlanWorkerInterval), false},
		{PricingEventArchiveWorker, config.Get().GetString(config.PricingEventArchiveWorkerInterval), false},
		{MeteringEventArchiveWorker, config.Get().GetString(config.MeteringEventArchiveWorkerInterval), false},
		{StripeInvoicePayingWorker, config.Get().GetString(config.StripeInvoicePayingWorkerInterval), false},
		{StripeInvoicePayingJanitorWorker, config.Get().GetString(config.StripeInvoicePayingJanitorInterval), false},
	}
	workerSchedules := make([]*gocron.Scheduler, len(workerConfigs))
	// Dump effective intervals at startup so dev can grep one line and
	// confirm whether StripeInvoicePayingWorker is actually on 1m. Each
	// entry's string is the raw value viper gave us — the gocron parser
	// only rejects malformed values, so a misconfigured oneconfig (e.g.
	// "1m0s" vs "1m") would silently turn the worker into a never-fire.
	intervals := make([]zap.Field, 0, len(workerConfigs))
	for i, conf := range workerConfigs {
		f := reflect.ValueOf(conf.JobFunc)
		intervals = append(intervals, zap.String(runtime.FuncForPC(f.Pointer()).Name(), conf.Interval))
		s, err := wrapworker.NewWorker(conf.JobFunc, conf.Interval, conf.runInPersonalEnv)
		panicIf.Error(err)
		workerSchedules[i] = s
	}
	log.Info("worker scheduler starting", zap.Int("workerCount", len(workerConfigs)))
	for _, f := range intervals {
		log.Info("worker interval", f)
	}
	for _, s := range workerSchedules {
		time.Sleep(maxStartDuration * time.Duration(rand.Float32()*100) / 100)
		s.StartAsync()
	}
}
