package service

import (
	"context"
	"time"

	roomeventsv1 "hyapp.local/api/proto/events/room/v1"
	roomv1 "hyapp.local/api/proto/room/v1"
	"hyapp/pkg/tencentim"
	"hyapp/pkg/xerr"
	"hyapp/services/room-service/internal/room/command"
	"hyapp/services/room-service/internal/room/outbox"
	"hyapp/services/room-service/internal/room/rank"
	"hyapp/services/room-service/internal/room/state"
)

// RobotMicUpInput 是内部机器人编排器占麦输入；调用方必须只传 robot-service 登记过的机器人用户。
type RobotMicUpInput struct {
	Meta         *roomv1.RequestMeta
	TargetUserID int64
	SeatNo       int32
}

// RobotVirtualMicUp 把机器人放到静音虚拟麦位，不要求真实 RTC 推流。
func (s *Service) RobotVirtualMicUp(ctx context.Context, input RobotMicUpInput) (*roomv1.MicUpResponse, error) {
	ctx = contextFromMeta(ctx, input.Meta)
	cmd := command.RobotMicUp{
		Base:         baseFromMeta(input.Meta),
		TargetUserID: input.TargetUserID,
		SeatNo:       input.SeatNo,
	}
	if cmd.TargetUserID <= 0 {
		cmd.TargetUserID = cmd.ActorUserID()
	}
	cmd.MicSessionID = micSessionIDForCommand(cmd.RoomID(), cmd.ID())

	result, err := s.mutateRoom(ctx, cmd, true, func(now time.Time, current *state.RoomState, _ *rank.LocalRank) (mutationResult, []outbox.Record, error) {
		if err := requireActiveRoom(current); err != nil {
			return mutationResult{}, nil, err
		}
		if _, exists := current.OnlineUsers[cmd.TargetUserID]; !exists {
			return mutationResult{}, nil, xerr.New(xerr.NotFound, "robot not in room")
		}
		if _, exists := current.SeatByUser(cmd.TargetUserID); exists {
			return mutationResult{}, nil, xerr.New(xerr.Conflict, "robot already on seat")
		}
		index := current.SeatIndex(cmd.SeatNo)
		if index < 0 {
			return mutationResult{}, nil, xerr.New(xerr.InvalidArgument, "seat does not exist")
		}
		if current.MicSeats[index].Locked {
			return mutationResult{}, nil, xerr.New(xerr.PermissionDenied, "seat is locked")
		}
		if current.MicSeats[index].UserID != 0 {
			return mutationResult{}, nil, xerr.New(xerr.Conflict, "seat is occupied")
		}

		current.Version++
		current.MicSeats[index].UserID = cmd.TargetUserID
		current.MicSeats[index].PublishState = state.MicPublishPublishing
		current.MicSeats[index].MicSessionID = cmd.MicSessionID
		current.MicSeats[index].PublishDeadlineMS = 0
		current.MicSeats[index].MicSessionRoomVersion = current.Version
		current.MicSeats[index].LastPublishEventTimeMS = now.UnixMilli()
		current.MicSeats[index].MicHeartbeatAtMS = now.UnixMilli()
		current.MicSeats[index].MicMuted = true
		seatStatus := current.MicSeats[index].SeatStatus()
		targetGiftValue := roomUserGiftValue(current, cmd.TargetUserID)

		micEvent, err := outbox.Build(current.RoomID, "RoomMicChanged", current.Version, now, &roomeventsv1.RoomMicChanged{
			ActorUserId:       cmd.ActorUserID(),
			TargetUserId:      cmd.TargetUserID,
			FromSeat:          0,
			ToSeat:            cmd.SeatNo,
			Action:            "robot_up",
			MicSessionId:      cmd.MicSessionID,
			PublishState:      state.MicPublishPublishing,
			PublishDeadlineMs: 0,
			SeatStatus:        seatStatus,
			TargetGiftValue:   targetGiftValue,
		})
		if err != nil {
			return mutationResult{}, nil, err
		}

		return mutationResult{
			snapshot:          current.ToProto(),
			seatNo:            cmd.SeatNo,
			micSessionID:      cmd.MicSessionID,
			publishDeadlineMS: 0,
			syncEvent: &tencentim.RoomEvent{
				EventID:      micEvent.EventID,
				RoomID:       current.RoomID,
				EventType:    "room_mic_publish_confirmed",
				ActorUserID:  tencentim.FormatOptionalUserID(cmd.ActorUserID()),
				TargetUserID: tencentim.FormatOptionalUserID(cmd.TargetUserID),
				SeatNo:       cmd.SeatNo,
				RoomVersion:  current.Version,
				Attributes: map[string]string{
					"action":            "robot_up",
					"publish_state":     state.MicPublishPublishing,
					"mic_session_id":    cmd.MicSessionID,
					"seat_status":       seatStatus,
					"mic_muted":         "true",
					"target_gift_value": int64String(targetGiftValue),
				},
			},
		}, []outbox.Record{micEvent}, nil
	})
	if err != nil {
		return nil, err
	}
	if result.micSessionID == "" {
		if seat := protoSeatByUser(result.snapshot, cmd.TargetUserID); seat != nil {
			result.seatNo = seat.GetSeatNo()
			result.micSessionID = seat.GetMicSessionId()
		}
	}
	return &roomv1.MicUpResponse{
		Result:            commandResult(result.applied, result.snapshot.GetVersion(), s.clock.Now()),
		SeatNo:            result.seatNo,
		Room:              result.snapshot,
		MicSessionId:      result.micSessionID,
		PublishDeadlineMs: 0,
	}, nil
}
