package roomrocket

import (
	"context"
	"errors"
	"fmt"
	"slices"
	"strings"

	"hyapp-admin-server/internal/appctx"
	"hyapp-admin-server/internal/integration/roomclient"
)

const (
	roomRocketLevelCount = 5

	defaultFuelSource        = "heat_value"
	defaultLaunchDelayMS     = int64(30_000)
	defaultBroadcastScope    = "region"
	defaultRewardStackPolicy = "allow_stack"
)

var (
	ErrInvalidArgument = errors.New("invalid argument")

	defaultLevelFuelThresholds = []int64{1_000, 3_000, 6_000, 10_000, 15_000}
)

type Service struct {
	roomClient roomclient.Client
}

type RoomRocketConfig struct {
	AppCode           string                  `json:"appCode"`
	Enabled           bool                    `json:"enabled"`
	ConfigVersion     int64                   `json:"configVersion"`
	FuelSource        string                  `json:"fuelSource"`
	LaunchDelayMS     int64                   `json:"launchDelayMs"`
	BroadcastEnabled  bool                    `json:"broadcastEnabled"`
	BroadcastScope    string                  `json:"broadcastScope"`
	BroadcastDelayMS  int64                   `json:"broadcastDelayMs"`
	RewardStackPolicy string                  `json:"rewardStackPolicy"`
	Levels            []RoomRocketLevelConfig `json:"levels"`
	GiftFuelRules     []GiftFuelRuleConfig    `json:"giftFuelRules"`
	UpdatedByAdminID  int64                   `json:"updatedByAdminId"`
	CreatedAtMS       int64                   `json:"createdAtMs"`
	UpdatedAtMS       int64                   `json:"updatedAtMs"`
}

type RoomRocketLevelConfig struct {
	Level              int32                  `json:"level"`
	FuelThreshold      int64                  `json:"fuelThreshold"`
	CoverURL           string                 `json:"coverUrl"`
	AnimationURL       string                 `json:"animationUrl"`
	LaunchAnimationURL string                 `json:"launchAnimationUrl"`
	LaunchedImageURL   string                 `json:"launchedImageUrl"`
	InRoomRewards      []RoomRocketRewardItem `json:"inRoomRewards"`
	Top1Rewards        []RoomRocketRewardItem `json:"top1Rewards"`
	IgniterRewards     []RoomRocketRewardItem `json:"igniterRewards"`
}

type RoomRocketRewardItem struct {
	RewardItemID    string `json:"rewardItemId"`
	ResourceGroupID int64  `json:"resourceGroupId"`
	Weight          int64  `json:"weight"`
	Quantity        int64  `json:"quantity"`
	DisplayName     string `json:"displayName"`
	IconURL         string `json:"iconUrl"`
}

type GiftFuelRuleConfig struct {
	RuleID        string `json:"ruleId"`
	GiftID        string `json:"giftId"`
	GiftTypeCode  string `json:"giftTypeCode"`
	MultiplierPPM int64  `json:"multiplierPpm"`
	FixedFuel     int64  `json:"fixedFuel"`
	Excluded      bool   `json:"excluded"`
}

type updateRoomRocketConfigRequest struct {
	Enabled           bool                    `json:"enabled"`
	FuelSource        string                  `json:"fuelSource"`
	LaunchDelayMS     int64                   `json:"launchDelayMs"`
	BroadcastEnabled  bool                    `json:"broadcastEnabled"`
	BroadcastScope    string                  `json:"broadcastScope"`
	BroadcastDelayMS  int64                   `json:"broadcastDelayMs"`
	RewardStackPolicy string                  `json:"rewardStackPolicy"`
	Levels            []RoomRocketLevelConfig `json:"levels"`
	GiftFuelRules     []GiftFuelRuleConfig    `json:"giftFuelRules"`
}

func NewService(roomClient roomclient.Client) *Service {
	return &Service{roomClient: roomClient}
}

