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.core.JwtValidator;
import com.liquidnet.commons.lang.util.BsonUtil;
import com.liquidnet.commons.lang.util.JsonUtils;
import com.liquidnet.service.adam.dto.vo.AdamUserInfoVo;
import com.liquidnet.service.adam.entity.AdamUserInfo;
import com.liquidnet.service.adam.mapper.AdamUserInfoMapper;
import com.liquidnet.service.adam.service.IAdamRdmService;
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 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.HashMap;
import java.util.Map;

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

    @Override
//    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void edit(AdamUserInfoVo userInfoVo) {
        long s = System.currentTimeMillis();
        Document doc = mongoTemplate.getCollection(AdamUserInfoVo.class.getSimpleName()).findOneAndUpdate(
                Query.query(Criteria.where("uid").is(userInfoVo.getUid())).getQueryObject(),
                new Document("$set", Document.parse(JsonUtils.toJson(userInfoVo))),
                new FindOneAndUpdateOptions().returnDocument(ReturnDocument.AFTER)
        );
        log.debug("#MDB耗时:{}ms", System.currentTimeMillis() - s);
        if (null != doc) {
            userInfoVo = BsonUtil.toBean(doc, AdamUserInfoVo.class);

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

            s = System.currentTimeMillis();
            rabbitTemplate.convertAndSend(MQConst.EX_LNS_SQL_UCENTER, MQConst.RK_SQL_UCENTER,
                    SqlMapping.get(
                            userInfoVo.getIsComplete() == 0 ? "adam_user_info.add" : "adam_user_info.edit",
                            userInfoVo.getNickname(),
                            JsonUtils.toJson(userInfoVo.getSex()),
                            userInfoVo.getBirthday(),
                            userInfoVo.getArea(),
                            userInfoVo.getSignature(),
                            userInfoVo.getAvatar(),
                            userInfoVo.getBackground(),
                            userInfoVo.getQrCode(),
                            null,// qr_pic
                            JsonUtils.toJson(userInfoVo.getTagMe()),
                            userInfoVo.getUid()
                    )
            );
            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 = new HashMap<>();
        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;
    }
}
