package broadcast

import (
	"context"
	"encoding/json"
	"fmt"
	"strings"

	"google.golang.org/protobuf/proto"
	luckygifteventsv1 "hyapp.local/api/proto/events/luckygift/v1"
	"hyapp/pkg/appcode"
	"hyapp/pkg/luckygiftmq"
	"hyapp/pkg/xerr"
	broadcastdomain "hyapp/services/activity-service/internal/domain/broadcast"
)

// HandleLuckyGiftEvent 把 lucky-gift-service 在钱包返奖成功后发布的 owner fact 投影成区域 IM outbox。
// MQ 事实只负责描述已发生的中奖；activity-service 独立拥有区域展示策略和腾讯 IM 投递状态，不能反写 owner outbox。
func (s *Service) HandleLuckyGiftEvent(ctx context.Context, envelope *luckygifteventsv1.EventEnvelope) (broadcastdomain.ConsumeLuckyGiftEventResult, error) {
	if envelope == nil {
		return broadcastdomain.ConsumeLuckyGiftEventResult{}, xerr.New(xerr.InvalidArgument, "lucky gift event envelope is required")
	}
	result := broadcastdomain.ConsumeLuckyGiftEventResult{
		EventID: envelope.GetEventId(),
		Status:  broadcastdomain.StatusSkipped,
	}
	if envelope.GetEventType() != luckygiftmq.EventTypeLuckyGiftDrawn {
		// 独立 topic 未来可能增加审计类事实；未知类型必须确认跳过，不能误生成客户端无法识别的播报。
		return result, nil
	}

	var event luckygifteventsv1.LuckyGiftDrawn
	if err := proto.Unmarshal(envelope.GetBody(), &event); err != nil {
		return broadcastdomain.ConsumeLuckyGiftEventResult{}, fmt.Errorf("decode lucky gift drawn body: %w", err)
	}
	if err := validateLuckyGiftDrawnEnvelope(envelope, &event); err != nil {
		return broadcastdomain.ConsumeLuckyGiftEventResult{}, err
	}
	// 区域顶部飘屏只接受已经实际到账的 10 倍及以上奖励。pending/failed 事实、0 奖励和无区域事实
	// 都属于合法跳过，避免展示先于钱包到账或把区域未知事件扩大成全局消息。
	if !shouldPublishLuckyGiftBigWin(&event) {
		return result, nil
	}

	eventCtx := appcode.WithContext(ctx, envelope.GetAppCode())
	sender, err := s.luckyGiftSenderProfile(eventCtx, &event)
	if err != nil {
		// 资料查询是完整 IM 快照的一部分；暂时失败必须交还 MQ 重试，不能先确认后永久退化成错误昵称。
		return broadcastdomain.ConsumeLuckyGiftEventResult{}, fmt.Errorf("resolve lucky gift sender profile: %w", err)
	}
	broadcastEventID := "lucky_gift_big_win:" + event.GetDrawId()
	payloadJSON, err := luckyGiftBigWinPayload(envelope, &event, broadcastEventID, sender, s.now().UTC().UnixMilli())
	if err != nil {
		return broadcastdomain.ConsumeLuckyGiftEventResult{}, err
	}
	published, err := s.PublishRegionBroadcast(eventCtx, PublishInput{
		EventID:       broadcastEventID,
		BroadcastType: broadcastdomain.TypeLuckyGiftBigWin,
		RegionID:      event.GetVisibleRegionId(),
		PayloadJSON:   payloadJSON,
	})
	if err != nil {
		return broadcastdomain.ConsumeLuckyGiftEventResult{}, err
	}
	// im_broadcast_outbox 的 (app_code,event_id) 是本投影的幂等事实；MQ 重投只会得到 Created=false，
	// 不会生成第二条区域消息，也不需要读取 lucky-gift-service 的 owner outbox 状态。
	return broadcastdomain.ConsumeLuckyGiftEventResult{
		EventID:          envelope.GetEventId(),
		Status:           published.Status,
		BroadcastEventID: published.EventID,
		BroadcastCreated: published.Created,
	}, nil
}

func validateLuckyGiftDrawnEnvelope(envelope *luckygifteventsv1.EventEnvelope, event *luckygifteventsv1.LuckyGiftDrawn) error {
	if strings.TrimSpace(envelope.GetAppCode()) == "" || strings.TrimSpace(envelope.GetEventId()) == "" {
		return xerr.New(xerr.InvalidArgument, "lucky gift event identity is incomplete")
	}
	drawID := strings.TrimSpace(event.GetDrawId())
	if drawID == "" || strings.TrimSpace(event.GetRoomId()) == "" || event.GetSenderUserId() <= 0 {
		return xerr.New(xerr.InvalidArgument, "lucky gift drawn fact is incomplete")
	}
	if envelopeDrawID := strings.TrimSpace(envelope.GetDrawId()); envelopeDrawID != "" && envelopeDrawID != drawID {
		return xerr.New(xerr.InvalidArgument, "lucky gift envelope draw_id does not match body")
	}
	return nil
}

func shouldPublishLuckyGiftBigWin(event *luckygifteventsv1.LuckyGiftDrawn) bool {
	if event == nil || !strings.EqualFold(strings.TrimSpace(event.GetRewardStatus()), "granted") {
		return false
	}
	return event.GetEffectiveRewardCoins() > 0 &&
		event.GetVisibleRegionId() > 0 &&
		event.GetMultiplierPpm() >= luckygiftmq.LuckyGiftDisplayMinMultiplierPPM
}

