package service

import (
	"fmt"
	"strings"
	"time"

	roomeventsv1 "hyapp.local/api/proto/events/room/v1"
	"hyapp/services/room-service/internal/room/command"
	"hyapp/services/room-service/internal/room/outbox"
	"hyapp/services/room-service/internal/room/state"
)

// roomMicChangedEvent 建立 RoomMicChanged 事实；展示快照只来自命令入口，
// room-service 不反查 user-service，保证 Room Cell 主链路仍只维护房态。
func roomMicChangedEvent(event *roomeventsv1.RoomMicChanged, profile command.UserDisplayProfile) *roomeventsv1.RoomMicChanged {
	if event == nil {
		event = &roomeventsv1.RoomMicChanged{}
	}
	event.TargetNickname = strings.TrimSpace(profile.Nickname)
	event.TargetAvatar = strings.TrimSpace(profile.Avatar)
	event.TargetDisplayUserId = strings.TrimSpace(profile.DisplayUserID)
	event.TargetPrettyDisplayUserId = strings.TrimSpace(profile.PrettyDisplayUserID)
	return event
}

// roomMicChangedIMAttributes 是 direct IM 和 outbox 补偿的唯一麦位 attributes 映射。
// 目标用户展示字段同时写 target_*；actor 与 target 相同时再写旧客户端已认识的 nickname/avatar。
func roomMicChangedIMAttributes(event *roomeventsv1.RoomMicChanged) map[string]string {
	if event == nil {
		return map[string]string{}
	}
	values := map[string]string{
		"action":                        strings.TrimSpace(event.GetAction()),
		"mic_session_id":                strings.TrimSpace(event.GetMicSessionId()),
		"publish_state":                 strings.TrimSpace(event.GetPublishState()),
		"reason":                        strings.TrimSpace(event.GetReason()),
		"publish_deadline_ms":           fmt.Sprintf("%d", event.GetPublishDeadlineMs()),
		"publish_event_time_ms":         fmt.Sprintf("%d", event.GetPublishEventTimeMs()),
		"mic_muted":                     fmt.Sprintf("%t", event.GetMicMuted()),
		"seat_status":                   strings.TrimSpace(event.GetSeatStatus()),
		"target_gift_value":             fmt.Sprintf("%d", event.GetTargetGiftValue()),
		"target_nickname":               strings.TrimSpace(event.GetTargetNickname()),
		"target_avatar":                 strings.TrimSpace(event.GetTargetAvatar()),
		"target_display_user_id":        strings.TrimSpace(event.GetTargetDisplayUserId()),
		"target_pretty_display_user_id": strings.TrimSpace(event.GetTargetPrettyDisplayUserId()),
	}
	if event.GetActorUserId() == event.GetTargetUserId() {
		values["nickname"] = values["target_nickname"]
		values["avatar"] = values["target_avatar"]
		values["display_user_id"] = values["target_display_user_id"]
		values["pretty_display_user_id"] = values["target_pretty_display_user_id"]
	}
	for key, value := range values {
		if value == "" {
			delete(values, key)
		}
	}
	return values
}

// buildMicDownEventForSeat records every non-MicDown release path as the same
// RoomMicChanged/down fact, so downstream duration consumers never need to infer
// mic closure from leave/kick/close side effects.
func buildMicDownEventForSeat(roomID string, roomVersion int64, now time.Time, actorUserID int64, targetUserID int64, seatNo int32, micSessionID string, reason string) (outbox.Record, error) {
	return outbox.Build(roomID, "RoomMicChanged", roomVersion, now, roomMicChangedEvent(&roomeventsv1.RoomMicChanged{
		ActorUserId:  actorUserID,
		TargetUserId: targetUserID,
		FromSeat:     seatNo,
		ToSeat:       0,
		Action:       "down",
		MicSessionId: micSessionID,
		PublishState: state.MicPublishIdle,
		Reason:       reason,
		SeatStatus:   state.MicSeatStatusEmpty,
	}, command.UserDisplayProfile{}))
}
