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.service.adam.constant.AdamRedisConst;
import com.liquidnet.service.adam.entity.AdamAddresses;
import com.liquidnet.service.adam.mapper.AdamAddressesMapper;
import com.liquidnet.service.adam.service.IAdamAddressesService;
import com.liquidnet.service.base.SqlMapping;
import com.mongodb.BasicDBObject;
import com.mongodb.client.model.FindOneAndUpdateOptions;
import com.mongodb.client.model.ReturnDocument;
import com.mongodb.client.result.UpdateResult;
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-05-11
 */
@Service
public class AdamAddressesServiceImpl extends ServiceImpl<AdamAddressesMapper, AdamAddresses> implements IAdamAddressesService {
    @Autowired
    AdamAddressesMapper adamAddressesMapper;
    @Autowired
    MongoConverter mongoConverter;
    @Autowired
    MongoTemplate mongoTemplate;
    @Autowired
    RabbitTemplate rabbitTemplate;
    @Autowired
    RedisUtil redisUtil;

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

        mongoTemplate.insert(Collections.singletonList(adamAddresses), AdamAddresses.class.getSimpleName());

        // TODO: 2021/5/14
        List<Object> objectList = Arrays.asList();
        rabbitTemplate.convertAndSend(MQConst.EXCHANGES_LIQUIDNET_SQL, MQConst.ROUTING_KEY_SQL,
                SqlMapping.get("adam_addresses.add", objectList.toArray()));

        redisUtil.hset(AdamRedisConst.INFO_ADDRESSES.concat(adamAddresses.getUid()), adamAddresses.getAddressesId(), adamAddresses);
    }

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

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

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

        return infoList;
    }

    @Override
    public AdamAddresses query(String uid, String addressesId) {
        AdamAddresses info = (AdamAddresses) redisUtil.hget(AdamRedisConst.INFO_ADDRESSES.concat(uid), addressesId);

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

        return info;
    }

    @Override
    public void def(String uid, String addressesId) {
        LocalDateTime now = LocalDateTime.now();
        LinkedList<Object[]> linkedList = new LinkedList<>();
        {// 取消原默认
            AdamAddresses unDeaultAddresses = new AdamAddresses();
            unDeaultAddresses.setIsDefault(false);
            unDeaultAddresses.setUpdatedAt(now);
            BasicDBObject object = new BasicDBObject("$set", mongoConverter.convertToMongoType(unDeaultAddresses));
            Document doc = mongoTemplate.getCollection(AdamAddresses.class.getSimpleName()).findOneAndUpdate(
                    Query.query(Criteria.where("uid").is(uid).and("isDefault").is(true)).getQueryObject(),
                    object, new FindOneAndUpdateOptions().returnDocument(ReturnDocument.AFTER)
            );
            AdamAddresses addresses = BsonUtil.toBean(doc, AdamAddresses.class);

            List<Object> paramList = new ArrayList<>();
            paramList.add(unDeaultAddresses.getIsDefault());
            paramList.add(unDeaultAddresses.getUpdatedAt());
            paramList.add(addresses.getAddressesId());
            linkedList.add(paramList.toArray());

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

            List<Object> paramList = new ArrayList<>();
            paramList.add(defaultAddresses.getIsDefault());
            paramList.add(defaultAddresses.getUpdatedAt());
            paramList.add(addressesId);
//            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(addresses.getUid()), addresses.getAddressesId(), addresses);
        }
        rabbitTemplate.convertAndSend(MQConst.EXCHANGES_LIQUIDNET_SQL, MQConst.ROUTING_KEY_SQL,
                SqlMapping.get("adam_addresses.update.is_default", linkedList));

    }

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

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

                AdamAddresses addresses = BsonUtil.toBean(doc, AdamAddresses.class);
                redisUtil.hset(AdamRedisConst.INFO_ENTERS.concat(addresses.getUid()), addresses.getAddressesId(), addresses);
            }
        }

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

        // TODO: 2021/5/14
        List<Object> paramList = Arrays.asList(null);
        rabbitTemplate.convertAndSend(MQConst.EXCHANGES_LIQUIDNET_SQL, MQConst.ROUTING_KEY_SQL,
                SqlMapping.get("adam_addresses.edit", paramList.toArray()));

        redisUtil.hset(AdamRedisConst.INFO_ADDRESSES.concat(info.getUid()), info.getAddressesId(), BsonUtil.toBean(doc, AdamAddresses.class));
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void remove(String uid, String addressesId) {
        AdamAddresses addresses = new AdamAddresses();
        addresses.setUpdatedAt(LocalDateTime.now());
        addresses.setDeletedAt(addresses.getUpdatedAt());
        addresses.setState(2);

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

        List<Object> paramList = Arrays.asList(addresses.getUpdatedAt(), addresses.getDeletedAt(), addressesId);
        rabbitTemplate.convertAndSend(MQConst.EXCHANGES_LIQUIDNET_SQL, MQConst.ROUTING_KEY_SQL,
                SqlMapping.get("adam_addresses.remove", paramList.toArray()));

        redisUtil.hdel(AdamRedisConst.INFO_ADDRESSES.concat(uid), addressesId);
    }
}
