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

import com.google.common.collect.Lists;
import com.red.circle.framework.mybatis.constant.PageConstant;
import com.red.circle.other.infra.database.mongo.entity.team.team.TeamApplicationProcess;
import com.red.circle.other.infra.database.mongo.service.team.team.TeamApplicationProcessService;
import com.red.circle.other.inner.model.cmd.team.TeamApplicationProcessChangeCmd;
import com.red.circle.other.inner.model.cmd.team.TeamApplicationProcessQryCmd;
import com.red.circle.other.inner.enums.team.TeamAppProcessReason;
import com.red.circle.other.inner.enums.team.TeamApplicationProcessStatus;
import com.red.circle.other.inner.enums.team.TeamApplicationType;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.date.TimestampUtils;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
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/12/9
 */
@Service
@RequiredArgsConstructor
public class TeamApplicationProcessServiceImpl implements TeamApplicationProcessService {

  private final MongoTemplate mongoTemplate;

  @Override
  public void add(TeamApplicationProcess process) {
    if (Objects.isNull(process.getExpiredTime())) {
      process.setExpiredTime(TimestampUtils.nowPlusDays(60));
    }
    mongoTemplate.save(process);
  }

  @Override
  public void changeStatus(TeamApplicationProcessChangeCmd param) {

    mongoTemplate.updateFirst(Query.query(Criteria.where("id").is(param.getId())),
        new Update()
            .set("status", param.getStatus())
            .set("updateUser", param.getUpdateUserId())
            .set("updateTime", TimestampUtils.now())
            .set("updateUserOrigin", param.getUpdateUserOrigin()),
        TeamApplicationProcess.class);
  }

  @Override
  public void changeUserTeamWaitReject(Long userId) {
    mongoTemplate.updateFirst(Query.query(Criteria.where("createUser").is(userId)),
        new Update()
            .set("type", TeamApplicationType.TEAM)
            .set("status", TeamApplicationProcessStatus.REJECT)
            .set("updateTime", TimestampUtils.now()),
        TeamApplicationProcess.class);
  }

  @Override
  public List<TeamApplicationProcess> listByCondition(TeamApplicationProcessQryCmd teamQuery) {

    Criteria criteria = Criteria.where("sysOrigin").is(teamQuery.getSysOrigin());

    if (Objects.nonNull(teamQuery.getAssociateId())) {
      criteria.and("associateId").is(teamQuery.getAssociateId());
    }

    if (Objects.nonNull(teamQuery.getType())) {
      criteria.and("type").is(teamQuery.getType());
    }

    if (Objects.nonNull(teamQuery.getReason())) {
      criteria.and("reason").is(teamQuery.getReason());
    }

    if (Objects.nonNull(teamQuery.getStatus())) {
      criteria.and("status").is(teamQuery.getStatus());
    }

    if (Objects.nonNull(teamQuery.getApplicantUserId())) {
      criteria.and("createUser").is(teamQuery.getApplicantUserId());
    }

    if (Objects.nonNull(teamQuery.getLastId())) {
      criteria.and("id").lt(teamQuery.getLastId());
    }

    List<TeamApplicationProcess> list = mongoTemplate.find(Query.query(criteria)
                    .with(Sort.by(Sort.Order.desc("id")))
                    .limit(PageConstant.DEFAULT_LIMIT_SIZE),
            TeamApplicationProcess.class);
    return list;
  }

  @Override
  public void deleteById(Long id) {

    mongoTemplate.remove(Query.query(Criteria.where("id").is(id)), TeamApplicationProcess.class);
  }

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

  @Override
  public Boolean existWaitByUserId(Long userId) {
    return mongoTemplate.exists(Query.query(Criteria.where("createUser").is(userId)
        .and("status").is(TeamApplicationProcessStatus.WAIT)), TeamApplicationProcess.class);
  }

  @Override
  public String getQuitWaitApplyId(Long userId) {
    return Optional.ofNullable(
            mongoTemplate.findOne(Query.query(Criteria.where("createUser").is(userId)
                .and("type").is(TeamApplicationType.TEAM)
                .and("status").is(TeamApplicationProcessStatus.WAIT)
                .and("reason").is(TeamAppProcessReason.QUIT)), TeamApplicationProcess.class))
        .map(TeamApplicationProcess::getId)
        .map(Objects::toString)
        .orElse("");
  }

  @Override
  public TeamApplicationProcess getQuitWaitApplyById(Long id) {
    return mongoTemplate.findOne(Query.query(Criteria.where("id").is(id)
        .and("type").is(TeamApplicationType.TEAM)
        .and("status").is(TeamApplicationProcessStatus.WAIT)
        .and("reason").is(TeamAppProcessReason.QUIT)), TeamApplicationProcess.class);
  }


