package service

import (
	"context"
	"strings"
	"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"
)

func (s *Service) requireRoomOpenForEntry(ctx context.Context, roomID string) error {
	closed, err := s.isRoomClosed(ctx, roomID)
	if err != nil || !closed {
		return err
	}
	return xerr.New(xerr.RoomClosed, "room closed")
}

func (s *Service) isRoomClosed(ctx context.Context, roomID string) (bool, error) {
	roomMeta, exists, err := s.repository.GetRoomMeta(ctx, roomID)
	if err != nil || !exists {
		return false, err
	}
	return isClosedRoomStatus(roomMeta.Status), nil
}

func isClosedRoomStatus(status string) bool {
	value := strings.TrimSpace(status)
	return value != "" && !strings.EqualFold(value, state.RoomStatusActive)
}

// CloseRoom 关闭房间，统一清理 presence、麦位、读模型状态和房间外事件。
func (s *Service) CloseRoom(ctx context.Context, req *roomv1.CloseRoomRequest) (*roomv1.CloseRoomResponse, error) {
	ctx = contextFromMeta(ctx, req.GetMeta())
	cmd := command.CloseRoom{
		Base:   baseFromMeta(req.GetMeta()),
		Reason: strings.TrimSpace(req.GetReason()),
	}
	if cmd.Reason == "" {
		cmd.Reason = "closed_by_owner"
	}

	result, err := s.mutateRoom(ctx, cmd, true, func(now time.Time, current *state.RoomState, _ *rank.LocalRank) (mutationResult, []outbox.Record, error) {
		if current == nil {
			return mutationResult{}, nil, xerr.New(xerr.NotFound, "room not found")
		}
		if current.Status == state.RoomStatusClosed {
			return mutationResult{snapshot: current.ToProto()}, nil, nil
		}
		if current.Status != state.RoomStatusActive && current.Status != state.RoomStatusClosing {
			return mutationResult{}, nil, xerr.New(xerr.Conflict, "room is not active")
		}
		if err := requireOwnerPresent(current, cmd.ActorUserID()); err != nil {
			return mutationResult{}, nil, err
		}

		type occupiedMicSeat struct {
			userID       int64
			seatNo       int32
			micSessionID string
		}
		occupiedSeats := make([]occupiedMicSeat, 0, len(current.MicSeats))
		for _, seat := range current.MicSeats {
			if seat.UserID != 0 && seat.MicSessionID != "" {
				occupiedSeats = append(occupiedSeats, occupiedMicSeat{
					userID:       seat.UserID,
					seatNo:       seat.SeatNo,
					micSessionID: seat.MicSessionID,
				})
			}
		}

		current.Status = state.RoomStatusClosed
		current.OnlineUsers = map[int64]*state.RoomUserState{}
		for index := range current.MicSeats {
			current.ClearMicSession(index)
		}
		current.Version++

		closedEvent, err := outbox.Build(current.RoomID, "RoomClosed", current.Version, now, &roomeventsv1.RoomClosed{
			ActorUserId: cmd.ActorUserID(),
			Reason:      cmd.Reason,
		})
		if err != nil {
			return mutationResult{}, nil, err
		}
		records := make([]outbox.Record, 0, len(occupiedSeats)+1)
		for _, seat := range occupiedSeats {
			// 关房是 Room Cell 统一清场，给每个占麦 session 单独写 down 事件，保证用户时长能闭合。
			micEvent, err := buildMicDownEventForSeat(current.RoomID, current.Version, now, cmd.ActorUserID(), seat.userID, seat.seatNo, seat.micSessionID, "room_closed")
			if err != nil {
				return mutationResult{}, nil, err
			}
			records = append(records, micEvent)
		}
		records = append(records, closedEvent)

		return mutationResult{
			snapshot:   current.ToProto(),
			roomStatus: state.RoomStatusClosed,
			syncEvent: &tencentim.RoomEvent{
				EventID:     closedEvent.EventID,
				RoomID:      current.RoomID,
				EventType:   "room_closed",
				ActorUserID: cmd.ActorUserID(),
				RoomVersion: current.Version,
				Attributes: map[string]string{
					"reason": cmd.Reason,
				},
			},
		}, records, nil
	})
	if err != nil {
		return nil, err
	}

	return &roomv1.CloseRoomResponse{
		Result: commandResult(result.applied, result.snapshot.GetVersion(), s.clock.Now()),
		Room:   result.snapshot,
	}, nil
}
