// Package gamemq defines RocketMQ payloads owned by game-service.
package gamemq

import (
	"encoding/json"
	"errors"
	"strings"
)

const (
	MessageTypeGameOutboxEvent = "game_outbox_event"

	TagGameOutboxEvent = "game_outbox_event"

	EventTypeGameOrderSettled      = "GameOrderSettled"
	EventTypeSelfGameMatchCreated  = "SelfGameMatchCreated"
	EventTypeSelfGameMatchReady    = "SelfGameMatchReady"
	EventTypeSelfGameMatchSettled  = "SelfGameMatchSettled"
	EventTypeSelfGameMatchCanceled = "SelfGameMatchCanceled"
	EventTypeSelfGameMatchFailed   = "SelfGameMatchFailed"
	EventTypeSelfGamePoolAdjusted  = "SelfGamePoolAdjusted"
)

// GameOutboxMessage is the MQ representation of one committed game_outbox fact.
type GameOutboxMessage struct {
	MessageType  string `json:"message_type"`
	AppCode      string `json:"app_code"`
	EventID      string `json:"event_id"`
	EventType    string `json:"event_type"`
	OrderID      string `json:"order_id"`
	UserID       int64  `json:"user_id"`
	PlatformCode string `json:"platform_code"`
	GameID       string `json:"game_id"`
	OpType       string `json:"op_type"`
	CoinAmount   int64  `json:"coin_amount"`
	PayloadJSON  string `json:"payload_json"`
	OccurredAtMS int64  `json:"occurred_at_ms"`
}

// EncodeGameOutboxMessage serializes a game fact for fanout through RocketMQ.
func EncodeGameOutboxMessage(message GameOutboxMessage) ([]byte, error) {
	message.MessageType = MessageTypeGameOutboxEvent
	if err := validateGameOutboxMessage(message); err != nil {
		return nil, err
	}
	if strings.TrimSpace(message.PayloadJSON) == "" {
		message.PayloadJSON = "{}"
	}
	return json.Marshal(message)
}

// DecodeGameOutboxMessage validates the MQ body and restores the game fact.
func DecodeGameOutboxMessage(body []byte) (GameOutboxMessage, error) {
	var message GameOutboxMessage
	if err := json.Unmarshal(body, &message); err != nil {
		return GameOutboxMessage{}, err
	}
	if message.MessageType != MessageTypeGameOutboxEvent {
		return GameOutboxMessage{}, errors.New("unexpected game outbox message_type")
	}
	if err := validateGameOutboxMessage(message); err != nil {
		return GameOutboxMessage{}, err
	}
	if strings.TrimSpace(message.PayloadJSON) == "" {
		message.PayloadJSON = "{}"
	}
	return message, nil
}

func validateGameOutboxMessage(message GameOutboxMessage) error {
	if strings.TrimSpace(message.AppCode) == "" ||
		strings.TrimSpace(message.EventID) == "" ||
		strings.TrimSpace(message.EventType) == "" ||
		strings.TrimSpace(message.GameID) == "" ||
		message.OccurredAtMS <= 0 {
		return errors.New("game outbox message is incomplete")
	}
	if message.EventType == EventTypeGameOrderSettled && (strings.TrimSpace(message.OrderID) == "" ||
		message.UserID <= 0 ||
		strings.TrimSpace(message.OpType) == "" ||
		message.CoinAmount < 0) {
		// 厂商可能发送 0 金额派奖或 0 金额结算确认；这类订单是已落库事实，必须允许 MQ 投递。
		// 负数仍然不是合法订单金额，扣减方向必须由 op_type=debit 表达，不能把符号塞进金额字段。
		return errors.New("game order outbox message is incomplete")
	}
	return nil
}
