package com.red.circle.other.infra.database.mongo.service.sys.impl;

import com.google.common.collect.Maps;
import com.red.circle.other.infra.database.mongo.dto.GamePlayersUser;
import com.red.circle.other.infra.database.mongo.dto.query.GameMatchModelConfigQuery;
import com.red.circle.other.infra.database.mongo.entity.sys.GameMatchModelConfig;
import com.red.circle.other.infra.database.mongo.service.sys.GameMatchModelConfigService;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.func.VoidCallable;
import com.red.circle.tool.core.num.NumUtils;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Sort;
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 2022/7/11
 */
@Service
@RequiredArgsConstructor
public class GameMatchModelConfigServiceImpl implements GameMatchModelConfigService {

  private final MongoTemplate mongoTemplate;

  @Override
  public void add(GameMatchModelConfig config) {
    mongoTemplate.save(config);
  }

  @Override
  public GameMatchModelConfig getById(Long id) {
    return mongoTemplate.findOne(Query.query(Criteria.where("id").is(id)),
        GameMatchModelConfig.class);
  }

  @Override
  public Map<Long, GameMatchModelConfig> mapByIds(Set<Long> ids) {
    if (CollectionUtils.isEmpty(ids)) {
      return Maps.newHashMap();
    }
    return Optional.of(mongoTemplate.find(Query.query(Criteria.where("id").in(ids)),
            GameMatchModelConfig.class))
        .map(configs -> configs.stream()
            .collect(Collectors.toMap(GameMatchModelConfig::getId, v -> v)))
        .orElseGet(Maps::newHashMap);
  }

  @Override
  public List<GameMatchModelConfig> list(GameMatchModelConfigQuery query) {
    Criteria criteria = Criteria.where("sysOrigin").is(query.getSysOrigin());

    if (Objects.nonNull(query.getId())) {
      criteria.and("id").is(query.getId());
    }

    if (Objects.nonNull(query.getGameCode())) {
      criteria.and("gameCode").is(query.getGameCode());
    }

    if (Objects.nonNull(query.getShowcase())) {
      criteria.and("showcase").is(query.getShowcase());
    }

    return mongoTemplate.find(Query.query(criteria)
            .with(Sort.by(Sort.Order.asc("sort")))
            .limit(query.getLimit()),
        GameMatchModelConfig.class);
  }

  @Override
  public List<GameMatchModelConfig> listShowcaseConfig(String sysOrigin) {
    return mongoTemplate.find(Query.query(Criteria.where("sysOrigin").is(sysOrigin)
                .and("showcase").is(Boolean.TRUE))
            .with(Sort.by(Sort.Order.desc("sort")))
            .limit(100),
        GameMatchModelConfig.class);
  }

  @Override
  public void removeById(Long id) {
    mongoTemplate.remove(Query.query(Criteria.where("id").is(id)), GameMatchModelConfig.class);
  }

  @Override
  public void updateOnlinePlayer(VoidCallable<List<GamePlayersUser>> consumer) {
    List<GameMatchModelConfig> gameMatchModelConfigs = mongoTemplate.find(
        Query.query(Criteria.where("showcase").is(Boolean.TRUE))
            .limit(100),
        GameMatchModelConfig.class
    );

    if (CollectionUtils.isEmpty(gameMatchModelConfigs)) {
      return;
    }

    gameMatchModelConfigs.forEach(conf -> {
      List<GamePlayersUser> gamePlayersUsers = consumer.apply();

      if (CollectionUtils.isNotEmpty(gamePlayersUsers)) {
        mongoTemplate.updateMulti(Query.query(Criteria.where("id").is(conf.getId())),
            new Update()
                .set("onlinePlayersUsers", gamePlayersUsers),
            GameMatchModelConfig.class);
      }

      if (CollectionUtils.isEmpty(conf.getModels())) {
        return;
      }

      conf.getModels().forEach(model -> {
        Criteria filterCriteria = Criteria.where("i.players").is(model.getPlayers());

        int addQuantity = NumUtils.randomBetween(1, 50);
        int conditionQuantity = addQuantity;

        boolean topLine =
            Objects.nonNull(model.getOnlinePlayers()) && model.getOnlinePlayers() > 2000;
        if (topLine || NumUtils.randomBetween(1, 10) % 2 == 0) {
          filterCriteria.and("i.onlinePlayers").gt(conditionQuantity);
          addQuantity = -addQuantity;
        }
        mongoTemplate.updateMulti(Query.query(Criteria.where("id").is(conf.getId())),
            new Update()
                .inc("models.$[i].onlinePlayers", addQuantity)
                .filterArray(filterCriteria),
            GameMatchModelConfig.class);
      });
    });

  }


}
