package service

import (
	"context"
	"errors"
	"fmt"
	"strings"
	"time"

	"google.golang.org/protobuf/proto"
	luckygifteventsv1 "hyapp.local/api/proto/events/luckygift/v1"
	roomeventsv1 "hyapp.local/api/proto/events/room/v1"
	"hyapp/pkg/appcode"
	"hyapp/pkg/luckygiftmq"
	"hyapp/services/room-service/internal/room/outbox"
)

const roomLuckyGiftDrawnEventType = "RoomLuckyGiftDrawn"

// HandleLuckyGiftOutboxEvent 把 lucky-gift-service 已完成返奖的 owner 事实持久化为房间展示 outbox。
// 中奖展示不改变房间状态，因此不能伪造 Room Cell 命令、推进 room_version 或写 command log；
// room_outbox 的稳定 event_id 同时承担本服务消费幂等和腾讯云 IM 失败补偿。
func (s *Service) HandleLuckyGiftOutboxEvent(ctx context.Context, envelope *luckygifteventsv1.EventEnvelope) error {
	if s == nil || s.repository == nil {
		return errors.New("room repository is not configured")
	}
	if envelope == nil || envelope.GetEventType() != luckygiftmq.EventTypeLuckyGiftDrawn {
		return errors.New("unexpected lucky gift outbox event")
	}

	var drawn luckygifteventsv1.LuckyGiftDrawn
	if err := proto.Unmarshal(envelope.GetBody(), &drawn); err != nil {
		return fmt.Errorf("decode lucky gift drawn body: %w", err)
	}
	if err := validateLuckyGiftDrawnEnvelope(envelope, &drawn); err != nil {
		return err
	}

	// owner 发布全部正向返奖事实；房间金色飘屏只消费达到统一 10x 门槛的 granted 中奖。
	// 低倍事实没有房间副作用，重复投递仍保持同样的 no-op 结果。
	if !strings.EqualFold(strings.TrimSpace(drawn.GetRewardStatus()), "granted") ||
		drawn.GetEffectiveRewardCoins() <= 0 ||
		drawn.GetMultiplierPpm() < luckygiftmq.LuckyGiftDisplayMinMultiplierPPM {
		return nil
	}

	body := &roomeventsv1.RoomLuckyGiftDrawn{
		DrawId:                    drawn.GetDrawId(),
		CommandId:                 drawn.GetCommandId(),
		PoolId:                    drawn.GetPoolId(),
		GiftId:                    drawn.GetGiftId(),
		GiftCount:                 drawn.GetGiftCount(),
		SenderUserId:              drawn.GetSenderUserId(),
		TargetUserId:              drawn.GetTargetUserId(),
		SenderName:                drawn.GetSenderName(),
		SenderAvatar:              drawn.GetSenderAvatar(),
		SenderDisplayUserId:       drawn.GetSenderDisplayUserId(),
		SenderPrettyDisplayUserId: drawn.GetSenderPrettyDisplayUserId(),
		VisibleRegionId:           drawn.GetVisibleRegionId(),
		RoomLocked:                drawn.GetRoomLocked(),
		OwnerUserId:               drawn.GetOwnerUserId(),
		CountryId:                 drawn.GetCountryId(),
		CoinSpent:                 drawn.GetCoinSpent(),
		RuleVersion:               drawn.GetRuleVersion(),
		ExperiencePool:            drawn.GetExperiencePool(),
		SelectedTierId:            drawn.GetSelectedTierId(),
		MultiplierPpm:             drawn.GetMultiplierPpm(),
		BaseRewardCoins:           drawn.GetBaseRewardCoins(),
		EffectiveRewardCoins:      drawn.GetEffectiveRewardCoins(),
		StageFeedback:             drawn.GetStageFeedback(),
		HighMultiplier:            drawn.GetHighMultiplier(),
		RewardStatus:              "granted",
		WalletTransactionId:       drawn.GetWalletTransactionId(),
		DrawCreatedAtMs:           drawn.GetDrawCreatedAtMs(),
		RewardGrantedAtMs:         drawn.GetRewardGrantedAtMs(),
	}
	record, err := outbox.Build(drawn.GetRoomId(), roomLuckyGiftDrawnEventType, 0, time.UnixMilli(envelope.GetOccurredAtMs()).UTC(), body)
	if err != nil {
		return err
	}
	// owner event_id 是跨服务唯一事实 ID；覆盖 room 本地随机 ID 后，MQ 重投只能命中同一条 room_outbox。
	record.AppCode = strings.TrimSpace(envelope.GetAppCode())
	record.EventID = strings.TrimSpace(envelope.GetEventId())
	record.Envelope.AppCode = record.AppCode
	record.Envelope.EventId = record.EventID
	ctx = appcode.WithContext(ctx, record.AppCode)
	if err := s.repository.SaveOutbox(ctx, []outbox.Record{record}); err != nil {
		return err
	}

	// 唤醒必须发生在 SaveOutbox 提交之后；重复事实最多产生一个多余唤醒，不会产生第二条 IM outbox。
	s.notifyOutboxCommitted([]outbox.Record{record})
	return nil
}

func validateLuckyGiftDrawnEnvelope(envelope *luckygifteventsv1.EventEnvelope, drawn *luckygifteventsv1.LuckyGiftDrawn) error {
	if envelope == nil || drawn == nil {
		return errors.New("lucky gift drawn fact is required")
	}
	appCode := strings.TrimSpace(envelope.GetAppCode())
	drawID := strings.TrimSpace(drawn.GetDrawId())
	eventID := strings.TrimSpace(envelope.GetEventId())
	if appCode == "" || drawID == "" || drawID != strings.TrimSpace(envelope.GetDrawId()) ||
		strings.TrimSpace(drawn.GetRoomId()) == "" || strings.TrimSpace(drawn.GetCommandId()) == "" ||
		strings.TrimSpace(drawn.GetGiftId()) == "" || drawn.GetGiftCount() <= 0 ||
		drawn.GetSenderUserId() <= 0 || drawn.GetTargetUserId() <= 0 ||
		envelope.GetOccurredAtMs() <= 0 || drawn.GetDrawCreatedAtMs() <= 0 || drawn.GetRewardGrantedAtMs() <= 0 {
		return errors.New("lucky gift drawn fact is incomplete")
	}
	if eventID != "lucky_gift_drawn:"+drawID {
		// 同一 draw 只能映射到一个房间 IM 幂等键；不能接受 producer 临时生成的随机 event_id。
		return errors.New("lucky gift drawn event_id does not match draw_id")
	}
	return nil
}
