package com.liquidnet.service.adam.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.liquidnet.common.cache.redis.util.RedisUtil;
import com.liquidnet.common.mq.constant.MQConst;
import com.liquidnet.commons.lang.util.BsonUtil;
import com.liquidnet.commons.lang.util.SqlMapping;
import com.liquidnet.service.adam.constant.AdamRedisConst;
import com.liquidnet.service.adam.entity.AdamEnters;
import com.liquidnet.service.adam.mapper.AdamEntersMapper;
import com.liquidnet.service.adam.service.IAdamEntersService;
import com.mongodb.BasicDBObject;
import com.mongodb.client.model.FindOneAndUpdateOptions;
import com.mongodb.client.model.ReturnDocument;
import com.mongodb.client.result.UpdateResult;
import lombok.extern.slf4j.Slf4j;
import org.bson.Document;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.time.LocalDateTime;
import java.util.*;

/**
 * <p>
 * 入场人 服务实现类
 * </p>
 *
 * @author liquidnet
 * @since 2021-04-28
 */
@Slf4j
@Service
public class AdamEntersServiceImpl extends ServiceImpl<AdamEntersMapper, AdamEnters> implements IAdamEntersService {
    @Autowired
    AdamEntersMapper adamEntersMapper;
    @Autowired
    MongoConverter mongoConverter;
    @Autowired
    MongoTemplate mongoTemplate;
    @Autowired
    RabbitTemplate rabbitTemplate;
    @Autowired
    RedisUtil redisUtil;

