package broadcast

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

	"hyapp/pkg/appcode"
	broadcastdomain "hyapp/services/activity-service/internal/domain/broadcast"
)

// ConsumeRedPacketWalletEvent converts one MQ-delivered wallet red-packet fact into IM outbox records.
func (s *Service) ConsumeRedPacketWalletEvent(ctx context.Context, event broadcastdomain.RedPacketWalletEvent) error {
	if s == nil {
		return nil
	}
	ctx = appcode.WithContext(ctx, event.AppCode)
	switch event.EventType {
	case "WalletRedPacketCreated":
		return s.consumeRedPacketCreated(ctx, event)
	case "WalletRedPacketClaimed":
		if strings.EqualFold(event.Status, "finished") {
			return s.publishRedPacketRoomEvent(ctx, event, "STATUS_CHANGED", event.CreatedAtMS)
		}
	case "WalletRedPacketRefunded":
		return s.publishRedPacketRoomEvent(ctx, event, "STATUS_CHANGED", event.CreatedAtMS)
	}
	return nil
}

func (s *Service) ConsumeRedPacketCreatedEvent(ctx context.Context, event broadcastdomain.RedPacketWalletEvent) error {
	if event.EventType == "" {
		event.EventType = "WalletRedPacketCreated"
	}
	return s.ConsumeRedPacketWalletEvent(ctx, event)
}

func (s *Service) consumeRedPacketCreated(ctx context.Context, event broadcastdomain.RedPacketWalletEvent) error {
	if event.RoomID == "" {
		return nil
	}
	enriched, err := s.withRedPacketSenderProfile(ctx, event)
	if err != nil {
		return err
	}
	event = enriched
	if err := s.publishRedPacketRoomEvent(ctx, event, "CREATED", event.CreatedAtMS); err != nil {
		return err
	}
	if strings.EqualFold(event.PacketType, "delayed") && event.RegionID > 0 {
		if _, err := s.PublishRegionBroadcast(ctx, PublishInput{
			EventID:       event.EventID + ":region_created",
			BroadcastType: broadcastdomain.TypeRedPacket,
			RegionID:      event.RegionID,
			PayloadJSON:   redPacketIMPayload(event, "CREATED", "REGION"),
		}); err != nil {
			return err
		}
	}
	if strings.EqualFold(event.PacketType, "delayed") && event.OpenAtMS > 0 {
		_, err := s.PublishRoomBroadcast(ctx, event.RoomID, PublishInput{
			EventID:       event.EventID + ":open_due",
			BroadcastType: broadcastdomain.TypeRedPacket,
			RegionID:      event.RegionID,
			PayloadJSON:   redPacketIMPayload(event, "OPEN_DUE", "ROOM"),
			DeliverAtMS:   event.OpenAtMS,
		})
		return err
	}
	return nil
}

func (s *Service) withRedPacketSenderProfile(ctx context.Context, event broadcastdomain.RedPacketWalletEvent) (broadcastdomain.RedPacketWalletEvent, error) {
	if event.SenderUserID <= 0 || s == nil || s.senderProfileSource == nil {
		return event, nil
	}
	profile, err := s.senderProfileSource.GetSenderProfile(ctx, event.SenderUserID)
	if err != nil {
		return event, err
	}
	if event.SenderAccount == "" {
		event.SenderAccount = profile.Account
	}
	if event.SenderNickname == "" {
		event.SenderNickname = profile.Nickname
	}
	if event.SenderAvatar == "" {
		event.SenderAvatar = profile.Avatar
	}
	if event.RegionCode == "" {
		event.RegionCode = profile.RegionCode
	}
	return event, nil
}

func (s *Service) publishRedPacketRoomEvent(ctx context.Context, event broadcastdomain.RedPacketWalletEvent, eventName string, fallbackDeliverAtMS int64) error {
	if event.RoomID == "" {
		return nil
	}
	_, err := s.PublishRoomBroadcast(ctx, event.RoomID, PublishInput{
		EventID:       event.EventID,
		BroadcastType: broadcastdomain.TypeRedPacket,
		RegionID:      event.RegionID,
		PayloadJSON:   redPacketIMPayload(event, eventName, "ROOM"),
		DeliverAtMS:   fallbackDeliverAtMS,
	})
	return err
}

func redPacketIMPayload(event broadcastdomain.RedPacketWalletEvent, eventName string, scope string) string {
	packetMode := "IMMEDIATE"
	if strings.EqualFold(event.PacketType, "delayed") {
		packetMode = "DELAYED"
	}
	status := strings.ToUpper(strings.TrimSpace(event.Status))
	if status == "" {
		status = "ACTIVE"
	}
	data := map[string]any{
		"event":          eventName,
		"scope":          scope,
		"packetNo":       event.PacketID,
		"packetId":       event.PacketID,
		"roomId":         event.RoomID,
		"regionId":       event.RegionID,
		"regionCode":     event.RegionCode,
		"room_locked":    event.RoomLocked,
		"packetMode":     packetMode,
		"senderUserId":   redPacketUserIDString(event.SenderUserID),
		"actualAccount":  redPacketSenderAccount(event),
		"userNickname":   event.SenderNickname,
		"userAvatar":     event.SenderAvatar,
		"totalAmount":    event.TotalAmount,
		"totalCount":     event.TotalCount,
		"remainAmount":   event.RemainingAmount,
		"remainCount":    event.RemainingCount,
		"status":         status,
		"delaySeconds":   redPacketDelaySeconds(event),
		"refundedAmount": event.RefundedAmount,
		"claimStartTime": event.OpenAtMS,
		"expireTime":     event.ExpiresAtMS,
		"serverTime":     event.CreatedAtMS,
	}
	if event.OwnerUserID > 0 {
		// 红包协议使用独立 data 包装；用户 ID 直接写字符串，避免任何未经过统一播报归一化的调用方暴露 JSON 数字精度问题。
		data["owner_user_id"] = redPacketUserIDString(event.OwnerUserID)
	}
	payload := map[string]any{
		"type": "ROOM_RED_PACKET",
		"data": data,
	}
	encoded, err := json.Marshal(payload)
	if err != nil {
		return `{"type":"ROOM_RED_PACKET","data":{}}`
	}
	return string(encoded)
}

func redPacketUserIDString(userID int64) string {
	if userID <= 0 {
		return ""
	}
	return strconv.FormatInt(userID, 10)
}

func redPacketSenderAccount(event broadcastdomain.RedPacketWalletEvent) string {
	if strings.TrimSpace(event.SenderAccount) != "" {
		return event.SenderAccount
	}
	return redPacketUserIDString(event.SenderUserID)
}

func redPacketDelaySeconds(event broadcastdomain.RedPacketWalletEvent) int64 {
	if !strings.EqualFold(event.PacketType, "delayed") || event.OpenAtMS <= event.CreatedAtMS {
		return 0
	}
	return (event.OpenAtMS - event.CreatedAtMS) / 1000
}
