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

import com.google.common.collect.Lists;
import com.mongodb.client.model.Filters;
import com.red.circle.component.mongodb.config.MongoPrimaryService;
import com.red.circle.component.mongodb.page.MongoPageHelper;
import com.red.circle.framework.dto.PageResult;
import com.red.circle.other.infra.database.mongo.entity.game.ktv.GameKtvUserSongCount;
import com.red.circle.other.infra.database.mongo.entity.game.ktv.query.GameKtvUserSongQuery;
import com.red.circle.other.infra.database.mongo.service.game.GameKtvUserSongCountService;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.date.TimestampUtils;
import com.red.circle.tool.core.sequence.IdWorkerUtils;
import com.red.circle.tool.core.text.StringUtils;
import java.sql.Timestamp;
import java.util.Comparator;
import java.util.Date;
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.bson.Document;
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;

/**
 * Ktv用户已点歌单.
 *
 * @author pengshigang on 2023/5/5
 */

@Service
@RequiredArgsConstructor
public class GameKtvUserSongCountServiceImpl implements GameKtvUserSongCountService {

  private final MongoTemplate mongoTemplate;
  private final MongoPageHelper mongoPageHelper;
  private final MongoPrimaryService mongoPrimaryService;

  @Override
  public void inc(Long id, Long value) {

    if (Objects.isNull(id) || value <= 0L) {
      return;
    }

    if (mongoPrimaryService.exists("game_ktv_user_song_count",
        Filters.and(Filters.eq("_id", id)))) {

      mongoTemplate.updateFirst(Query.query(Criteria.where("id").is(id)),
          new Update().inc("popularityValue", value).set("updateTime", TimestampUtils.now()),
          GameKtvUserSongCount.class);
    }

  }

  @Override
  public void add(GameKtvUserSongCount count) {

    count.setId(IdWorkerUtils.getId());
    count.setPopularityValue(0L);
    count.setCreateTime(TimestampUtils.now());
    count.setExpiredTime(TimestampUtils.nowPlusDays(30));
    mongoTemplate.save(count);

  }

  @Override
  public Boolean exist(Long id) {
    return mongoPrimaryService.exists("game_ktv_user_song_count", Filters.eq("_id", id));
  }

  @Override
  public Boolean existByRoomId(Long roomId) {
    return mongoPrimaryService.exists("game_ktv_user_song_count",
        Filters.and(
            Filters.eq("roomId", roomId),
            Filters.ne("status", "END"))
    );
  }

  @Override
  public GameKtvUserSongCount getById(Long id) {

    return mongoPrimaryService.find("game_ktv_user_song_count",
        Filters.eq("_id", id),
        documents -> {

          if (CollectionUtils.isEmpty(documents)) {
            return null;
          }
          return getBuildKtvCount(documents.get(0));
        });

  }

  private GameKtvUserSongCount getBuildKtvCount(Document document) {

    if (Objects.isNull(document)) {
      return null;
    }

    return new GameKtvUserSongCount()
        .setId(document.getLong("_id"))
        .setSysOrigin(document.getString("sysOrigin"))
        .setRegionId(document.getString("regionId"))
        .setUserId(document.getLong("userId"))
        .setRoomId(document.getLong("roomId"))
        .setSongId(document.getLong("songId"))
        .setPopularityValue(document.getLong("popularityValue"))
        .setStatus(document.getString("status"))
        .setSortTime(getTimestamp(document, "sortTime"))
        .setCreateTime(getTimestamp(document, "createTime"))
        .setUpdateTime(getTimestamp(document, "updateTime"))
        .setExpiredTime(getTimestamp(document, "expiredTime"));
  }

  private Timestamp getTimestamp(Document document, String field) {
    Date val = document.getDate(field);
    if (Objects.isNull(val)) {
      return null;
    }
    return new Timestamp(val.getTime());
  }

  @Override
  public Boolean isMaximumNumber(Long roomId, Long userId) {

    long userCount = mongoTemplate.count(
        Query.query(Criteria.where("roomId").is(roomId).and("userId").is(userId)
            .and("status").ne("END")),
        GameKtvUserSongCount.class);
    return userCount >= 10;
  }

  @Override
  public List<GameKtvUserSongCount> list(String sysOrigin, Long roomId) {

    List<Document> documents = mongoPrimaryService.find("game_ktv_user_song_count",
        Filters.and(
            Filters.eq("sysOrigin", sysOrigin),
            Filters.eq("roomId", roomId),
            Filters.ne("status", "END"))
        , v -> v);

    if (CollectionUtils.isEmpty(documents)) {
      return Lists.newArrayList();
    }

    List<GameKtvUserSongCount> songs = documents.stream().map(this::getBuildKtvCount)
        .filter(Objects::nonNull)
        .sorted(Comparator.comparing(GameKtvUserSongCount::getCreateTime).reversed())
        .collect(Collectors.toList());

    if (CollectionUtils.isEmpty(songs)) {
      return Lists.newArrayList();
    }

    Map<String, List<GameKtvUserSongCount>> listMap = songs.stream()
        .collect(Collectors.groupingBy(GameKtvUserSongCount::getStatus));
    if (CollectionUtils.isEmpty(listMap)) {
      return Lists.newArrayList();
    }

    List<GameKtvUserSongCount> result = Lists.newArrayList();
    if (CollectionUtils.isNotEmpty(listMap.get("PLAYING"))) {
      result.add(listMap.get("PLAYING").get(0));
    }

    if (CollectionUtils.isNotEmpty(listMap.get("WAIT"))) {
      result.add(listMap.get("WAIT").get(0));
    }

    result.addAll(Optional.ofNullable(listMap.get("TOP")).map(list -> list.stream()
        .sorted(Comparator.comparing(GameKtvUserSongCount::getCreateTime).reversed())
        .collect(Collectors.toList())).orElse(Lists.newArrayList()));

    result.addAll(Optional.ofNullable(listMap.get("NORMAL")).map(
        list -> list.stream().sorted(Comparator.comparing(GameKtvUserSongCount::getCreateTime))
            .collect(Collectors.toList())).orElse(Lists.newArrayList()));

    return result;
  }

  @Override
  public void updateStatus(Long id, String status) {

    if (Boolean.FALSE.equals(exist(id))) {
      return;
    }

    Update update = new Update().set("status", status).set("updateTime", TimestampUtils.now());

    if (Objects.equals(status, "TOP")) {
      update.set("sortTime", TimestampUtils.now());
    }

    mongoTemplate.updateFirst(Query.query(Criteria.where("id").is(id)), update,
        GameKtvUserSongCount.class);
  }

  @Override
  public void deleteByIds(Set<Long> ids) {

    mongoTemplate.remove(Query.query(Criteria.where("id").in(ids)), GameKtvUserSongCount.class);

  }

  @Override
  public PageResult<GameKtvUserSongCount> pageCount(GameKtvUserSongQuery query) {

    Criteria criteria = Criteria.where("sysOrigin").is(query.getSysOrigin()).and("status")
        .is("END");

    if (StringUtils.isNotBlank(query.getRegion())) {
      criteria.and("regionId").is(query.getRegion());
    }
    if (Objects.nonNull(query.getUserId())) {
      criteria.and("userId").is(query.getUserId());
    }
    if (Objects.nonNull(query.getRoomId())) {
      criteria.and("roomId").is(query.getRoomId());
    }

    return mongoPageHelper.query(new Query(criteria),
        GameKtvUserSongCount.class,
        query.getPageSize(),
        query.getPageNumber(),
        Sort.Order.desc("createTime")
    );

  }


}
