package com.crm.rely.backend.service.impl;

import com.crm.rely.backend.configuration.LocaleMessage;
import com.crm.rely.backend.core.constant.Constants;
import com.crm.rely.backend.core.entity.system.sms.BlackListEntity;
import com.crm.rely.backend.core.entity.system.sms.SmsValidatorEntity;
import com.crm.rely.backend.exception.ServiceException;
import com.crm.rely.backend.service.BlackListService;
import com.crm.rely.backend.service.RedisService;
import com.crm.rely.backend.util.DateUtil;
import com.google.common.base.Strings;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.Date;

public class BlackListServiceImpl implements BlackListService {

    @Autowired
    protected RedisService redisService;
    /**
     * 黑名单redis保存时key前缀
     */
    protected String KEY_PREFIX;
    protected String KEY_PREFIX_BLACK;
    @Autowired
    private LocaleMessage localeMessage;
    /**
     * 每次发送时间间隔
     */
    private Long SINGLE_SEND_RANGE = 30 * 1000L;
    /**
     * 多次发送时间间隔
     */
    private Long MULTI_SEND_RANGE = 3 * 60 * 1000L;
    /**
     * 多次发送次数
     */
    private Integer MULTI_SEND_TIMES = 3;
    /**
     * 黑名单重试次数
     */
    private Integer BLACK_LIST_RETRY_TIMES = 3;
    /**
     * 黑名单持续时间（小时）
     */
    private Integer FORBID_HOURS = 1;


    public BlackListServiceImpl() {

        KEY_PREFIX = "BLACKLIST";
    }

    /**
     * @param key                 黑名单redis保存时key前缀
     * @param keyBack             黑名单redis保存时key前缀
     * @param singleSendRange     每次发送时间间隔
     * @param multiSendRange      多次发送时间间隔
     * @param multiSendTimes      多次发送次数
     * @param blackListRetryTimes 黑名单重试次数
     * @param ForbidHours         黑名单持续时间（小时）
     */
    public BlackListServiceImpl(String key, String keyBack, Long singleSendRange, Long multiSendRange,
                                Integer multiSendTimes, Integer blackListRetryTimes, Integer ForbidHours) {

        KEY_PREFIX = key;
        KEY_PREFIX_BLACK = keyBack;
        if (singleSendRange != null) {
            SINGLE_SEND_RANGE = singleSendRange;
        }
        if (multiSendRange != null) {
            MULTI_SEND_RANGE = multiSendRange;
        }
        if (multiSendTimes != null) {
            MULTI_SEND_TIMES = multiSendTimes;
        }
        if (blackListRetryTimes != null) {
            BLACK_LIST_RETRY_TIMES = blackListRetryTimes;
        }
        if (ForbidHours != null) {
            FORBID_HOURS = ForbidHours;
        }
    }

    @Override
    public void validate(String content, String type) throws ServiceException {

        validate(content, type, "127.0.0.1");
    }