// GetConfig 读取当前 App 的语音房火箭配置；缺行时返回关闭态默认配置，便于首次进入后台页面。
func (s *Service) GetConfig(ctx context.Context) (RoomRocketConfig, error) {
	if s.roomClient == nil {
		return RoomRocketConfig{}, fmt.Errorf("room service client is not configured")
	}
	config, err := s.roomClient.GetRoomRocketConfig(ctx)
	if err != nil {
		return RoomRocketConfig{}, err
	}
	return configFromClient(config)
}

// UpdateConfig 保存房间火箭低频配置；版本号由后台递增，运行时只使用已提交版本。
func (s *Service) UpdateConfig(ctx context.Context, req updateRoomRocketConfigRequest, operatorAdminID int64) (RoomRocketConfig, error) {
	if s.roomClient == nil {
		return RoomRocketConfig{}, fmt.Errorf("room service client is not configured")
	}
	config, err := normalizeConfig(configFromUpdateRequest(ctx, req))
	if err != nil {
		return RoomRocketConfig{}, err
	}
	saved, err := s.roomClient.UpdateRoomRocketConfig(ctx, roomclient.UpdateRoomRocketConfigRequest{
		Config:  configToClient(config),
		AdminID: operatorAdminID,
	})
	if err != nil {
		return RoomRocketConfig{}, err
	}
	return configFromClient(saved)
}

func configFromUpdateRequest(ctx context.Context, req updateRoomRocketConfigRequest) RoomRocketConfig {
	// 房间火箭配置按 App 隔离；更新请求体不让前端传 app_code，必须使用中间件写入的上下文值。
	return RoomRocketConfig{
		AppCode:           appctx.FromContext(ctx),
		Enabled:           req.Enabled,
		FuelSource:        req.FuelSource,
		LaunchDelayMS:     req.LaunchDelayMS,
		BroadcastEnabled:  req.BroadcastEnabled,
		BroadcastScope:    req.BroadcastScope,
		BroadcastDelayMS:  req.BroadcastDelayMS,
		RewardStackPolicy: req.RewardStackPolicy,
		Levels:            req.Levels,
		GiftFuelRules:     req.GiftFuelRules,
	}
}

func defaultConfig(appCode string) RoomRocketConfig {
	levels := make([]RoomRocketLevelConfig, 0, roomRocketLevelCount)
	for index, threshold := range defaultLevelFuelThresholds {
		levels = append(levels, RoomRocketLevelConfig{
			Level:         int32(index + 1),
			FuelThreshold: threshold,
		})
	}
	return RoomRocketConfig{
		AppCode:           appCode,
		FuelSource:        defaultFuelSource,
		LaunchDelayMS:     defaultLaunchDelayMS,
		BroadcastEnabled:  true,
		BroadcastScope:    defaultBroadcastScope,
		RewardStackPolicy: defaultRewardStackPolicy,
		Levels:            levels,
		GiftFuelRules:     []GiftFuelRuleConfig{},
	}
}

func normalizeConfig(config RoomRocketConfig) (RoomRocketConfig, error) {
	config.AppCode = strings.TrimSpace(config.AppCode)
	if config.AppCode == "" {
		return RoomRocketConfig{}, fmt.Errorf("%w: app_code 不能为空", ErrInvalidArgument)
	}
	if config.ConfigVersion < 0 {
		return RoomRocketConfig{}, fmt.Errorf("%w: 配置版本不能小于 0", ErrInvalidArgument)
	}
	config.FuelSource = defaultString(strings.TrimSpace(config.FuelSource), defaultFuelSource)
	if config.FuelSource != defaultFuelSource {
		return RoomRocketConfig{}, fmt.Errorf("%w: 燃料来源不支持", ErrInvalidArgument)
	}
	if config.LaunchDelayMS < 0 {
		return RoomRocketConfig{}, fmt.Errorf("%w: 发射倒计时不能小于 0", ErrInvalidArgument)
	}
	config.BroadcastScope = defaultString(strings.TrimSpace(config.BroadcastScope), defaultBroadcastScope)
	if !stringIn(config.BroadcastScope, "none", "region", "global") {
		return RoomRocketConfig{}, fmt.Errorf("%w: 广播范围不支持", ErrInvalidArgument)
	}
	if config.BroadcastDelayMS < 0 {
		return RoomRocketConfig{}, fmt.Errorf("%w: 广播延迟不能小于 0", ErrInvalidArgument)
	}
	config.RewardStackPolicy = defaultString(strings.TrimSpace(config.RewardStackPolicy), defaultRewardStackPolicy)
	if !stringIn(config.RewardStackPolicy, "allow_stack", "priority_only") {
		return RoomRocketConfig{}, fmt.Errorf("%w: 奖励叠加策略不支持", ErrInvalidArgument)
	}
	levels, err := normalizeLevels(config.Levels)
	if err != nil {
		return RoomRocketConfig{}, err
	}
	config.Levels = levels
	giftFuelRules, err := normalizeGiftFuelRules(config.GiftFuelRules)
	if err != nil {
		return RoomRocketConfig{}, err
	}
	config.GiftFuelRules = giftFuelRules
	return config, nil
}

