package service

import (
	"context"

	roomv1 "hyapp.local/api/proto/room/v1"
	"hyapp/pkg/appcode"
)

// CheckSpeakPermission 给 gateway 等受控业务入口提供同步发言守卫；腾讯 IM 普通消息不再进入本服务。
func (s *Service) CheckSpeakPermission(ctx context.Context, req *roomv1.CheckSpeakPermissionRequest) (*roomv1.CheckSpeakPermissionResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetAppCode())
	closed, err := s.isRoomClosed(ctx, req.GetRoomId())
	if err != nil {
		return nil, err
	}
	if closed {
		return &roomv1.CheckSpeakPermissionResponse{
			Allowed: false,
			Reason:  "room_closed",
		}, nil
	}

	// 守卫查询优先读内存 Cell；没有 Cell 时从快照读取，避免外部 IM 入口越权自判。
	snapshot, err := s.currentSnapshot(ctx, req.GetRoomId())
	if err != nil {
		return nil, err
	}

	if snapshot == nil {
		// 没有快照代表 room-service 不认识该房间。
		return &roomv1.CheckSpeakPermissionResponse{
			Allowed: false,
			Reason:  "room_not_found",
		}, nil
	}

	nowMS := s.clock.Now().UnixMilli()
	banned := snapshotUserBanned(snapshot, req.GetUserId(), nowMS)
	// 允许发言必须同时满足未 ban、未 mute、房间开启聊天、用户在业务 presence 内。
	if !banned && !containsID(snapshot.GetMuteUserIds(), req.GetUserId()) && snapshot.GetChatEnabled() && findProtoUser(snapshot, req.GetUserId()) != nil {
		return &roomv1.CheckSpeakPermissionResponse{
			Allowed:     true,
			RoomVersion: snapshot.GetVersion(),
		}, nil
	}

	reason := "not_in_room"
	switch {
	case banned:
		// ban 优先级最高，被踢用户即使仍在腾讯云 IM 群内也不能发言。
		reason = "user_banned"
	case containsID(snapshot.GetMuteUserIds(), req.GetUserId()):
		reason = "user_muted"
	case !snapshot.GetChatEnabled():
		reason = "chat_disabled"
	}

	return &roomv1.CheckSpeakPermissionResponse{
		Allowed:     false,
		Reason:      reason,
		RoomVersion: snapshot.GetVersion(),
	}, nil
}

// VerifyRoomPresence 给外部 IM 入口提供进群前的同步 presence 守卫。
func (s *Service) VerifyRoomPresence(ctx context.Context, req *roomv1.VerifyRoomPresenceRequest) (*roomv1.VerifyRoomPresenceResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetAppCode())
	// 腾讯云 IM 进群前必须通过该守卫，不能只依赖客户端先调用过 JoinRoom。
	closed, err := s.isRoomClosed(ctx, req.GetRoomId())
	if err != nil {
		return nil, err
	}
	if closed {
		return &roomv1.VerifyRoomPresenceResponse{
			Present: false,
			Reason:  "room_closed",
		}, nil
	}

	snapshot, err := s.currentSnapshot(ctx, req.GetRoomId())
	if err != nil {
		return nil, err
	}

	if snapshot == nil {
		// 房间不存在时外部 IM 入口不应把用户加入房间频道。
		return &roomv1.VerifyRoomPresenceResponse{
			Present: false,
			Reason:  "room_not_found",
		}, nil
	}

	if snapshotUserBanned(snapshot, req.GetUserId(), s.clock.Now().UnixMilli()) {
		// ban 用户不能订阅，即使快照里还有旧 presence 也以 ban 为准。
		return &roomv1.VerifyRoomPresenceResponse{
			Present:     false,
			Reason:      "user_banned",
			RoomVersion: snapshot.GetVersion(),
		}, nil
	}

	if findProtoUser(snapshot, req.GetUserId()) == nil {
		// 没有 room-service presence 的用户不能偷收房间消息。
		return &roomv1.VerifyRoomPresenceResponse{
			Present:     false,
			Reason:      "not_in_room",
			RoomVersion: snapshot.GetVersion(),
		}, nil
	}

	return &roomv1.VerifyRoomPresenceResponse{
		Present:     true,
		RoomVersion: snapshot.GetVersion(),
	}, nil
}
