package com.testor.module.sys.service.impl;

import cn.hutool.core.io.FastByteArrayOutputStream;
import cn.hutool.core.lang.UUID;
import com.google.code.kaptcha.Producer;
import com.testor.module.sys.service.AccountLockService;
import com.tongtech.tfw.backend.common.models.exceptions.ServiceException;
import com.tongtech.tfw.backend.common.models.response.ResponseInfo;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author rsq
 * @program zlmy_boot
 * @return
 **/
@Service
public class AccountLockServiceImpl implements AccountLockService {

    private static final String REDIS_ATTEMPTS_KEY_PREFIX = "account_attempts:";
    private static final String REDIS_LOCKED_KEY_PREFIX = "account_locked:";
    private static final int MAX_ATTEMPTS_1 = 3; // 连续错误3次，锁定15分钟
    private static final int MAX_ATTEMPTS_2 = 5; // 连续错误5次，锁定30分钟

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private Producer kaptchaProduer;

    @Override
    public int getAttempts(String accountOrIp) {
        String key = getAttemptsKey(accountOrIp);
        String attempts = redisTemplate.opsForValue().get(key);
        return attempts == null ? 0 : Integer.parseInt(attempts);
    }

    @Override
    public void increaseAttempts(String accountOrIp) {
        String key = getAttemptsKey(accountOrIp);
        String count = redisTemplate.opsForValue().get(key);
        Integer attempts = Integer.parseInt(StringUtils.isNotBlank(count) ? count : "0");
        if (attempts == 0) {
            attempts = 1;
        } else {
            attempts++;
            if (attempts == MAX_ATTEMPTS_1) {
                lock(accountOrIp, 15);
            } else if (attempts == MAX_ATTEMPTS_2) {
                lock(accountOrIp, 30);
            }
        }
        redisTemplate.opsForValue().set(key, attempts.toString(), 30, TimeUnit.MINUTES); // 过期时间为30分钟
    }

    @Override
    public void resetAttempts(String accountOrIp) {
        String key = getAttemptsKey(accountOrIp);
        redisTemplate.delete(key);
    }

    @Override
    public void resetLocked(String accountOrIp) {
        String key = getLockedKey(accountOrIp);
        redisTemplate.delete(key);
    }

    @Override
    public boolean isLocked(String accountOrIp) {
        String key = getLockedKey(accountOrIp);
        return redisTemplate.hasKey(key);
    }

    @Override
    public void lock(String accountOrIp, int duration) {
        String key = getLockedKey(accountOrIp);
        redisTemplate.opsForValue().set(key, "locked", duration, TimeUnit.MINUTES);
    }

    private String getAttemptsKey(String accountOrIp) {
        return REDIS_ATTEMPTS_KEY_PREFIX + accountOrIp;
    }

    private String getLockedKey(String accountOrIp) {
        return REDIS_LOCKED_KEY_PREFIX + accountOrIp;
    }

    @Override
    public Map<String, Object> getKaptcha(HttpServletResponse response, HttpSession session) {
        String imagecode = kaptchaProduer.createText();
        // 生成图片
        BufferedImage image = kaptchaProduer.createImage(imagecode);
        // 将验证码存入Session
        session.setAttribute("kaptcha", imagecode);
        //将图片输出给浏览器
        String uuid = UUID.randomUUID().toString();//uuid-->验证码唯一标识
        FastByteArrayOutputStream os = new FastByteArrayOutputStream();
        try {

            ImageIO.write(image, "png", os);

            //验证码实现redis缓存，过期时间2分钟

            session.setAttribute("uuid", imagecode);

            redisTemplate.opsForValue().set(uuid, imagecode, 2, TimeUnit.MINUTES);

        } catch (IOException e) {
            throw new ServiceException(new ResponseInfo(500, e.getMessage()));
        }

        Map<String, Object> map = new HashMap();
        map.put("uuid", uuid);
        map.put("img", Base64.getEncoder().encodeToString(os.toByteArray()));
        return map;
    }

    @Override
    public void checkDefaultKaptcha(String uuid, String text) {
        String captcha = redisTemplate.opsForValue().get(uuid);
        if (captcha == null) {
            throw new ServiceException(new ResponseInfo(500, "验证码失效"));
        }
        int compare = StringUtils.compare(captcha, text);
        if (compare != 0) {
            throw new ServiceException(new ResponseInfo(500, "验证码不正确"));
        }
        //删除验证码
        redisTemplate.delete(uuid);
    }
}