    /**
     * 是否需要加入黑名单
     *
     * @param content 邮箱
     * @throws ServiceException
     */
    @Override
    public void validate(String content, String type, String ip) throws ServiceException {

        String key = KEY_PREFIX + content;

        if (Strings.isNullOrEmpty(type)) {
            type = KEY_PREFIX_BLACK;
        }
        String typeKey = type + key;
        /*
            过滤黑名单
         */
        checkFromList(content, System.currentTimeMillis());

        SmsValidatorEntity smsValidatorEntity = redisService.getObject(typeKey);

        /*
            根据类型获取记录信息
         */
        if (smsValidatorEntity == null) {
            smsValidatorEntity = new SmsValidatorEntity();
            smsValidatorEntity.setCount(1);
            smsValidatorEntity.setTime(System.currentTimeMillis());
            smsValidatorEntity.setTries(0);
            redisService.saveObject(typeKey, smsValidatorEntity, MULTI_SEND_RANGE);
            removeFromList(content);
            return;
        }

        //添加重试次数
        smsValidatorEntity.setTries(smsValidatorEntity.getTries() + 1);
        redisService.saveObject(typeKey, smsValidatorEntity, MULTI_SEND_RANGE);
        /*
            判断时间,1分钟之内不可发送多次
         */
        Long timeRange = System.currentTimeMillis() - smsValidatorEntity.getTime();
        if (timeRange < SINGLE_SEND_RANGE) {

            /*
                如果重试次数超过三次，则视为盗刷短信行为，入黑名单1天
             */
            if (smsValidatorEntity.getTries() >= BLACK_LIST_RETRY_TIMES) {
                //加入黑名单
                addToList(content, ip, new Date());
            }
//            throw new ServiceException(String.format(localeMessage.getMessage("please_try_again_in_seconds"),
//                    (SINGLE_SEND_RANGE - timeRange) / 1000));
        }

        /*
            判断次数
         */
        Integer count = smsValidatorEntity.getCount();
        if (timeRange < MULTI_SEND_RANGE) {
            if (count >= MULTI_SEND_TIMES) {
                Long minutes = (MULTI_SEND_RANGE / 60) / 1000;
                addToList(content, ip, new Date());
                throw new ServiceException(localeMessage.getMessage("frequent_requests"));
            } else {
                smsValidatorEntity.setCount(smsValidatorEntity.getCount() + 1);
                smsValidatorEntity.setTries(0);
                redisService.saveObject(typeKey, smsValidatorEntity, MULTI_SEND_RANGE);
                //有成功发送则移除黑名单
                removeFromList(content);
            }
        } else {
            smsValidatorEntity.setCount(1);
            smsValidatorEntity.setTime(System.currentTimeMillis());
            smsValidatorEntity.setTries(0);
            redisService.saveObject(typeKey, smsValidatorEntity, MULTI_SEND_RANGE);
            //有成功发送则移除黑名单
            removeFromList(content);
        }

    }

    @Override
    public boolean checkSeal(String content) throws ServiceException {
        String key = KEY_PREFIX_BLACK + content;
        BlackListEntity blackListEntity = redisService.getEntity(key, BlackListEntity.class);
        if (blackListEntity != null && blackListEntity.getEndTime().getTime() > System.currentTimeMillis()) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 根据黑名单验证
     *
     * @param content 黑名单内容
     * @param now     当前发送时间
     * @throws ServiceException
     */
    @Override
    public void checkFromList(String content, Long now) throws ServiceException {

        if (checkSeal(content)) {
            throw new ServiceException(Constants.RESTRICT_REQUEST);
        } else {
            removeFromList(content);
        }
    }

    /**
     * 移除黑名单
     *
     * @param content 黑名单内容
     * @throws ServiceException
     */
    @Override
    public void removeFromList(String content) throws ServiceException {
        removeFromList(content, null);
    }

    @Override
    public void removeFromList(String content, String type) throws ServiceException {
        String key = KEY_PREFIX_BLACK + content;
        redisService.remove(key);

        if (Strings.isNullOrEmpty(type)) {
            type = KEY_PREFIX_BLACK;
        }
        String typeKey = type + KEY_PREFIX + content;

        redisService.remove(typeKey);
    }

    /**
     * 添加至黑名单
     *
     * @param content 黑名单内容
     * @param ip      ip
     * @param nowDate
     * @throws ServiceException
     */
    @Override
    public void addToList(String content, String ip, Date nowDate) throws ServiceException {
        BlackListEntity blackListEntity = new BlackListEntity();
        blackListEntity.setIp(ip);
        blackListEntity.setContent(content);
        blackListEntity.setValid(1);
        blackListEntity.setStartTime(nowDate);
        Date endTime = DateUtil.operationHour(blackListEntity.getStartTime(), FORBID_HOURS);
        blackListEntity.setEndTime(endTime);

        String key = KEY_PREFIX_BLACK + content;
        //添加到redis中 设置过期时间为FORBID_HOURS * 60 * 60 * 1000L
        redisService.save(key, blackListEntity, FORBID_HOURS * 60 * 60 * 1000L);
    }
}