    @Override
    public List<AdamEnters> list(String uid) {
        List<AdamEnters> infoList = new ArrayList<>();

        Map<Object, Object> map = redisUtil.hmget(AdamRedisConst.INFO_ENTERS.concat(uid));

        if (CollectionUtils.isEmpty(map)) {
            infoList = mongoTemplate.find(Query.query(Criteria.where("uid").is(uid).and("state").is(1)), AdamEnters.class, AdamEnters.class.getSimpleName());
        } else {
            for (Object o : map.values()) {
                infoList.add((AdamEnters) o);
            }
        }

        return infoList;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void add(AdamEnters info) {
        if (info.getIsDefault()) {
            Set<Object> keys = (Set<Object>) redisUtil.hkeys(AdamRedisConst.INFO_ENTERS.concat(info.getUid()));
            if (!CollectionUtils.isEmpty(keys)) {
                AdamEnters deaultEnters = new AdamEnters();
                deaultEnters.setIsDefault(false);
                deaultEnters.setUpdatedAt(info.getCreatedAt());
                deaultEnters.setUid(info.getUid());
                BasicDBObject object = new BasicDBObject("$set", mongoConverter.convertToMongoType(deaultEnters));
                Document doc = mongoTemplate.getCollection(AdamEnters.class.getSimpleName()).findOneAndUpdate(
                        Query.query(Criteria.where("uid").is(deaultEnters.getUid()).and("isDefault").is(true)).getQueryObject(),
                        object, new FindOneAndUpdateOptions().returnDocument(ReturnDocument.AFTER)
                );

                if (null != doc) {
                    List<Object> paramList = new ArrayList<>();
                    paramList.add(deaultEnters.getIsDefault());
                    paramList.add(deaultEnters.getUpdatedAt());
                    paramList.add(doc.get("uid"));
                    rabbitTemplate.convertAndSend(MQConst.EXCHANGES_LIQUIDNET_SQL, MQConst.ROUTING_KEY_SQL,
                            SqlMapping.get("adam_enters.update.is_default", paramList.toArray()));

                    AdamEnters enters = BsonUtil.toBean(doc, AdamEnters.class);
                    redisUtil.hset(AdamRedisConst.INFO_ENTERS.concat(enters.getUid()), enters.getEntersId(), enters);
                }
            }
        }

        mongoTemplate.insert(Collections.singletonList(info), AdamEnters.class.getSimpleName());

        List<Object> paramList = new ArrayList<>();
        paramList.add(info.getEntersId());
        paramList.add(info.getUid());
        paramList.add(info.getType());
        paramList.add(info.getName());
        paramList.add(info.getMobile());
        paramList.add(info.getIdCard());
        paramList.add(info.getIsDefault());
        paramList.add(info.getState());
        paramList.add(info.getCreatedAt());
        paramList.add(info.getUpdatedAt());
        paramList.add(info.getDeletedAt());
        paramList.add(info.getComment());
        rabbitTemplate.convertAndSend(MQConst.EXCHANGES_LIQUIDNET_SQL, MQConst.ROUTING_KEY_SQL,
                SqlMapping.get("adam_enters.add", paramList.toArray()));

        redisUtil.hset(AdamRedisConst.INFO_ENTERS.concat(info.getUid()), info.getEntersId(), info);
    }

    @Override
    public AdamEnters query(String uid, String entersId) {
        AdamEnters info = (AdamEnters) redisUtil.hget(AdamRedisConst.INFO_ENTERS.concat(uid), entersId);

        if (null == info) {
            info = mongoTemplate.findOne(Query.query(Criteria.where("entersId").is(entersId)), AdamEnters.class, AdamEnters.class.getSimpleName());
        }

        return info;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void edit(AdamEnters info) {
        AdamEnters oldEnters = (AdamEnters) redisUtil.hget(AdamRedisConst.INFO_ENTERS.concat(info.getUid()), info.getEntersId());
        if (null == oldEnters) return;
        if (info.getIsDefault() && !oldEnters.getIsDefault()) {
            AdamEnters deaultEnters = new AdamEnters();
            deaultEnters.setIsDefault(false);
            deaultEnters.setUpdatedAt(info.getUpdatedAt());
            deaultEnters.setUid(info.getUid());
            BasicDBObject object = new BasicDBObject("$set", mongoConverter.convertToMongoType(deaultEnters));
            Document doc = mongoTemplate.getCollection(AdamEnters.class.getSimpleName()).findOneAndUpdate(
                    Query.query(Criteria.where("uid").is(deaultEnters.getUid()).and("isDefault").is(true)).getQueryObject(),
                    object, new FindOneAndUpdateOptions().returnDocument(ReturnDocument.AFTER)
            );

            if (null != doc) {
                List<Object> paramList = new ArrayList<>();
                paramList.add(deaultEnters.getIsDefault());
                paramList.add(deaultEnters.getUpdatedAt());
                paramList.add(doc.get("uid"));
                rabbitTemplate.convertAndSend(MQConst.EXCHANGES_LIQUIDNET_SQL, MQConst.ROUTING_KEY_SQL,
                        SqlMapping.get("adam_enters.update.is_default", paramList.toArray()));

                AdamEnters enters = BsonUtil.toBean(doc, AdamEnters.class);
                redisUtil.hset(AdamRedisConst.INFO_ENTERS.concat(enters.getUid()), enters.getEntersId(), enters);
            }
        }

        BasicDBObject object = new BasicDBObject("$set", mongoConverter.convertToMongoType(info));
        Document doc = mongoTemplate.getCollection(AdamEnters.class.getSimpleName()).findOneAndUpdate(
                Query.query(Criteria.where("entersId").is(info.getEntersId())).getQueryObject(),
                object, new FindOneAndUpdateOptions().returnDocument(ReturnDocument.AFTER)
        );

        List<Object> paramList = new ArrayList<>();
        paramList.add(info.getType());
        paramList.add(info.getName());
        paramList.add(info.getMobile());
        paramList.add(info.getIdCard());
        paramList.add(info.getIsDefault());
        paramList.add(info.getState());
        paramList.add(info.getUpdatedAt());
        paramList.add(info.getEntersId());
        rabbitTemplate.convertAndSend(MQConst.EXCHANGES_LIQUIDNET_SQL, MQConst.ROUTING_KEY_SQL,
                SqlMapping.get("adam_enters.edit", paramList.toArray()));

        redisUtil.hset(AdamRedisConst.INFO_ENTERS.concat(info.getUid()), info.getEntersId(), BsonUtil.toBean(doc, AdamEnters.class));
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void remove(String uid, String entersId) {
        AdamEnters oldEnters = (AdamEnters) redisUtil.hget(AdamRedisConst.INFO_ENTERS.concat(uid), entersId);
        if (null == oldEnters) return;
        oldEnters.setUpdatedAt(LocalDateTime.now());
        oldEnters.setState(2);

        BasicDBObject object = new BasicDBObject("$set", mongoConverter.convertToMongoType(oldEnters));
        UpdateResult updateResult = mongoTemplate.getCollection(AdamEnters.class.getSimpleName())
                .updateOne(Query.query(Criteria.where("entersId").is(entersId)).getQueryObject(), object);

        List<Object> paramList = new ArrayList<>();
        paramList.add(oldEnters.getState());
        paramList.add(oldEnters.getUpdatedAt());
        paramList.add(oldEnters.getUpdatedAt());
        paramList.add(entersId);
        rabbitTemplate.convertAndSend(MQConst.EXCHANGES_LIQUIDNET_SQL, MQConst.ROUTING_KEY_SQL,
                SqlMapping.get("adam_enters.remove", paramList.toArray()));

        redisUtil.hdel(AdamRedisConst.INFO_ENTERS.concat(uid), entersId);
    }
}