func normalizeLevels(levels []RoomRocketLevelConfig) ([]RoomRocketLevelConfig, error) {
	if len(levels) == 0 {
		return defaultConfig("placeholder").Levels, nil
	}
	if len(levels) != roomRocketLevelCount {
		return nil, fmt.Errorf("%w: 火箭等级必须配置 5 级", ErrInvalidArgument)
	}
	seen := make(map[int32]bool, roomRocketLevelCount)
	normalized := make([]RoomRocketLevelConfig, 0, roomRocketLevelCount)
	for _, level := range levels {
		if level.Level < 1 || level.Level > roomRocketLevelCount {
			return nil, fmt.Errorf("%w: 火箭等级只能是 1 到 5", ErrInvalidArgument)
		}
		if seen[level.Level] {
			return nil, fmt.Errorf("%w: 火箭等级不能重复", ErrInvalidArgument)
		}
		seen[level.Level] = true
		if level.FuelThreshold <= 0 {
			return nil, fmt.Errorf("%w: 第 %d 级燃料阈值必须大于 0", ErrInvalidArgument, level.Level)
		}
		level.CoverURL = strings.TrimSpace(level.CoverURL)
		level.AnimationURL = strings.TrimSpace(level.AnimationURL)
		level.LaunchAnimationURL = strings.TrimSpace(level.LaunchAnimationURL)
		level.LaunchedImageURL = strings.TrimSpace(level.LaunchedImageURL)
		var err error
		if level.InRoomRewards, err = normalizeRewardPool(level.Level, "in_room", level.InRoomRewards); err != nil {
			return nil, err
		}
		if level.Top1Rewards, err = normalizeRewardPool(level.Level, "top1", level.Top1Rewards); err != nil {
			return nil, err
		}
		if level.IgniterRewards, err = normalizeRewardPool(level.Level, "igniter", level.IgniterRewards); err != nil {
			return nil, err
		}
		normalized = append(normalized, level)
	}
	slices.SortFunc(normalized, func(left, right RoomRocketLevelConfig) int {
		return int(left.Level - right.Level)
	})
	return normalized, nil
}

func normalizeRewardPool(level int32, role string, rewards []RoomRocketRewardItem) ([]RoomRocketRewardItem, error) {
	normalized := make([]RoomRocketRewardItem, 0, len(rewards))
	for index, reward := range rewards {
		reward.RewardItemID = strings.TrimSpace(reward.RewardItemID)
		reward.DisplayName = strings.TrimSpace(reward.DisplayName)
		reward.IconURL = strings.TrimSpace(reward.IconURL)
		if reward.RewardItemID == "" && reward.ResourceGroupID == 0 && reward.Weight == 0 && reward.Quantity == 0 && reward.DisplayName == "" && reward.IconURL == "" {
			continue
		}
		if reward.ResourceGroupID <= 0 {
			return nil, fmt.Errorf("%w: 第 %d 级奖励资源组不能为空", ErrInvalidArgument, level)
		}
		if reward.Weight <= 0 {
			return nil, fmt.Errorf("%w: 第 %d 级奖励权重必须大于 0", ErrInvalidArgument, level)
		}
		if reward.Quantity <= 0 {
			// 历史配置没有 quantity 字段；后台读写都按 1 份兼容，避免旧配置打开页面即失败。
			reward.Quantity = 1
		}
		if reward.RewardItemID == "" {
			reward.RewardItemID = fmt.Sprintf("level_%d_%s_%d", level, role, index+1)
		}
		normalized = append(normalized, reward)
	}
	return normalized, nil
}

