package service

import (
	"context"
	"strings"

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

// FollowRoom 建立当前用户对房间的关注关系。
// 关注关系是用户个性化读模型事实，不改变 Room Cell 核心状态，也不需要 Redis lease 串行化。
func (s *Service) FollowRoom(ctx context.Context, req *roomv1.FollowRoomRequest) (*roomv1.FollowRoomResponse, error) {
	ctx = contextFromMeta(ctx, req.GetMeta())
	roomID, userID, err := normalizeRoomFollowInput(req.GetMeta(), req.GetRoomId(), req.GetUserId())
	if err != nil {
		return nil, err
	}

	meta, exists, err := s.repository.GetRoomMeta(ctx, roomID)
	if err != nil {
		return nil, err
	}
	if !exists {
		return nil, xerr.New(xerr.NotFound, "room not found")
	}
	if isClosedRoomStatus(meta.Status) {
		// 已关闭房间不再建立新的关注关系；历史关注仍可通过 UnfollowRoom 幂等取消。
		return nil, xerr.New(xerr.RoomClosed, "room closed")
	}

	nowMS := s.clock.Now().UnixMilli()
	record, err := s.repository.FollowRoom(ctx, userID, roomID, nowMS)
	if err != nil {
		return nil, err
	}

	return &roomv1.FollowRoomResponse{
		RoomId:       roomID,
		Followed:     true,
		FollowedAtMs: record.FollowedAtMS,
		ServerTimeMs: nowMS,
	}, nil
}

// UnfollowRoom 取消当前用户对房间的关注关系。
// 未关注或历史房间已关闭都按幂等成功处理，方便客户端在本地状态不确定时直接收敛。
func (s *Service) UnfollowRoom(ctx context.Context, req *roomv1.UnfollowRoomRequest) (*roomv1.UnfollowRoomResponse, error) {
	ctx = contextFromMeta(ctx, req.GetMeta())
	roomID, userID, err := normalizeRoomFollowInput(req.GetMeta(), req.GetRoomId(), req.GetUserId())
	if err != nil {
		return nil, err
	}

	nowMS := s.clock.Now().UnixMilli()
	if err := s.repository.UnfollowRoom(ctx, userID, roomID, nowMS); err != nil {
		return nil, err
	}

	return &roomv1.UnfollowRoomResponse{
		RoomId:       roomID,
		Followed:     false,
		ServerTimeMs: nowMS,
	}, nil
}

func normalizeRoomFollowInput(meta *roomv1.RequestMeta, rawRoomID string, userID int64) (string, int64, error) {
	roomID := strings.TrimSpace(rawRoomID)
	metaRoomID := strings.TrimSpace(meta.GetRoomId())
	if roomID == "" {
		roomID = metaRoomID
	}
	if metaRoomID != "" && roomID != metaRoomID {
		// meta.room_id 是链路和审计字段；与 body/path room_id 不一致时必须拒绝，避免写错关系。
		return "", 0, xerr.New(xerr.InvalidArgument, "room_id is invalid")
	}
	if !roomid.ValidStringID(roomID) {
		return "", 0, xerr.New(xerr.InvalidArgument, "room_id is invalid")
	}
	if userID <= 0 {
		return "", 0, xerr.New(xerr.InvalidArgument, "user_id is required")
	}
	return roomID, userID, nil
}
