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

import com.liquidnet.common.cache.redis.util.RedisUtil;
import com.liquidnet.common.mq.constant.MQConst;
import com.liquidnet.commons.lang.core.JwtValidator;
import com.liquidnet.commons.lang.util.BsonUtil;
import com.liquidnet.commons.lang.util.CollectionUtil;
import com.liquidnet.commons.lang.util.JsonUtils;
import com.liquidnet.service.adam.dto.vo.AdamUserInfoVo;
import com.liquidnet.service.adam.service.AdamRdmService;
import com.liquidnet.service.adam.service.IAdamUserInfoService;
import com.liquidnet.service.base.SqlMapping;
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.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.LinkedList;
import java.util.Map;

/**
 * <p>
 * 用户信息 服务实现类
 * </p>
 *
 * @author liquidnet
 * @since 2021-05-10
 */
@Slf4j
@Service
public class AdamUserInfoServiceImpl implements IAdamUserInfoService {
    @Autowired
    MongoTemplate mongoTemplate;
    @Autowired
    AdamRdmService adamRdmService;
    @Autowired
    RabbitTemplate rabbitTemplate;
    @Autowired
    RedisUtil redisUtil;
    @Autowired
    JwtValidator jwtValidator;

    @Override
//    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void edit(AdamUserInfoVo userInfoVo) {
        userInfoVo.setCreateAt(null);
        Document updateDoc = Document.parse(JsonUtils.toJson(userInfoVo));
        updateDoc.replace("updatedAt", userInfoVo.getUpdatedAt());
        long s = System.currentTimeMillis();
        UpdateResult updateResult = mongoTemplate.getCollection(AdamUserInfoVo.class.getSimpleName()).updateOne(
                Query.query(Criteria.where("uid").is(userInfoVo.getUid())).getQueryObject(),
                new Document("$set", updateDoc)
        );
        log.debug("#MDB耗时:{}ms", System.currentTimeMillis() - s);
        if (updateResult.getModifiedCount() <= 0) {
            log.warn("Invalid update MDB.AdamUserInfoVo:{}", JsonUtils.toJson(userInfoVo));
        }

        s = System.currentTimeMillis();
        adamRdmService.setUserInfoVoByUid(userInfoVo.getUid(), userInfoVo);
        log.debug("#RDS耗时:{}ms", System.currentTimeMillis() - s);

        LinkedList<String> toMqSqls = CollectionUtil.linkedListString();
        LinkedList<Object[]> updateUserObjs = CollectionUtil.linkedListObjectArr(),
                updateUserInfoObjs = CollectionUtil.linkedListObjectArr();

        s = System.currentTimeMillis();
        toMqSqls.add(SqlMapping.get("adam_user.complete"));
        updateUserObjs.add(new Object[]{1, userInfoVo.getUpdatedAt(), userInfoVo.getUid()});
        toMqSqls.add(SqlMapping.get("adam_user_info.edit"));
        updateUserInfoObjs.add(new Object[]{
                userInfoVo.getNickname(),
                JsonUtils.toJson(userInfoVo.getSex()),
                userInfoVo.getBirthday(),
                userInfoVo.getArea(),
                userInfoVo.getSignature(),
                userInfoVo.getAvatar(),
                userInfoVo.getBackground(),
                JsonUtils.toJson(userInfoVo.getTagMe()),
                userInfoVo.getUid()}
        );
        log.debug("#SQL.GET耗时:{}ms", System.currentTimeMillis() - s);

        s = System.currentTimeMillis();
        rabbitTemplate.convertAndSend(MQConst.EX_LNS_SQL_UCENTER, MQConst.RK_SQL_UCENTER,
                SqlMapping.gets(toMqSqls, updateUserObjs, updateUserInfoObjs)
        );
        log.debug("#MQ耗时:{}ms", System.currentTimeMillis() - s);
    }

    @Override
//    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public String editMobile(String uid, String mobile) {
        LocalDateTime now = LocalDateTime.now();
//        AdamUserInfoVo updateInfoVo = AdamUserInfoVo.getNew();
//        updateInfoVo.setUpdatedAt(now);
//        updateInfoVo.setMobile(mobile);
        long s = System.currentTimeMillis();
        Document doc = mongoTemplate.getCollection(AdamUserInfoVo.class.getSimpleName()).findOneAndUpdate(
                Query.query(Criteria.where("uid").is(uid)).getQueryObject(),
                new Document("$set", new Document("mobile", mobile).append("updatedAt", now)),
                new FindOneAndUpdateOptions().returnDocument(ReturnDocument.BEFORE)
        );
        log.debug("#MDB耗时:{}ms", System.currentTimeMillis() - s);
        log.debug("edit mobile - before doc:{}", JsonUtils.toJson(doc));
        AdamUserInfoVo beforeUserInfoVo = BsonUtil.toBean(doc, AdamUserInfoVo.class);

        s = System.currentTimeMillis();
        adamRdmService.delUidByMobile(beforeUserInfoVo.getMobile());
        adamRdmService.setUidByMobile(mobile, uid);
        beforeUserInfoVo.setMobile(mobile);
        beforeUserInfoVo.setUpdatedAt(now);
        adamRdmService.setUserInfoVoByUid(uid, beforeUserInfoVo);
        log.debug("#RDS耗时:{}ms", System.currentTimeMillis() - s);

        s = System.currentTimeMillis();
        rabbitTemplate.convertAndSend(MQConst.EX_LNS_SQL_UCENTER, MQConst.RK_SQL_UCENTER,
                SqlMapping.get("adam_user.edit.mobile", mobile, now, uid));
        log.debug("#MQ耗时:{}ms", System.currentTimeMillis() - s);

        return this.flushSsoProcess(beforeUserInfoVo);
    }

    private String flushSsoProcess(AdamUserInfoVo userInfoVo) {
        Map<String, Object> claimsMap = CollectionUtil.mapStringObject();
        claimsMap.put("sub", userInfoVo.getUid());
        claimsMap.put("mobile", userInfoVo.getMobile());
        claimsMap.put("nickname", userInfoVo.getNickname());
        claimsMap.put("type", "user");

        String token = jwtValidator.create(claimsMap);

        redisUtil.set(
                jwtValidator.getSsoRedisKey().concat(userInfoVo.getUid()),
                DigestUtils.md5DigestAsHex(token.getBytes(StandardCharsets.UTF_8)),
                jwtValidator.getExpireTtl() * 60
        );
        return token;
    }
}
