package luckygift

import (
	"fmt"
	"strings"

	"hyapp/pkg/xerr"
	domain "hyapp/services/activity-service/internal/domain/luckygift"
)

const (
	ppmScale              int64 = 1_000_000
	highTierMultiplierPPM int64 = 10_000_000
)

// DefaultRuleConfig 返回 v2 配置草稿。草稿默认 disabled，只有后台发布后才写入不可变版本表。
func DefaultRuleConfig(appCode, poolID string) domain.RuleConfig {
	poolID = normalizePoolID(poolID)
	return domain.RuleConfig{
		AppCode:               appCode,
		PoolID:                poolID,
		Enabled:               false,
		TargetRTPPPM:          950_000,
		PoolRatePPM:           960_000,
		SettlementWindowWager: 1_000_000,
		ControlBandPPM:        10_000,
		GiftPriceReference:    100,
		// 用户阶段按累计金币流水折算的等价抽数推进；后台仍看到“抽数”，运行侧不会再被不同价格礼物误导。
		NoviceMaxEquivalentDraws: 2_000,
		NormalMaxEquivalentDraws: 20_000,
		// v2 风控上限是绝对金币额，运行时只累计真实返奖；小额礼物不能把窗口上限按比例缩小。
		MaxSinglePayout:      50_000,
		UserHourlyPayoutCap:  34_200,
		UserDailyPayoutCap:   615_600,
		DeviceDailyPayoutCap: 1_026_000,
		RoomHourlyPayoutCap:  684_000,
		AnchorDailyPayoutCap: 12_312_000,
		Stages: []domain.RuleStage{
			defaultRuleStage(domain.StageNovice, []ruleTierSeed{
				{id: "novice_none", multiplierPPM: 0, weightPPM: 100_000},
				{id: "novice_0_5x", multiplierPPM: 500_000, weightPPM: 200_000},
				{id: "novice_1x", multiplierPPM: 1_000_000, weightPPM: 550_000},
				{id: "novice_2x", multiplierPPM: 2_000_000, weightPPM: 150_000},
			}),
			defaultRuleStage(domain.StageNormal, []ruleTierSeed{
				{id: "normal_none", multiplierPPM: 0, weightPPM: 100_000},
				{id: "normal_0_5x", multiplierPPM: 500_000, weightPPM: 200_000},
				{id: "normal_1x", multiplierPPM: 1_000_000, weightPPM: 550_000},
				{id: "normal_2x", multiplierPPM: 2_000_000, weightPPM: 150_000},
			}),
			defaultRuleStage(domain.StageAdvanced, []ruleTierSeed{
				{id: "advanced_none", multiplierPPM: 0, weightPPM: 270_000},
				{id: "advanced_1x", multiplierPPM: 1_000_000, weightPPM: 600_000},
				{id: "advanced_2x", multiplierPPM: 2_000_000, weightPPM: 100_000},
				{id: "advanced_5x", multiplierPPM: 5_000_000, weightPPM: 30_000},
			}),
		},
	}
}

type ruleTierSeed struct {
	id            string
	multiplierPPM int64
	weightPPM     int64
}

func defaultRuleStage(stage string, tiers []ruleTierSeed) domain.RuleStage {
	out := domain.RuleStage{Stage: stage, Tiers: make([]domain.RuleTier, 0, len(tiers))}
	for _, tier := range tiers {
		out.Tiers = append(out.Tiers, domain.RuleTier{
			Stage:          stage,
			TierID:         tier.id,
			MultiplierPPM:  tier.multiplierPPM,
			BaseWeightPPM:  tier.weightPPM,
			RewardSource:   domain.SourceBaseRTP,
			HighWaterOnly:  tier.multiplierPPM >= highTierMultiplierPPM,
			BroadcastLevel: domain.BroadcastNone,
			Enabled:        true,
		})
	}
	return out
}

