package com.diagbot.service.impl;

import com.diagbot.entity.ImgVerInfo;
import com.diagbot.exception.CommonException;
import com.diagbot.exception.CommonErrorCode;
import com.diagbot.service.ImgVerService;
import com.diagbot.util.DateUtil;
import com.diagbot.util.StringUtil;
import com.diagbot.util.VerifyCodeUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Date;

/**
 * @Description: 图片验证接口实现
 * @author: gaodm
 * @time: 2018/9/4 16:25
 */
@Service
@Slf4j
public class ImgVerServiceImpl implements ImgVerService {
    @Autowired
    @Qualifier("redisTemplateForImg")
    private RedisTemplate redisForImg;

    private byte[] serializeKey(Object o) {
        return redisForImg.getKeySerializer().serialize(o);
    }

    private byte[] serializeValue(Object o) {
        return redisForImg.getValueSerializer().serialize(o);
    }

    private Object deserializeValue(byte[] b) {
        return redisForImg.getValueSerializer().deserialize(b);
    }

    private byte[] getUserImgKey(String imgId) {
        String userImgFormat = "user_img_%s";
        return serializeKey(String.format(userImgFormat, imgId));
    }


    /**
     * 创建用户图片验证信息
     * @param imgVerInfo 图片验证码验证信息
     * @return 图片验证码数据流
     */
    @Override
    public byte[] createImgVerification(ImgVerInfo imgVerInfo){
        if (null == imgVerInfo){
            throw new CommonException(CommonErrorCode.PARAM_IS_NULL,
                    "用户图片验证信息不能为空!");
        }
        if (StringUtil.isBlank(imgVerInfo.getImgId())){
            throw new CommonException(CommonErrorCode.PARAM_IS_NULL,
                    "图片验证码唯一标志不能为空!");
        }
        //生成图片验证码
        Object[] obj = VerifyCodeUtils.createImage();
        imgVerInfo.setCode(obj[0].toString());
        imgVerInfo.setCreateTime(DateUtil.now());
        final Date expireDate = DateUtil.addMinutes(DateUtil.now(), 20);
        imgVerInfo.setExpireTime(expireDate);
        final byte[] redis_key = getUserImgKey(imgVerInfo.getImgId());
        redisForImg.execute(new RedisCallback<Object>() {
            @Override
            public Object doInRedis(RedisConnection connection) throws DataAccessException {
                //获取旧的
                byte[] bytes = connection.get(redis_key);
                //删除旧的
                if (bytes != null) {
                    connection.del(bytes);
                }
                //设置新的
                connection.setEx(
                        redis_key,
                        (expireDate.getTime() - DateUtil.now().getTime()) / 1000,
                        serializeValue(imgVerInfo)
                );
                return true;
            }
        });
        return (byte[]) obj[1];
    }

    /**
     * 验证短信验证码是否有效
     */
    @Override
    public Boolean verifyImgVerification(ImgVerInfo imgVerInfo){
        if (null == imgVerInfo){
            throw new CommonException(CommonErrorCode.PARAM_IS_NULL,
                    "用户图片验证信息不能为空!");
        }
        if (StringUtil.isBlank(imgVerInfo.getImgId())){
            throw new CommonException(CommonErrorCode.PARAM_IS_NULL,
                    "图片验证码唯一标志不能为空!");
        }
        if (StringUtil.isBlank(imgVerInfo.getCode())){
            throw new CommonException(CommonErrorCode.PARAM_IS_NULL,
                    "验证码不能为空!");
        }
        //从redis中取出
        final byte[] redis_key = getUserImgKey(imgVerInfo.getImgId());
        ImgVerInfo imgVerInfoRes = (ImgVerInfo) redisForImg.execute(new RedisCallback<ImgVerInfo>() {
            @Override
            public ImgVerInfo doInRedis(RedisConnection connection) throws DataAccessException {
                byte[] bytes = connection.get(redis_key);
                if (bytes == null) {
                    return null;
                }
                return (ImgVerInfo) deserializeValue(bytes);
            }
        });
        if (imgVerInfoRes == null) {
            log.info("用户图片验证信息不存在【imgId=" + imgVerInfo.getImgId() + "】!");
            throw new CommonException(CommonErrorCode.NOT_EXISTS,
                    "用户图片验证信息不存在【imgId=" + imgVerInfo.getImgId() + "】!");
        }
        //判断是否是想要的对象
        if (!imgVerInfoRes.getImgId().equals(imgVerInfo.getImgId())) {
            log.info("图片验证码唯一标志不一致!图片验证码唯一标志：{}", imgVerInfo.getImgId());
            throw new CommonException(CommonErrorCode.PARAM_IS_ERROR,
                    "图片验证码唯一标志不一致!");
        }
        if (!(imgVerInfoRes.getCode()).equals(imgVerInfo.getCode().toUpperCase())) {
            log.info("输入图形验证码有误!验证码：{}", imgVerInfo.getCode());
            throw new CommonException(CommonErrorCode.PARAM_IS_ERROR,
                    "输入图形验证码有误!");
        }
        //判断是否过期
        if (!DateUtil.after(imgVerInfoRes.getExpireTime(), DateUtil.now())) {
            log.info("图片验证码已过期，请重新获取!过期时间：{}", imgVerInfoRes.getExpireTime());
            throw new CommonException(CommonErrorCode.PARAM_IS_ERROR,
                    "图片验证码已过期，请重新获取!");
        }
        return true;
    }

    /**
     * 获取用户短信验证码信息
     * @param imgId
     * @return
     */
    @Override
    public ImgVerInfo getImgVerification(String imgId){
        return (ImgVerInfo) redisForImg.execute(new RedisCallback<Object>() {
            @Override
            public Object doInRedis(RedisConnection connection) throws DataAccessException {
                byte[] redis_key = getUserImgKey(imgId);
                byte[] bytes = connection.get(redis_key);
                if (bytes == null) {
                    return null;
                }
                return deserializeValue(bytes);
            }
        });
    }

    /**
     * 删除用户短信验证码信息
     */
    @Override
    public Boolean deleteImgVerification(String imgId){
        final byte[] redis_key = getUserImgKey(imgId);
        Long l = (Long) redisForImg.execute(new RedisCallback<Long>() {
            @Override
            public Long doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.del(redis_key);
            }
        });
        return l > 0;
    }
}
