package com.red.circle.live.infra.database.mongo.service.impl;

import com.red.circle.live.infra.database.mongo.entity.LiveMicSetting;
import com.red.circle.live.infra.database.mongo.entity.LiveMicSettingSeat;
import com.red.circle.live.infra.database.mongo.service.LiveMicSettingService;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.date.DateUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;

/**
 * 直播麦克风 设置服务.
 *
 * @author pengliang on 2023/12/11
 */
@Service
@RequiredArgsConstructor
public class LiveMicSettingServiceImpl implements LiveMicSettingService {

  private final MongoTemplate mongoTemplate;

  @Override
  public LiveMicSetting updateMicSize(Long roomId, int micSize) {

    LiveMicSetting oldMicSetting = getLiveMicSetting(roomId);

    if (Objects.isNull(oldMicSetting)) {
      LiveMicSetting newMicSetting = new LiveMicSetting()
          .setRoomId(roomId)
          .setMicSeats(createDefaultSeats(micSize))
          .setCreateTime(DateUtils.now())
          .setUpdateTime(DateUtils.now());
      mongoTemplate.save(newMicSetting);
      return newMicSetting;
    }

    int oldSize = oldMicSetting.getMicSeats().size();

    if (Objects.equals(oldSize, micSize)) {
      return oldMicSetting;
    }

    if (Objects.equals(micSize, 0)) {
      oldMicSetting.setMicSeats(CollectionUtils.newArrayList());
      updateLiveMicSetting(oldMicSetting);
      return oldMicSetting;
    }

    if (oldSize > micSize) {
      oldMicSetting.setMicSeats(oldMicSetting.getMicSeats().stream().limit(micSize).toList());
      updateLiveMicSetting(oldMicSetting);
      return oldMicSetting;
    }

    fillMicSetting(oldMicSetting.getMicSeats(), micSize - oldSize);
    updateLiveMicSetting(oldMicSetting);
    return oldMicSetting;
  }

  @Override
  public void updateMicSeat(Long roomId, LiveMicSettingSeat settingSeat) {
    mongoTemplate.updateFirst(
        Query.query(Criteria.where("roomId").is(roomId)),
        new Update()
            .set("micSeats.$[i].micLock", settingSeat.getMicLock())
            .set("micSeats.$[i].micMute", settingSeat.getMicMute())
            .set("micSeats.$[i].updateTime", DateUtils.now())
            .set("updateTime", DateUtils.now())
            .filterArray(Criteria.where("i.micIndex").is(settingSeat.getMicIndex())),
        LiveMicSetting.class);
  }

  @Override
  public LiveMicSettingSeat getMicSeat(Long roomId, Integer micIndex) {
    List<LiveMicSettingSeat> micSettingSeats = listMicSeat(roomId);
    if (CollectionUtils.isEmpty(micSettingSeats)) {
      return null;
    }

    return micSettingSeats.stream().filter(seat -> Objects.equals(seat.getMicIndex(), micIndex))
        .findFirst().orElse(null);
  }

  @Override
  public List<LiveMicSettingSeat> listMicSeat(Long roomId) {
    return Optional.ofNullable(getLiveMicSetting(roomId))
        .map(LiveMicSetting::getMicSeats)
        .orElseGet(CollectionUtils::newArrayList);
  }

  @Override
  public boolean checkLock(Long roomId, Integer micIndex) {

    LiveMicSetting setting = getLiveMicSetting(roomId);

    if (Objects.isNull(setting)) {
      return false;
    }

    return setting.getMicSeats().stream()
        .anyMatch(seat -> Objects.equals(seat.getMicIndex(), micIndex)
            && Objects.equals(seat.getMicLock(), Boolean.TRUE));
  }

  @Override
  public boolean exists(Long roomId) {
    Criteria criteria = Criteria.where("roomId").is(roomId);
    return mongoTemplate.exists(Query.query(criteria), LiveMicSetting.class);
  }

  private void updateLiveMicSetting(LiveMicSetting liveMicSetting) {
    mongoTemplate.updateFirst(Query.query(Criteria.where("id").is(liveMicSetting.getId())),
        new Update()
            .set("micSeats", liveMicSetting.getMicSeats())
            .set("updateTime", DateUtils.now()),
        LiveMicSetting.class);
  }

  private List<LiveMicSettingSeat> createDefaultSeats(int micSize) {
    List<LiveMicSettingSeat> micSeats = new ArrayList<>();
    for (int index = 0; index < micSize; index++) {
      micSeats.add(new LiveMicSettingSeat()
          .setMicIndex(index)
          .setMicLock(Boolean.FALSE)
          .setMicMute(Boolean.FALSE)
          .setUpdateTime(DateUtils.now())
      );
    }
    return micSeats;
  }

  private void fillMicSetting(List<LiveMicSettingSeat> micSeats, int fillSize) {
    micSeats.addAll(createDefaultSeats(fillSize));
    // 重置麦克风位置.
    int size = micSeats.size();
    for (int index = 0; index < size; index++) {
      micSeats.get(index).setMicIndex(index);
    }
  }

  private LiveMicSetting getLiveMicSetting(Long roomId) {
    Criteria criteria = Criteria.where("roomId").is(roomId);
    return mongoTemplate.findOne(Query.query(criteria),
        LiveMicSetting.class);
  }

}
