package promotion

import (
	"math/rand"
	"time"

	"github.com/pingcap/dbaas/billing2/config"
	"github.com/pingcap/dbaas/billing2/forms"
	"github.com/pingcap/dbaas/billing2/handler/eventbushandler"
	"github.com/pingcap/dbaas/billing2/model/payment/paymentsub"
	"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"
)

const (
	TenantRegisteredEventType = "tenant_registered"
	TenantPayableEventType    = "tenant-payable"
	PromotionGroup            = "promotion"
)

// TenantRegisteredEvent carries tenant_id + referral_code from account's tenant_registered event.
type TenantRegisteredEvent struct {
	TenantID     uint   `json:"tenant_id"`
	ReferralCode string `json:"referral_code"`
}

// RegisterEventHandlers registers all promotion event handlers on billing's eventbus.
func RegisterEventHandlers() error {
	log.Info("promotion: registering event handlers")

	err := eventbushandler.Get().RegisterEventHandleFunc(
		TenantRegisteredEventType,
		PromotionGroup,
		TenantRegisteredEvent{},
		func(event *eventbushandler.Event) error {
			data := event.EventData.(*TenantRegisteredEvent)
			log.Info("promotion: received tenant_registered event",
				zap.Uint("tenantID", data.TenantID),
				zap.String("referralCode", data.ReferralCode))
			// Referrer (A) gets signup reward
			if err := RewardReferralSignup(data.ReferralCode, data.TenantID); err != nil {
				return err
			}
			// New user (B) gets referee_signup reward
			return RewardNewUserSignup(data.ReferralCode, data.TenantID)
		},
	)
	if err != nil {
		log.Error("promotion: failed to register tenant_registered handler", zap.Error(err))
		return err
	}

	err = eventbushandler.Get().RegisterEventHandleFunc(
		TenantPayableEventType,
		PromotionGroup,
		forms.TenantID{},
		func(event *eventbushandler.Event) error {
			data := event.EventData.(*forms.TenantID)
			log.Info("promotion: received tenant-payable event",
				zap.Uint("tenantID", data.TenantID))
			// Referrer (A) gets bindcard reward
			if err := RewardReferralBindCard(data.TenantID); err != nil {
				return err
			}
			// New user (B) gets referee_bindcard reward
			return RewardNewUserBindCard(data.TenantID)
		},
	)
	if err != nil {
		log.Error("promotion: failed to register tenant-payable handler", zap.Error(err))
		return err
	}

	err = eventbushandler.Get().RegisterEventHandleFunc(
		paymentsub.InvoicePaidEventType,
		PromotionGroup,
		forms.InvoicePaidEvent{},
		func(event *eventbushandler.Event) error {
			data := event.EventData.(*forms.InvoicePaidEvent)
			log.Info("promotion: received invoice-paid event",
				zap.Uint("tenantID", data.TenantID),
				zap.String("paidAmount", data.PaidAmount.String()))
			RewardCommunityPayment(data)
			return nil
		},
	)
	if err != nil {
		log.Error("promotion: failed to register invoice-paid handler", zap.Error(err))
		return err
	}

	log.Info("promotion: event handlers registered successfully")
	return nil
}

// RunChecker starts the promotion checker jobs using gocron.
func RunChecker() {
	interval := config.Get().GetString(config.PromotionCheckInterval)

	checkerJobs := []struct {
		JobFunc  interface{}
		Interval string
	}{
		// 新用户激励 (ContinuousUse also triggers referee_continuous_use for referrer)
		{RewardFirstUse, interval},
		{RewardContinuousUse, interval},
	}

	for _, job := range checkerJobs {
		s, err := wrapworker.NewWorker(job.JobFunc, job.Interval, false)
		panicIf.Error(err)
		time.Sleep(time.Duration(rand.Float32()*1000) * time.Millisecond)
		s.StartAsync()
	}

	log.Info("promotion checker started", zap.String("interval", interval))
}