func (s *Service) luckyGiftSenderProfile(ctx context.Context, event *luckygifteventsv1.LuckyGiftDrawn) (SenderProfile, error) {
	profile := SenderProfile{
		UserID:              event.GetSenderUserId(),
		Account:             strings.TrimSpace(event.GetSenderDisplayUserId()),
		PrettyDisplayUserID: strings.TrimSpace(event.GetSenderPrettyDisplayUserId()),
		Nickname:            strings.TrimSpace(event.GetSenderName()),
		Avatar:              strings.TrimSpace(event.GetSenderAvatar()),
	}
	// 送礼主链路已经把展示快照写进 owner fact；字段齐全时禁止回查当前资料，避免用户改名后历史事实漂移。
	if profile.Nickname != "" && profile.Account != "" {
		return profile, nil
	}
	resolved, err := s.broadcastUserProfile(ctx, profile.UserID)
	if err != nil {
		return SenderProfile{}, err
	}
	if profile.Account == "" {
		profile.Account = strings.TrimSpace(resolved.Account)
	}
	if profile.PrettyDisplayUserID == "" {
		profile.PrettyDisplayUserID = strings.TrimSpace(resolved.PrettyDisplayUserID)
	}
	if profile.Nickname == "" {
		profile.Nickname = firstLuckyGiftDisplayValue(resolved.Nickname, profile.PrettyDisplayUserID, profile.Account)
	}
	if profile.Avatar == "" {
		profile.Avatar = strings.TrimSpace(resolved.Avatar)
	}
	return profile, nil
}

func firstLuckyGiftDisplayValue(values ...string) string {
	for _, value := range values {
		if normalized := strings.TrimSpace(value); normalized != "" {
			return normalized
		}
	}
	return ""
}

func luckyGiftBigWinPayload(envelope *luckygifteventsv1.EventEnvelope, event *luckygifteventsv1.LuckyGiftDrawn, eventID string, sender SenderProfile, sentAtMS int64) (string, error) {
	createdAtMS := event.GetDrawCreatedAtMs()
	if createdAtMS <= 0 {
		createdAtMS = envelope.GetOccurredAtMs()
	}
	// 字段保持拆服前 Flutter 已消费的扁平协议，并补充 granted/pretty ID 快照；不携带 gift_name，
	// 客户端继续用 gift_id 命中本地多语言礼物配置，避免服务端文案覆盖客户端翻译。
	action := map[string]any{
		"type":        "enter_room",
		"room_id":     event.GetRoomId(),
		"room_locked": event.GetRoomLocked(),
	}
	payload := map[string]any{
		"event_type":                    "lucky_gift_drawn",
		"event_id":                      eventID,
		"broadcast_type":                broadcastdomain.TypeLuckyGiftBigWin,
		"scope":                         broadcastdomain.ScopeRegion,
		"app_code":                      envelope.GetAppCode(),
		"region_id":                     event.GetVisibleRegionId(),
		"visible_region_id":             event.GetVisibleRegionId(),
		"draw_id":                       event.GetDrawId(),
		"command_id":                    event.GetCommandId(),
		"pool_id":                       event.GetPoolId(),
		"room_id":                       event.GetRoomId(),
		"room_locked":                   event.GetRoomLocked(),
		"gift_id":                       event.GetGiftId(),
		"gift_count":                    event.GetGiftCount(),
		"user_id":                       event.GetSenderUserId(),
		"sender_user_id":                event.GetSenderUserId(),
		"target_user_id":                event.GetTargetUserId(),
		"sender_name":                   sender.Nickname,
		"sender_avatar":                 sender.Avatar,
		"sender_display_user_id":        sender.Account,
		"sender_pretty_display_user_id": sender.PrettyDisplayUserID,
		"coin_spent":                    event.GetCoinSpent(),
		"rule_version":                  event.GetRuleVersion(),
		"experience_pool":               event.GetExperiencePool(),
		"selected_tier_id":              event.GetSelectedTierId(),
		"multiplier_ppm":                event.GetMultiplierPpm(),
		"base_reward_coins":             event.GetBaseRewardCoins(),
		"effective_reward_coins":        event.GetEffectiveRewardCoins(),
		"reward_status":                 "granted",
		"wallet_transaction_id":         event.GetWalletTransactionId(),
		"stage_feedback":                event.GetStageFeedback(),
		"high_multiplier":               event.GetHighMultiplier(),
		"created_at_ms":                 createdAtMS,
		"draw_created_at_ms":            createdAtMS,
		"reward_granted_at_ms":          event.GetRewardGrantedAtMs(),
		"sent_at_ms":                    sentAtMS,
		"action":                        action,
		"sender": map[string]any{
			"user_id":                sender.UserID,
			"display_user_id":        sender.Account,
			"pretty_display_user_id": sender.PrettyDisplayUserID,
			"nickname":               sender.Nickname,
			"avatar":                 sender.Avatar,
		},
	}
	setRoomOwnerSnapshot(payload, event.GetOwnerUserId())
	setRoomOwnerSnapshot(action, event.GetOwnerUserId())
	encoded, err := json.Marshal(payload)
	return string(encoded), err
}
