package service

import (
	"context"
	"log/slog"
	"time"

	"hyapp/pkg/appcode"
	"hyapp/pkg/logx"
	"hyapp/services/room-service/internal/room/outbox"
)

const roomDirectIMPublishTimeout = 2 * time.Second

func splitRoomDirectIMRecords(records []outbox.Record, suppressMirror map[string]bool) ([]outbox.Record, []outbox.Record) {
	if len(records) == 0 {
		return nil, nil
	}
	durable := make([]outbox.Record, 0, len(records))
	directIM := make([]outbox.Record, 0)
	for _, record := range records {
		if roomDirectIMOnlyEventType(record.EventType) {
			directIM = append(directIM, record)
			continue
		}
		durable = append(durable, record)
		if roomDirectIMMirrorEventType(record.EventType) && !suppressMirror[record.EventType] {
			// 这类事件同时服务 UI 和业务事实消费：MySQL outbox 保证统计、任务、通知等可补偿，直接 IM 只给客户端低延迟展示。
			directIM = append(directIM, record)
		}
	}
	return durable, directIM
}

func roomDirectIMOnlyEventType(eventType string) bool {
	switch eventType {
	case "RoomHeatChanged",
		"RoomRankChanged",
		"RoomGiftBatchSent",
		"RoomRocketFuelChanged",
		"RoomRocketLaunched":
		return true
	default:
		return false
	}
}

func roomDirectIMMirrorEventType(eventType string) bool {
	switch eventType {
	case "RoomGiftSent",
		"RoomUserJoined",
		"RoomUserLeft",
		"RoomMicChanged",
		"RoomMicSeatLocked",
		"RoomClosed",
		"RoomUserKicked",
		"RoomUserMuted",
		"RoomUserUnbanned",
		"RoomProfileUpdated",
		"RoomBackgroundChanged",
		"RoomChatEnabledChanged",
		"RoomAdminChanged",
		"RoomRocketIgnited":
		return true
	default:
		return false
	}
}

func (s *Service) publishDirectIMRecordsBestEffort(ctx context.Context, records []outbox.Record) {
	if s == nil || len(records) == 0 {
		return
	}
	if s.roomDisplayPublisher == nil {
		logx.Warn(ctx, "room_direct_im_publish_skipped",
			slog.String("node_id", s.nodeID),
			slog.Int("record_count", len(records)),
			slog.String("reason", "publisher_not_configured"),
		)
		return
	}
	scopedApp := appcode.FromContext(ctx)
	copied := append([]outbox.Record(nil), records...)
	for index := range copied {
		copied[index].AppCode = appcode.Normalize(firstNonEmpty(copied[index].AppCode, scopedApp))
		if copied[index].Envelope != nil {
			copied[index].Envelope.AppCode = copied[index].AppCode
		}
	}
	sortOutboxRecords(copied)

	go func(records []outbox.Record) {
		for _, record := range records {
			if record.Envelope == nil {
				logx.Warn(ctx, "room_direct_im_publish_skipped",
					slog.String("node_id", s.nodeID),
					slog.String("event_id", record.EventID),
					slog.String("event_type", record.EventType),
					slog.String("room_id", record.RoomID),
					slog.String("reason", "nil_envelope"),
				)
				continue
			}
			publishCtx, cancel := context.WithTimeout(appcode.WithContext(context.Background(), record.AppCode), roomDirectIMPublishTimeout)
			err := s.roomDisplayPublisher.PublishOutboxEvent(publishCtx, record.Envelope)
			cancel()
			if err != nil {
				logx.Warn(ctx, "room_direct_im_publish_failed",
					slog.String("node_id", s.nodeID),
					slog.String("event_id", record.EventID),
					slog.String("event_type", record.EventType),
					slog.String("room_id", record.RoomID),
					slog.String("error", trimOutboxError(err.Error())),
				)
			}
		}
	}(copied)
}
