package roomapi

import (
	"context"
	"net/http"
	"strconv"
	"strings"

	roomv1 "hyapp.local/api/proto/room/v1"
	"hyapp/services/gateway-service/internal/auth"
	"hyapp/services/gateway-service/internal/transport/http/httpkit"
)

const (
	maxUserVoiceRoomPresenceBatchSize = 100
	codeInvalidUserIDs                = "INVALID_USER_IDS"
	codeVoiceRoomPresenceUnavailable  = "VOICE_ROOM_PRESENCE_UNAVAILABLE"
)

// userVoiceRoomPresenceQueryClient 是个人资料页独占的窄查询能力。
// 生产 grpcRoomQueryClient 实现该接口；其他只使用房间列表的调用方无需为新 RPC 扩大 mock 面。
type userVoiceRoomPresenceQueryClient interface {
	BatchGetUserVoiceRoomPresences(context.Context, *roomv1.BatchGetUserVoiceRoomPresencesRequest) (*roomv1.BatchGetUserVoiceRoomPresencesResponse, error)
}

type userVoiceRoomPresenceBatchData struct {
	Items        []userVoiceRoomPresenceData `json:"items"`
	ServerTimeMS int64                       `json:"server_time_ms"`
}

type userVoiceRoomPresenceData struct {
	UserID       string                         `json:"user_id"`
	State        string                         `json:"state"`
	SeatNo       int32                          `json:"seat_no"`
	Joinable     bool                           `json:"joinable"`
	ObservedAtMS int64                          `json:"observed_at_ms"`
	Room         *userVoiceRoomPresenceRoomData `json:"room"`
}

type userVoiceRoomPresenceRoomData struct {
	RoomID        string `json:"room_id"`
	IMGroupID     string `json:"im_group_id"`
	RTCRoomID     string `json:"rtc_room_id"`
	RoomShortID   string `json:"room_short_id"`
	Title         string `json:"title"`
	OwnerUserID   string `json:"owner_user_id"`
	HostUserID    string `json:"host_user_id"`
	Mode          string `json:"mode"`
	CoverURL      string `json:"cover_url"`
	BackgroundURL string `json:"background_url"`
	OnlineCount   int32  `json:"online_count"`
	SeatCount     int32  `json:"seat_count"`
	Locked        bool   `json:"locked"`
}

// batchUserVoiceRoomPresences 为个人资料页返回当前登录用户可见的 Live 入口。
// 客户端只提交目标 user_ids；viewer_user_id 和 app_code 必须来自已验签请求上下文。
func (h *Handler) batchUserVoiceRoomPresences(writer http.ResponseWriter, request *http.Request) {
	queryClient, ok := h.roomQueryClient.(userVoiceRoomPresenceQueryClient)
	if !ok || queryClient == nil {
		httpkit.WriteError(writer, request, http.StatusServiceUnavailable, codeVoiceRoomPresenceUnavailable, "voice room presence unavailable")
		return
	}
	userIDs, ok := parseUserVoiceRoomPresenceUserIDs(request)
	if !ok {
		httpkit.WriteError(writer, request, http.StatusBadRequest, codeInvalidUserIDs, "invalid user ids")
		return
	}

	resp, err := queryClient.BatchGetUserVoiceRoomPresences(request.Context(), &roomv1.BatchGetUserVoiceRoomPresencesRequest{
		Meta:         httpkit.RoomMeta(request, "", ""),
		ViewerUserId: auth.UserIDFromContext(request.Context()),
		UserIds:      userIDs,
	})
	if err != nil {
		// 单个未上麦或不可见用户由 room-service 返回 not_on_mic；RPC 失败代表整批实时事实不可用，客户端必须隐藏 Live。
		httpkit.WriteError(writer, request, http.StatusServiceUnavailable, codeVoiceRoomPresenceUnavailable, "voice room presence unavailable")
		return
	}
	httpkit.WriteOK(writer, request, userVoiceRoomPresenceBatchDataFromProto(resp))
}

func parseUserVoiceRoomPresenceUserIDs(request *http.Request) ([]int64, bool) {
	rawValues, exists := request.URL.Query()["user_ids"]
	if !exists || len(rawValues) == 0 {
		return nil, false
	}
	seen := make(map[int64]struct{})
	result := make([]int64, 0, len(rawValues))
	for _, raw := range rawValues {
		if strings.TrimSpace(raw) == "" {
			return nil, false
		}
		for piece := range strings.SplitSeq(raw, ",") {
			piece = strings.TrimSpace(piece)
			userID, err := strconv.ParseInt(piece, 10, 64)
			if piece == "" || err != nil || userID <= 0 {
				return nil, false
			}
			if _, duplicate := seen[userID]; duplicate {
				continue
			}
			seen[userID] = struct{}{}
			result = append(result, userID)
			if len(result) > maxUserVoiceRoomPresenceBatchSize {
				return nil, false
			}
		}
	}
	return result, len(result) > 0
}

func userVoiceRoomPresenceBatchDataFromProto(resp *roomv1.BatchGetUserVoiceRoomPresencesResponse) userVoiceRoomPresenceBatchData {
	if resp == nil {
		return userVoiceRoomPresenceBatchData{Items: []userVoiceRoomPresenceData{}}
	}
	items := make([]userVoiceRoomPresenceData, 0, len(resp.GetItems()))
	for _, item := range resp.GetItems() {
		view := userVoiceRoomPresenceData{
			UserID:       httpkit.UserIDString(item.GetUserId()),
			State:        item.GetState(),
			SeatNo:       item.GetSeatNo(),
			Joinable:     item.GetJoinable(),
			ObservedAtMS: item.GetObservedAtMs(),
		}
		if room := item.GetRoom(); room != nil {
			view.Room = &userVoiceRoomPresenceRoomData{
				RoomID:        room.GetRoomId(),
				IMGroupID:     roomIMGroupID(room.GetRoomId()),
				RTCRoomID:     room.GetRoomId(),
				RoomShortID:   room.GetRoomShortId(),
				Title:         room.GetTitle(),
				OwnerUserID:   httpkit.UserIDString(room.GetOwnerUserId()),
				HostUserID:    httpkit.UserIDString(room.GetHostUserId()),
				Mode:          room.GetMode(),
				CoverURL:      room.GetCoverUrl(),
				BackgroundURL: room.GetBackgroundUrl(),
				OnlineCount:   room.GetOnlineCount(),
				SeatCount:     room.GetSeatCount(),
				Locked:        room.GetLocked(),
			}
		}
		items = append(items, view)
	}
	return userVoiceRoomPresenceBatchData{Items: items, ServerTimeMS: resp.GetServerTimeMs()}
}
