package workers

import (
	"time"

	"github.com/google/uuid"
	"go.uber.org/zap"

	"github.com/pingcap/dbaas/billing2/config"
	"github.com/pingcap/dbaas/billing2/model/billing"
	"github.com/pingcap/dbaas/billing2/utils/concurrency"
	"github.com/pingcap/dbaas/billing2/utils/log"
	"github.com/pingcap/dbaas/billing2/utils/timeutils"
)

// lakeCloudServiceFeeDate returns the measure date (yesterday).
func lakeCloudServiceFeeDate() time.Time {
	now := timeutils.GetNow()
	return timeutils.GetDayDate(now.Add(-time.Hour * 24))
}

// RunLakeCloudServiceFeeByTenant computes yesterday's fee and, if the
// calculation actually ran (first time today), compensates the previous
// N days for late-arriving data. No time gate — intended for manual
// triggering via the /worker/subtask/run endpoint.
func RunLakeCloudServiceFeeByTenant(traceID string, tenantID uint) {
	measureDate := lakeCloudServiceFeeDate()
	compensationDays := config.Get().GetInt(config.LakeCloudServiceFeeCompensationDays)

	calculated, _ := billing.CalculateLakeCloudServiceFee(traceID, tenantID, measureDate)

	if !calculated {
		return
	}
	for i := 1; i <= compensationDays; i++ {
		day := measureDate.AddDate(0, 0, -i)
		_ = billing.CompensateLakeCloudServiceFee(traceID, tenantID, day)
	}
}

// RunLakeCloudServiceFee runs calculation + compensation for all
// whitelisted tenants without any time gate. Intended for manual
// triggering via the /worker/run endpoint.
func RunLakeCloudServiceFee() error {
	traceID := uuid.NewString()
	tenantIDs := billing.GetEnableLakeCloudServiceFeeTenantIDs()
	log.Info("Lake Cloud Service Fee run start",
		zap.String("traceID", traceID),
		zap.Any("tenantIDs", tenantIDs))
	return concurrency.RunInPoolWithIDs(
		config.Get().GetUint(config.DefaultWorkerConcurrency),
		func(tenantID uint) {
			RunLakeCloudServiceFeeByTenant(traceID, tenantID)
		},
		tenantIDs,
	)
}

// SendLakeCloudServiceFeeMetering is the scheduled worker entry point.
// It applies the delay-hours gate (ensures yesterday's metering data has
// settled) then delegates to RunLakeCloudServiceFee.
func SendLakeCloudServiceFeeMetering() error {
	now := timeutils.GetNow()
	delayHours := config.Get().GetInt(config.LakeCloudServiceFeeDelayHours)
	if int(now.Sub(timeutils.GetDayDate(now)).Hours()) < delayHours {
		return nil
	}
	return RunLakeCloudServiceFee()
}