func normalizeGiftFuelRules(rules []GiftFuelRuleConfig) ([]GiftFuelRuleConfig, error) {
	normalized := make([]GiftFuelRuleConfig, 0, len(rules))
	for index, rule := range rules {
		rule.RuleID = strings.TrimSpace(rule.RuleID)
		rule.GiftID = strings.TrimSpace(rule.GiftID)
		rule.GiftTypeCode = strings.TrimSpace(rule.GiftTypeCode)
		if rule.RuleID == "" && rule.GiftID == "" && rule.GiftTypeCode == "" && rule.MultiplierPPM == 0 && rule.FixedFuel == 0 && !rule.Excluded {
			continue
		}
		if rule.GiftID == "" && rule.GiftTypeCode == "" {
			return nil, fmt.Errorf("%w: 礼物燃料规则必须选择礼物或礼物类型", ErrInvalidArgument)
		}
		if rule.MultiplierPPM < 0 || rule.FixedFuel < 0 {
			return nil, fmt.Errorf("%w: 礼物燃料规则不能配置负数", ErrInvalidArgument)
		}
		if !rule.Excluded && rule.MultiplierPPM == 0 && rule.FixedFuel == 0 {
			return nil, fmt.Errorf("%w: 礼物燃料规则必须配置倍率或固定燃料", ErrInvalidArgument)
		}
		if rule.RuleID == "" {
			rule.RuleID = fmt.Sprintf("gift_fuel_rule_%d", index+1)
		}
		normalized = append(normalized, rule)
	}
	return normalized, nil
}

func defaultString(value string, fallback string) string {
	if value == "" {
		return fallback
	}
	return value
}

func stringIn(value string, candidates ...string) bool {
	for _, candidate := range candidates {
		if value == candidate {
			return true
		}
	}
	return false
}

func configFromClient(input roomclient.RoomRocketConfig) (RoomRocketConfig, error) {
	config := RoomRocketConfig{
		AppCode:           input.AppCode,
		Enabled:           input.Enabled,
		ConfigVersion:     input.ConfigVersion,
		FuelSource:        input.FuelSource,
		LaunchDelayMS:     input.LaunchDelayMS,
		BroadcastEnabled:  input.BroadcastEnabled,
		BroadcastScope:    input.BroadcastScope,
		BroadcastDelayMS:  input.BroadcastDelayMS,
		RewardStackPolicy: input.RewardStackPolicy,
		Levels:            levelsFromClient(input.Levels),
		GiftFuelRules:     fuelRulesFromClient(input.GiftFuelRules),
		UpdatedByAdminID:  input.UpdatedByAdminID,
		CreatedAtMS:       input.CreatedAtMS,
		UpdatedAtMS:       input.UpdatedAtMS,
	}
	return normalizeConfig(config)
}

func configToClient(input RoomRocketConfig) roomclient.RoomRocketConfig {
	return roomclient.RoomRocketConfig{
		Enabled:           input.Enabled,
		FuelSource:        input.FuelSource,
		LaunchDelayMS:     input.LaunchDelayMS,
		BroadcastEnabled:  input.BroadcastEnabled,
		BroadcastScope:    input.BroadcastScope,
		BroadcastDelayMS:  input.BroadcastDelayMS,
		RewardStackPolicy: input.RewardStackPolicy,
		Levels:            levelsToClient(input.Levels),
		GiftFuelRules:     fuelRulesToClient(input.GiftFuelRules),
	}
}