  @Override
  public TeamApplicationProcess getJoinWaitApplyById(Long id) {
    return mongoTemplate.findOne(Query.query(Criteria.where("id").is(id)
        .and("type").is(TeamApplicationType.TEAM)
        .and("status").is(TeamApplicationProcessStatus.WAIT)
        .and("reason").is(TeamAppProcessReason.JOIN)), TeamApplicationProcess.class);
  }

  @Override
  public Boolean existTeamWaitByTeamId(Long teamId) {
    return mongoTemplate.exists(Query.query(Criteria.where("associateId").is(teamId)
        .and("type").is(TeamApplicationType.TEAM)
        .and("status").is(TeamApplicationProcessStatus.WAIT)), TeamApplicationProcess.class);
  }

  @Override
  public Long countTeamWaitByTeamId(Long teamId) {
    return mongoTemplate.count(Query.query(Criteria.where("associateId").is(teamId)
        .and("type").is(TeamApplicationType.TEAM)
        .and("status").is(TeamApplicationProcessStatus.WAIT)), TeamApplicationProcess.class);
  }

  @Override
  public List<TeamApplicationProcess> listTeamByTeamId(Long teamId,
      TeamApplicationProcessStatus status) {
    return mongoTemplate.find(Query.query(Criteria.where("associateId").is(teamId)
        .and("type").is(TeamApplicationType.TEAM)
        .and("status").is(status)), TeamApplicationProcess.class);
  }

  @Override
  public TeamApplicationProcess getTeamWaitApprovalJoin(Long userId) {
    return mongoTemplate.findOne(Query.query(Criteria.where("type").is(TeamApplicationType.TEAM)
            .and("reason").is(TeamAppProcessReason.JOIN)
            .and("status").is(TeamApplicationProcessStatus.WAIT)
            .and("createUser").is(userId)
        ).with(Sort.by(Sort.Order.desc("id"))),
        TeamApplicationProcess.class);
  }

  @Override
  public void remove(Long associateId, Long userId) {
    mongoTemplate.remove(Query.query(Criteria.where("associateId").is(associateId)
        .and("createUser").is(userId)), TeamApplicationProcess.class);
  }

  @Override
  public void removeBatchByAssociateId(List<Long> associateIds) {
    mongoTemplate.remove(Query.query(Criteria.where("associateId").in(associateIds)),
        TeamApplicationProcess.class);
  }

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

  @Override
  public List<TeamApplicationProcess> listApplyOutTeamMember(Integer day) {

    return mongoTemplate
        .find(Query.query(Criteria.where("type").is(TeamApplicationType.TEAM)
                .and("reason").is(TeamAppProcessReason.QUIT)
                .and("status").is(TeamApplicationProcessStatus.WAIT)
                .and("createTime").lt(TimestampUtils.nowMinusDays(day))),
            TeamApplicationProcess.class);

  }

  @Override
  public List<TeamApplicationProcess> listLastMonthApplyOutTeamMember() {

    return mongoTemplate
        .find(Query.query(Criteria.where("type").is(TeamApplicationType.TEAM)
                .and("reason").is(TeamAppProcessReason.QUIT)
                .and("status").is(TeamApplicationProcessStatus.WAIT)
                .andOperator(
                    Criteria.where("createTime").gte(TimestampUtils.firstDayOfLastMonth()),
                    Criteria.where("createTime").lte(TimestampUtils.lastDayOfLastMonth())
                )),
            TeamApplicationProcess.class);
  }

  @Override
  public void removeByUserId(Long memberUserId) {
    mongoTemplate.remove(Query.query(Criteria.where("createUser").is(memberUserId)),
        TeamApplicationProcess.class);
  }

  @Override
  public TeamApplicationProcess getQuitWaitApply(Long userId) {
    return mongoTemplate.findOne(Query.query(Criteria.where("createUser").is(userId)
        .and("type").is(TeamApplicationType.TEAM)
        .and("status").is(TeamApplicationProcessStatus.WAIT)
        .and("reason").is(TeamAppProcessReason.QUIT)), TeamApplicationProcess.class);
  }

  @Override
  public void deleteWaitByAppUserIds(Set<Long> userIds) {
    mongoTemplate.remove(Query.query(Criteria.where("createUserOrigin").is(0)
            .and("status").is(TeamApplicationProcessStatus.WAIT)
            .and("createUser").in(userIds)),
        TeamApplicationProcess.class);
  }

  @Override
  public List<TeamApplicationProcess> listByIds(Set<Long> ids) {
    if (CollectionUtils.isEmpty(ids)) {
      return Lists.newArrayList();
    }

    return mongoTemplate
            .find(Query.query(Criteria.where("type").is(TeamApplicationType.TEAM)
                            .and("reason").is(TeamAppProcessReason.QUIT)
                            .and("status").is(TeamApplicationProcessStatus.WAIT)
                            .and("id").in(ids)),
                    TeamApplicationProcess.class);
  }
}