func normalizeRuleConfig(config domain.RuleConfig) domain.RuleConfig {
	config.PoolID = normalizePoolID(config.PoolID)
	for stageIndex, stage := range config.Stages {
		stage.Stage = strings.TrimSpace(stage.Stage)
		for tierIndex, tier := range stage.Tiers {
			tier.Stage = strings.TrimSpace(tier.Stage)
			if tier.Stage == "" {
				tier.Stage = stage.Stage
			}
			tier.TierID = strings.TrimSpace(tier.TierID)
			tier.RewardSource = strings.TrimSpace(tier.RewardSource)
			if tier.RewardSource == "" {
				tier.RewardSource = domain.SourceBaseRTP
			}
			tier.BroadcastLevel = strings.TrimSpace(tier.BroadcastLevel)
			if tier.BroadcastLevel == "" {
				tier.BroadcastLevel = domain.BroadcastNone
			}
			stage.Tiers[tierIndex] = tier
		}
		config.Stages[stageIndex] = stage
	}
	return config
}

func validateRuleConfig(config domain.RuleConfig) error {
	if normalizePoolID(config.PoolID) == "" {
		return xerr.New(xerr.InvalidArgument, "lucky gift pool id is required")
	}
	if config.TargetRTPPPM < 100_000 || config.TargetRTPPPM > 990_000 {
		return xerr.New(xerr.InvalidArgument, "target RTP must be between 10% and 99%")
	}
	if config.PoolRatePPM < config.TargetRTPPPM || config.PoolRatePPM > ppmScale {
		return xerr.New(xerr.InvalidArgument, "pool rate must be between target RTP and 100%")
	}
	if config.SettlementWindowWager <= 0 {
		return xerr.New(xerr.InvalidArgument, "settlement window wager must be positive")
	}
	if config.ControlBandPPM <= 0 || config.ControlBandPPM > 50_000 {
		return xerr.New(xerr.InvalidArgument, "control band must be greater than 0 and no more than 5%")
	}
	if config.GiftPriceReference <= 0 {
		return xerr.New(xerr.InvalidArgument, "gift price reference must be positive")
	}
	if config.NoviceMaxEquivalentDraws < 0 || config.NormalMaxEquivalentDraws < config.NoviceMaxEquivalentDraws {
		return xerr.New(xerr.InvalidArgument, "equivalent draw stage thresholds are invalid")
	}
	// 风控上限是 v2 抽奖候选被 risk_cap 剪掉的直接来源，必须随规则版本固化，避免运行时继续吃代码硬编码。
	if config.MaxSinglePayout <= 0 || config.UserHourlyPayoutCap <= 0 || config.UserDailyPayoutCap <= 0 ||
		config.DeviceDailyPayoutCap <= 0 || config.RoomHourlyPayoutCap <= 0 || config.AnchorDailyPayoutCap <= 0 {
		return xerr.New(xerr.InvalidArgument, "risk payout caps must be positive")
	}
	stageMap := make(map[string]domain.RuleStage, len(config.Stages))
	for _, stage := range config.Stages {
		if !validRuleStage(stage.Stage) {
			return xerr.New(xerr.InvalidArgument, fmt.Sprintf("invalid lucky gift stage: %s", stage.Stage))
		}
		if _, exists := stageMap[stage.Stage]; exists {
			return xerr.New(xerr.InvalidArgument, fmt.Sprintf("duplicate lucky gift stage: %s", stage.Stage))
		}
		stageMap[stage.Stage] = stage
	}
	for _, stageName := range []string{domain.StageNovice, domain.StageNormal, domain.StageAdvanced} {
		stage, exists := stageMap[stageName]
		if !exists {
			return xerr.New(xerr.InvalidArgument, "lucky gift stages must include novice, normal and advanced")
		}
		if err := validateRuleStage(stage); err != nil {
			return err
		}
		if err := validateRuleStageExpectedRTP(stage, config.TargetRTPPPM, config.ControlBandPPM); err != nil {
			return err
		}
	}
	return nil
}

