package com.red.circle.wallet.infra.database.mongo.service.bank.impl;

import com.google.common.collect.Maps;
import com.red.circle.common.business.core.enums.ApprovalStatusEnum;
import com.red.circle.component.mongodb.page.MongoPageHelper;
import com.red.circle.framework.dto.PageResult;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.date.TimestampUtils;
import com.red.circle.tool.core.text.StringUtils;
import com.red.circle.wallet.infra.database.mongo.entity.bank.UserBankCard;
import com.red.circle.wallet.infra.database.mongo.service.bank.UserBankCardService;
import com.red.circle.wallet.inner.model.cmd.UserBankCardBackQryCmd;
import java.util.List;
import java.util.Map;
import java.util.Objects;
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 pengshigang on 2022/12/2
 */
@Service
@RequiredArgsConstructor
public class UserBankCardServiceImpl implements UserBankCardService {

  private final MongoTemplate mongoTemplate;
  private final MongoPageHelper mongoPageHelper;

  @Override
  public Boolean exist(Long userId, String cardType, String cardNo) {
    return mongoTemplate.exists(Query.query(Criteria.where("userId").is(userId)
        .and("cardNo").is(cardNo)
        .and("cardType").is(cardType).and("del").is(Boolean.FALSE)), UserBankCard.class);
  }

  @Override
  public UserBankCard getById(Long id) {

    Query query = new Query();
    query.addCriteria(Criteria.where("id").is(id));
    return mongoTemplate.findOne(query, UserBankCard.class);
  }

  @Override
  public UserBankCard getUseByUserId(Long userId) {
    return mongoTemplate.findOne(Query.query(Criteria.where("userId").is(userId)
        .and("use").is(Boolean.TRUE).and("del").is(Boolean.FALSE)), UserBankCard.class);
  }

  @Override
  public void add(UserBankCard card) {
    card.setUse(Boolean.FALSE);
    mongoTemplate.save(card);
  }

  @Override
  public void update(UserBankCard card) {

    mongoTemplate.updateFirst(Query.query(Criteria.where("id").is(card.getId())),
        new Update()
            .set("status", card.getStatus())
            .set("payee", card.getPayee())
            .set("cardNo", card.getCardNo())
            .set("updateTime", card.getUpdateTime())
            .set("updateUser", card.getUpdateUser()),
        UserBankCard.class);
  }

  @Override
  public List<UserBankCard> listByUserId(Long userId) {
    Query query = new Query();
    query.addCriteria(Criteria.where("userId").is(userId).and("del").is(Boolean.FALSE));
    query.with(Sort.by(Sort.Order.desc("createTime"), Sort.Order.desc("updateTime")));
    return mongoTemplate.find(query, UserBankCard.class);

  }

  @Override
  public List<UserBankCard> listPassByUserId(Long userId) {
    Query query = Query.query(Criteria.where("userId").is(userId)
            .and("status").is(ApprovalStatusEnum.PASS)
            .and("del").is(Boolean.FALSE))
        .with(Sort.by(Sort.Order.desc("createTime"), Sort.Order.desc("updateTime")));
    return mongoTemplate.find(query, UserBankCard.class);
  }

  @Override
  public void pass(Set<Long> ids, Long currentUidLong) {

    mongoTemplate
        .updateMulti(Query.query(Criteria.where("id").in(ids)),
            new Update()
                .set("updateUser", currentUidLong)
                .set("updateTime", TimestampUtils.now())
                .set("status", ApprovalStatusEnum.PASS.name()),
            UserBankCard.class);
  }

  @Override
  public void notPass(Set<Long> ids, Long currentUidLong) {

    mongoTemplate
        .updateMulti(Query.query(Criteria.where("id").in(ids)),
            new Update()
                .set("use", Boolean.FALSE)
                .set("updateUser", currentUidLong)
                .set("updateTime", TimestampUtils.now())
                .set("status", ApprovalStatusEnum.NOT_PASS.name()),
            UserBankCard.class);
  }

  @Override
  public PageResult<UserBankCard> pageQuery(UserBankCardBackQryCmd query) {

    Criteria criteria = Criteria.where("sysOrigin").is(query.getSysOrigin())
        .and("status").is(query.getStatus()).and("del").is(Boolean.FALSE);

    if (Objects.nonNull(query.getUserId())) {
      criteria.and("userId").is(query.getUserId());
    }

    if (StringUtils.isNotBlank(query.getCardType())) {
      criteria.and("cardType").is(query.getCardType());
    }

    return mongoPageHelper
        .query(new Query(criteria),
            UserBankCard.class,
            query.getPageQuery().getLimit(),
            query.getPageQuery().getCursor(),
            Sort.Order.desc("createTime"));
  }

  @Override
  public void deleteByIdByUserId(Long id, Long userId, Long currentUidLong) {
    mongoTemplate.updateFirst(Query.query(Criteria.where("id").is(id).and("userId").is(userId)),
        new Update()
            .set("del", Boolean.TRUE)
            .set("updateUser", currentUidLong)
            .set("updateTime", TimestampUtils.now()),
        UserBankCard.class);
  }

  @Override
  public void use(Long id, Long userId, Long currentUidLong) {
    Query query = new Query();
    query.addCriteria(
        Criteria.where("id").is(id).and("userId").is(userId).and("del").is(Boolean.FALSE));
    UserBankCard userBankCard = mongoTemplate.findOne(query, UserBankCard.class);
    if (Objects.isNull(userBankCard)) {
      return;
    }
    mongoTemplate.updateMulti(Query.query(Criteria.where("userId").is(userBankCard.getUserId())),
        new Update()
            .set("use", Boolean.FALSE)
            .set("updateUser", currentUidLong)
            .set("updateTime", TimestampUtils.now()),
        UserBankCard.class);

    mongoTemplate.updateFirst(Query.query(Criteria.where("id").is(id)),
        new Update()
            .set("updateUser", currentUidLong)
            .set("updateTime", TimestampUtils.now())
            .set("use", Boolean.TRUE),
        UserBankCard.class);
  }

  @Override
  public UserBankCard getOne(Long userId, String cardType, String cardNo) {

    Query query = new Query();
    query.addCriteria(Criteria.where("userId").is(userId)
        .and("cardType").is(cardType)
        .and("cardNo").is(cardNo));
    return mongoTemplate.findOne(query, UserBankCard.class);

  }

  @Override
  public void removeBatchByUserId(List<Long> userId) {
    mongoTemplate.remove(Query.query(Criteria.where("userId").in(userId)), UserBankCard.class);
  }

  @Override
  public Map<Long, UserBankCard> mapByIds(Set<Long> ids) {
    if (CollectionUtils.isEmpty(ids)) {
      return Maps.newHashMap();
    }

    List<UserBankCard> applies = mongoTemplate.find(Query.query(Criteria.where("id").in(ids)),
        UserBankCard.class);

    if (CollectionUtils.isEmpty(applies)) {
      return Maps.newHashMap();
    }

    return applies.stream().collect(Collectors.toMap(UserBankCard::getId, v -> v));
  }

}