func levelsFromClient(input []roomclient.RoomRocketLevelConfig) []RoomRocketLevelConfig {
	out := make([]RoomRocketLevelConfig, 0, len(input))
	for _, level := range input {
		out = append(out, RoomRocketLevelConfig{
			Level:              level.Level,
			FuelThreshold:      level.FuelThreshold,
			CoverURL:           level.CoverURL,
			AnimationURL:       level.AnimationURL,
			LaunchAnimationURL: level.LaunchAnimationURL,
			LaunchedImageURL:   level.LaunchedImageURL,
			InRoomRewards:      rewardsFromClient(level.InRoomRewards),
			Top1Rewards:        rewardsFromClient(level.Top1Rewards),
			IgniterRewards:     rewardsFromClient(level.IgniterRewards),
		})
	}
	return out
}

func levelsToClient(input []RoomRocketLevelConfig) []roomclient.RoomRocketLevelConfig {
	out := make([]roomclient.RoomRocketLevelConfig, 0, len(input))
	for _, level := range input {
		out = append(out, roomclient.RoomRocketLevelConfig{
			Level:              level.Level,
			FuelThreshold:      level.FuelThreshold,
			CoverURL:           level.CoverURL,
			AnimationURL:       level.AnimationURL,
			LaunchAnimationURL: level.LaunchAnimationURL,
			LaunchedImageURL:   level.LaunchedImageURL,
			InRoomRewards:      rewardsToClient(level.InRoomRewards),
			Top1Rewards:        rewardsToClient(level.Top1Rewards),
			IgniterRewards:     rewardsToClient(level.IgniterRewards),
		})
	}
	return out
}

func rewardsFromClient(input []roomclient.RoomRocketRewardItem) []RoomRocketRewardItem {
	out := make([]RoomRocketRewardItem, 0, len(input))
	for _, reward := range input {
		out = append(out, RoomRocketRewardItem{
			RewardItemID:    reward.RewardItemID,
			ResourceGroupID: reward.ResourceGroupID,
			Weight:          reward.Weight,
			Quantity:        reward.Quantity,
			DisplayName:     reward.DisplayName,
			IconURL:         reward.IconURL,
		})
	}
	return out
}

func rewardsToClient(input []RoomRocketRewardItem) []roomclient.RoomRocketRewardItem {
	out := make([]roomclient.RoomRocketRewardItem, 0, len(input))
	for _, reward := range input {
		out = append(out, roomclient.RoomRocketRewardItem{
			RewardItemID:    reward.RewardItemID,
			ResourceGroupID: reward.ResourceGroupID,
			Weight:          reward.Weight,
			Quantity:        reward.Quantity,
			DisplayName:     reward.DisplayName,
			IconURL:         reward.IconURL,
		})
	}
	return out
}

func fuelRulesFromClient(input []roomclient.GiftFuelRuleConfig) []GiftFuelRuleConfig {
	out := make([]GiftFuelRuleConfig, 0, len(input))
	for _, rule := range input {
		out = append(out, GiftFuelRuleConfig{
			RuleID:        rule.RuleID,
			GiftID:        rule.GiftID,
			GiftTypeCode:  rule.GiftTypeCode,
			MultiplierPPM: rule.MultiplierPPM,
			FixedFuel:     rule.FixedFuel,
			Excluded:      rule.Excluded,
		})
	}
	return out
}

func fuelRulesToClient(input []GiftFuelRuleConfig) []roomclient.GiftFuelRuleConfig {
	out := make([]roomclient.GiftFuelRuleConfig, 0, len(input))
	for _, rule := range input {
		out = append(out, roomclient.GiftFuelRuleConfig{
			RuleID:        rule.RuleID,
			GiftID:        rule.GiftID,
			GiftTypeCode:  rule.GiftTypeCode,
			MultiplierPPM: rule.MultiplierPPM,
			FixedFuel:     rule.FixedFuel,
			Excluded:      rule.Excluded,
		})
	}
	return out
}
