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.commons.lang.util.BsonUtil;
import com.liquidnet.service.adam.constant.AdamRedisConst;
import com.liquidnet.service.adam.dto.vo.AdamEntersVo;
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> queryByUid(String uid) {
        List<AdamEnters> infoList = new ArrayList<>();

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

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

        return infoList;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void add(AdamEnters info) {
        info.setIsDefault(CollectionUtils.isEmpty(this.queryByUid(info.getUid())));

        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).and("state").is(1)),
                    AdamEnters.class, AdamEnters.class.getSimpleName());
            if (null == info) redisUtil.hset(AdamRedisConst.INFO_ENTERS.concat(uid), entersId, info);
        }
        return info;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void def(String uid, String entersId) {
        LocalDateTime now = LocalDateTime.now();
        LinkedList<Object[]> linkedList = new LinkedList<>();
        List<AdamEnters> adamEnters = this.queryByUid(uid);
        if (adamEnters.size() > 1) {// 取消原默认
            AdamEnters unDeaultEnters = new AdamEnters();
            unDeaultEnters.setIsDefault(false);
            unDeaultEnters.setUpdatedAt(now);
            BasicDBObject object = new BasicDBObject("$set", mongoConverter.convertToMongoType(unDeaultEnters));
            Document doc = mongoTemplate.getCollection(AdamEnters.class.getSimpleName()).findOneAndUpdate(
                    Query.query(Criteria.where("uid").is(uid).and("state").is(1).and("isDefault").is(true)).getQueryObject(),
                    object, new FindOneAndUpdateOptions().returnDocument(ReturnDocument.AFTER)
            );
            if (null != doc) {
                AdamEnters enters = BsonUtil.toBean(doc, AdamEnters.class);

                List<Object> paramList = new ArrayList<>();
                paramList.add(unDeaultEnters.getIsDefault());
                paramList.add(unDeaultEnters.getUpdatedAt());
                paramList.add(enters.getEntersId());
//            rabbitTemplate.convertAndSend(MQConst.EXCHANGES_LIQUIDNET_SQL, MQConst.ROUTING_KEY_SQL,
//                    SqlMapping.get("adam_enters.update.is_default", paramList.toArray()));
                linkedList.add(paramList.toArray());

                redisUtil.hset(AdamRedisConst.INFO_ENTERS.concat(enters.getUid()), enters.getEntersId(), enters);
            }
        }
        {// 设置新默认
            AdamEnters defaultEnters = new AdamEnters();
            defaultEnters.setIsDefault(true);
            defaultEnters.setUpdatedAt(now);
            BasicDBObject object = new BasicDBObject("$set", mongoConverter.convertToMongoType(defaultEnters));
            Document doc = mongoTemplate.getCollection(AdamEnters.class.getSimpleName()).findOneAndUpdate(
                    Query.query(Criteria.where("uid").is(uid).and("entersId").is(entersId)).getQueryObject(),
                    object, new FindOneAndUpdateOptions().returnDocument(ReturnDocument.AFTER)
            );
            AdamEnters enters = BsonUtil.toBean(doc, AdamEnters.class);

            List<Object> paramList = new ArrayList<>();
            paramList.add(defaultEnters.getIsDefault());
            paramList.add(defaultEnters.getUpdatedAt());
            paramList.add(entersId);
//            rabbitTemplate.convertAndSend(MQConst.EXCHANGES_LIQUIDNET_SQL, MQConst.ROUTING_KEY_SQL,
//                    SqlMapping.get("adam_enters.update.is_default", paramList.toArray()));
            linkedList.add(paramList.toArray());

            redisUtil.hset(AdamRedisConst.INFO_ENTERS.concat(enters.getUid()), enters.getEntersId(), enters);
        }
//        rabbitTemplate.convertAndSend(MQConst.EXCHANGES_LIQUIDNET_SQL, MQConst.ROUTING_KEY_SQL,
//                SqlMapping.get("adam_enters.update.is_default", linkedList));
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void edit(AdamEnters info) {
        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 enters = new AdamEnters();
        enters.setUpdatedAt(LocalDateTime.now());
        enters.setDeletedAt(enters.getUpdatedAt());
        enters.setState(2);

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

        List<Object> paramList = new ArrayList<>();
        paramList.add(enters.getUpdatedAt());
        paramList.add(enters.getDeletedAt());
        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);
    }

    @Override
    public AdamEntersVo queryDefault(String uid) {
        List<AdamEnters> list = this.queryByUid(uid);

        if (!CollectionUtils.isEmpty(list)) {
            AdamEnters defaultVo = null;
            for (AdamEnters l : list) {
                if (l.getIsDefault()) {
                    return AdamEntersVo.getNew().copy(l);
                }
                defaultVo = null == defaultVo ? l :
                        l.getCreatedAt().isAfter(defaultVo.getCreatedAt()) ? l : defaultVo;
            }
            return AdamEntersVo.getNew().copy(defaultVo);
        }
        return null;
    }
}
