package service

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

	"hyapp/pkg/appcode"
	"hyapp/pkg/walletmq"
	"hyapp/pkg/xerr"
)

// HandleWalletRedPacketProjectionEvent 消费 wallet realtime outbox，异步刷新房间列表红包聚合。
// 该链路不进入 ListRooms 请求，也不读取 wallet 数据库；列表延迟只增加同一行两个标量字段的反序列化成本。
func (s *Service) HandleWalletRedPacketProjectionEvent(ctx context.Context, message walletmq.WalletOutboxMessage) error {
	event, handled, err := roomRedPacketProjectionEvent(message, s.clock.Now().UnixMilli())
	if err != nil || !handled {
		return err
	}
	store, ok := s.repository.(RoomRedPacketProjectionStore)
	if !ok {
		return xerr.New(xerr.Unavailable, "room red packet projection store is unavailable")
	}
	ctx = appcode.WithContext(ctx, message.AppCode)
	entry, exists, err := store.ApplyRoomRedPacketProjectionEvent(ctx, event)
	if err != nil || !exists {
		return err
	}
	// MySQL 事务是权威投影；Redis 失败只影响短期命中率，MQ 重投和定时重建都会继续收敛。
	s.projectRoomListCacheBestEffort(ctx, entry)
	return nil
}

func roomRedPacketProjectionEvent(message walletmq.WalletOutboxMessage, projectedAtMS int64) (RoomRedPacketProjectionEvent, bool, error) {
	eventType := strings.TrimSpace(message.EventType)
	eventRank := int32(0)
	switch eventType {
	case walletmq.EventTypeWalletRedPacketCreated:
		eventRank = 1
	case walletmq.EventTypeWalletRedPacketClaimed:
		eventRank = 2
	case walletmq.EventTypeWalletRedPacketRefunded:
		eventRank = 3
	default:
		return RoomRedPacketProjectionEvent{}, false, nil
	}

	var payload struct {
		PacketID        string `json:"packet_id"`
		RoomID          string `json:"room_id"`
		Status          string `json:"status"`
		PacketStatus    string `json:"packet_status"`
		RemainingAmount int64  `json:"remaining_amount"`
		RemainingCount  int32  `json:"remaining_count"`
		ExpiresAtMS     int64  `json:"expires_at_ms"`
	}
	if err := json.Unmarshal([]byte(message.PayloadJSON), &payload); err != nil {
		return RoomRedPacketProjectionEvent{}, true, err
	}
	status := strings.ToLower(strings.TrimSpace(payload.Status))
	if payload.PacketStatus != "" {
		status = strings.ToLower(strings.TrimSpace(payload.PacketStatus))
	}
	if eventType == walletmq.EventTypeWalletRedPacketRefunded {
		status = "refunded"
		payload.RemainingAmount = 0
		payload.RemainingCount = 0
	}
	if strings.TrimSpace(message.EventID) == "" || strings.TrimSpace(payload.PacketID) == "" ||
		strings.TrimSpace(payload.RoomID) == "" || status == "" || message.OccurredAtMS <= 0 {
		return RoomRedPacketProjectionEvent{}, true, fmt.Errorf("%s projection payload is incomplete", eventType)
	}
	if projectedAtMS <= 0 {
		projectedAtMS = message.OccurredAtMS
	}
	return RoomRedPacketProjectionEvent{
		EventID:         strings.TrimSpace(message.EventID),
		EventType:       eventType,
		PacketID:        strings.TrimSpace(payload.PacketID),
		RoomID:          strings.TrimSpace(payload.RoomID),
		Status:          status,
		RemainingAmount: payload.RemainingAmount,
		RemainingCount:  payload.RemainingCount,
		ExpiresAtMS:     payload.ExpiresAtMS,
		OccurredAtMS:    message.OccurredAtMS,
		ProjectedAtMS:   projectedAtMS,
		EventRank:       eventRank,
	}, true, nil
}