func validateRuleStage(stage domain.RuleStage) error {
	var weightSum int64
	hasBaseTier := false
	hasZeroTier := false
	for _, tier := range stage.Tiers {
		if tier.Stage != stage.Stage {
			return xerr.New(xerr.InvalidArgument, fmt.Sprintf("tier %s stage does not match %s", tier.TierID, stage.Stage))
		}
		if tier.TierID == "" {
			return xerr.New(xerr.InvalidArgument, fmt.Sprintf("%s tier id is required", stage.Stage))
		}
		if tier.MultiplierPPM < 0 {
			return xerr.New(xerr.InvalidArgument, fmt.Sprintf("%s tier multiplier is invalid", tier.TierID))
		}
		if tier.BaseWeightPPM < 0 {
			return xerr.New(xerr.InvalidArgument, fmt.Sprintf("%s tier probability is invalid", tier.TierID))
		}
		if !validRewardSource(tier.RewardSource) {
			return xerr.New(xerr.InvalidArgument, fmt.Sprintf("%s reward source is invalid", tier.TierID))
		}
		if !validBroadcastLevel(tier.BroadcastLevel) {
			return xerr.New(xerr.InvalidArgument, fmt.Sprintf("%s broadcast level is invalid", tier.TierID))
		}
		if tier.Enabled {
			weightSum += tier.BaseWeightPPM
			hasBaseTier = hasBaseTier || tier.RewardSource == domain.SourceBaseRTP
			hasZeroTier = hasZeroTier || tier.MultiplierPPM == 0
		}
		if tier.Enabled && tier.MultiplierPPM >= highTierMultiplierPPM && !tier.HighWaterOnly {
			return xerr.New(xerr.InvalidArgument, fmt.Sprintf("%s high multiplier tier must be high-water only", tier.TierID))
		}
	}
	if weightSum != ppmScale {
		return xerr.New(xerr.InvalidArgument, fmt.Sprintf("%s stage probability must sum to 100%%", stage.Stage))
	}
	if !hasBaseTier {
		return xerr.New(xerr.InvalidArgument, fmt.Sprintf("%s stage must include at least one base RTP tier", stage.Stage))
	}
	if !hasZeroTier {
		return xerr.New(xerr.InvalidArgument, fmt.Sprintf("%s stage must include explicit 0x tier", stage.Stage))
	}
	return nil
}

func validateRuleStageExpectedRTP(stage domain.RuleStage, targetRTPPPM int64, controlBandPPM int64) error {
	var expectedPPM int64
	for _, tier := range stage.Tiers {
		if !tier.Enabled || tier.RewardSource != domain.SourceBaseRTP {
			continue
		}
		expectedPPM += tier.MultiplierPPM * tier.BaseWeightPPM / ppmScale
	}
	lower := targetRTPPPM - controlBandPPM
	upper := targetRTPPPM + controlBandPPM
	if expectedPPM < lower || expectedPPM > upper {
		return xerr.New(xerr.InvalidArgument, fmt.Sprintf("%s stage expected RTP %.2f%% must be within target band %.2f%%-%.2f%%",
			stage.Stage,
			float64(expectedPPM)/10_000,
			float64(lower)/10_000,
			float64(upper)/10_000,
		))
	}
	return nil
}

func validRuleStage(stage string) bool {
	return stage == domain.StageNovice || stage == domain.StageNormal || stage == domain.StageAdvanced
}

func validRewardSource(source string) bool {
	return source == domain.SourceBaseRTP ||
		source == domain.SourceActivity ||
		source == domain.SourcePresentation
}

func validBroadcastLevel(level string) bool {
	return level == domain.BroadcastNone ||
		level == domain.BroadcastRoom ||
		level == domain.BroadcastRegion ||
		level == domain.BroadcastGlobal
}

func normalizePoolID(poolID string) string {
	poolID = strings.TrimSpace(poolID)
	if poolID == "" {
		return domain.DefaultPoolID
	}
	return poolID
}
