package cn.quantgroup.xyqb.controller.external.lock;

import cn.quantgroup.xyqb.Constants;
import cn.quantgroup.xyqb.controller.IBaseController;
import cn.quantgroup.xyqb.model.JsonResult;
import cn.quantgroup.xyqb.util.ValidationUtil;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * 锁控制器
 *
 * @author renwc
 * @version 1.0.0
 * @since 2017-11-25
 */
@Slf4j
@RestController
@RequestMapping("/lock")
public class LockIpv4Controller implements IBaseController {
    private static final String WORD = "Are you a robot monkey？（^_^）";
    @Autowired
    @Qualifier("stringRedisTemplate")
    private RedisTemplate<String, String> redisTemplate;

    /**
     * 获取操作密令
     *
     * @param act     - 操作标记，true-锁定/添加，false-解锁/删除
     * @param request
     * @return
     * @header lock_ipv4 - 获取密令
     */
    @RequestMapping("/key")
    public JsonResult key(@RequestParam(required = false) String act, HttpServletRequest request) {
        //系统环境
        String jvmTest = Boolean.valueOf(System.getProperty("test")).toString();
        if (Objects.equals(Boolean.TRUE.toString(), act) || Objects.equals(Boolean.FALSE.toString(), act)) {
            // 操作标记
            boolean lock = Objects.equals(Boolean.TRUE.toString(), act);
            String header_key = request.getHeader(Constants.IPV4_LOCK.replace(":", ""));
            if (Objects.equals(Constants.CLEAR_LOCK_FOR_IPV4, header_key)) {
                String md5Key = ValidationUtil.getMd5Key(lock);
                return JsonResult.buildErrorStateResult(WORD.concat(jvmTest), md5Key);
            }
        }
        return JsonResult.buildErrorStateResult(WORD.concat(jvmTest), null);
    }

    /**
     * 锁定/解锁特定IP
     *
     * @param ip      - 目标IP
     * @param key     - 密令
     * @param act     - 操作标记，true-锁定，false-解锁
     * @param request
     * @return
     */
    @RequestMapping("/lock_ipv4")
    public JsonResult lockIpv4(@RequestParam() String ip,
                               @RequestParam() String key,
                               @RequestParam(required = false) String act,
                               HttpServletRequest request) {
        if (!ValidationUtil.validateIpv4(ip) || StringUtils.isBlank(act) || StringUtils.isBlank(key)) {
            log.info("Lock_ipv4: fail to clear_or_lock ip:{}", ip);
            return JsonResult.buildErrorStateResult(WORD, null);
        }
        if (Objects.equals(Boolean.TRUE.toString(), act) || Objects.equals(Boolean.FALSE.toString(), act)) {
            // 操作标记
            boolean lock = Objects.equals(Boolean.TRUE.toString(), act);
            boolean valid = ValidationUtil.isValid(key, lock);
            if (valid) {
                lockIpv4(ip, lock);
                return JsonResult.buildSuccessResult("Success", null);
            }
        }
        log.info("Lock_ipv4: fail to clear_or_lock ip:{}", ip);
        return JsonResult.buildErrorStateResult(WORD, null);
    }

    /**
     * 锁定/解锁特定IP
     *
     * @param ip   - 目标IP
     * @param lock - 操作标记，true-锁定，false-解锁
     * @header lock_ipv4 - 获取密令
     */
    private void lockIpv4(String ip, boolean lock) {
        String lockIpv4Key = getLockIpv4Key(ip);
        if (lock) {
            // 每分钟计数阈值
            long counts = Constants.IPV4_LOCK_ON_FAILED_COUNTS;
            String redisCounts = redisTemplate.opsForValue().get(Constants.IPV4_LOCK_ON_COUNTS_REDIS);
            if (StringUtils.isNumeric(redisCounts) && Integer.valueOf(redisCounts) > 0) {
                counts = Integer.valueOf(redisCounts);
            }
            // 锁定时长
            long minutes = Constants.IPV4_FAILED_LOCK_MINUTES;
            String redisMinutes = redisTemplate.opsForValue().get(Constants.IPV4_LOCK_ON_COUNTS_REDIS);
            if (StringUtils.isNumeric(redisMinutes) && Integer.valueOf(redisMinutes) > 0) {
                minutes = Integer.valueOf(redisMinutes);
            }
            redisTemplate.opsForValue().set(lockIpv4Key, Boolean.TRUE.toString(), minutes, TimeUnit.MINUTES);
            log.info("Lock_ipv4: locked ip Success. ip:{}, error overstep {} times in {} minutes, do lock {} minutes", ip, counts, Constants.IPV4_FAILED_COUNT_MINUTES, minutes);
        } else {
            redisTemplate.delete(lockIpv4Key);
            log.info("Lock_ipv4: unlocked ip Success. ip:{}", ip);
        }
    }

    /**
     * 配置特定IP到黑/白名单 - Redis
     *
     * @param ip      - 目标IP
     * @param key     - 密令
     * @param act     - 操作：true-添加，false-删除
     * @param type    - 名单类型：true-黑名单，false-白名单
     * @param request
     * @return
     */
    @RequestMapping("/configHitList")
    public JsonResult configHitList(@RequestParam() String ip,
                                    @RequestParam() String key,
                                    @RequestParam(required = false) String act,
                                    @RequestParam(required = false) String type,
                                    HttpServletRequest request) {
        if (!ValidationUtil.validateIpv4(ip) || StringUtils.isBlank(key) || StringUtils.isBlank(act) || StringUtils.isBlank(type)) {
            log.info("Lock_ipv4: fail to config hit list for ip:{}", ip);
            return JsonResult.buildErrorStateResult(WORD, null);
        }
        boolean actOk = Objects.equals(Boolean.TRUE.toString(), act) || Objects.equals(Boolean.FALSE.toString(), act);
        boolean typeOk = Objects.equals(Boolean.TRUE.toString(), type) || Objects.equals(Boolean.FALSE.toString(), type);
        // 操作标记
        boolean valid = (actOk && typeOk) && ValidationUtil.isValid(key, Objects.equals(Boolean.TRUE.toString(), act));
        if (valid) {
            boolean operate = Objects.equals(Boolean.TRUE.toString(), act);
            boolean lock = Objects.equals(Boolean.TRUE.toString(), type);
            configHitList(ip, operate, lock);
            return JsonResult.buildSuccessResult("Success", null);
        }
        log.info("Lock_ipv4: fail to config hit list for ip:{}", ip);
        return JsonResult.buildErrorStateResult(WORD, null);
    }

    /**
     * 配置特定IP到黑/白名单 - Redis
     *
     * @param ip      - 目标IP
     * @param operate - 操作：true-添加，false-删除
     * @param lock    - 名单类型：true-黑名单，false-白名单
     */
    private void configHitList(String ip, boolean operate, boolean lock) {
        if (!ValidationUtil.validateIpv4(ip)) {
            return;
        }
        if (operate) {
            if (lock) {
                redisTemplate.opsForSet().add(Constants.IPV4_LOCK_BLACK, ip);
                log.info("Lock_ipv4: add black-list item Success, ip:{}", ip);
            } else {
                redisTemplate.opsForSet().add(Constants.IPV4_LOCK_WHITE, ip);
                log.info("Lock_ipv4: add white-list item Success, ip:{}", ip);
            }
        } else {
            if (lock) {
                redisTemplate.opsForSet().remove(Constants.IPV4_LOCK_BLACK, ip);
                log.info("Lock_ipv4: remove black-list item Success, ip:{}", ip);
            } else {
                redisTemplate.opsForSet().remove(Constants.IPV4_LOCK_WHITE, ip);
                log.info("Lock_ipv4: remove white-list item Success, ip:{}", ip);
            }
        }
        log.info("Lock_ipv4: white-list:{},black-list:{}", JSONObject.toJSON(redisTemplate.opsForSet().members(Constants.IPV4_LOCK_WHITE)), JSONObject.toJSON(redisTemplate.opsForSet().members(Constants.IPV4_LOCK_BLACK)));
    }

    /**
     * 设定可选阈值 - Redis
     *
     * @param key     - 密令
     * @param act     - 操作：true-添加，false-删除
     * @param counts  - 每分钟计数阈值
     * @param minutes - 锁定时长
     * @param request
     * @return
     */
    @RequestMapping("/configNoun")
    public JsonResult configNoun(@RequestParam() String key,
                                 @RequestParam(required = false) String act,
                                 @RequestParam(required = false) String counts,
                                 @RequestParam(required = false) String minutes,
                                 HttpServletRequest request) {
        if (StringUtils.isBlank(key) || StringUtils.isBlank(act) || !StringUtils.isNumeric(counts) || !StringUtils.isNumeric(minutes)) {
            log.info("Lock_ipv4: fail to config noun");
            return JsonResult.buildErrorStateResult(WORD, null);
        }
        boolean actOk = Objects.equals(Boolean.TRUE.toString(), act) || Objects.equals(Boolean.FALSE.toString(), act);
        boolean valid = actOk && ValidationUtil.isValid(key, Objects.equals(Boolean.TRUE.toString(), act));
        if (valid) {
            // 操作标记
            boolean operate = Objects.equals(Boolean.TRUE.toString(), act);
            // 每分钟计数阈值
            int redisCounts = Integer.valueOf(counts);
            // 锁定时长
            int redisMinutes = Integer.valueOf(minutes);
            if (redisCounts > 0 && redisMinutes > 0) {
                configNoun(redisCounts, redisMinutes, operate);
                return JsonResult.buildSuccessResult("Success", null);
            }
        }
        log.info("Lock_ipv4: fail to config noun");
        return JsonResult.buildErrorStateResult(WORD, null);
    }

    /**
     * 设定可选阈值 - Redis
     *
     * @param counts  - 每分钟计数阈值(? > 0)
     * @param minutes - 锁定时长(? > 0)
     * @param operate - 操作：true-添加，false-删除
     */
    private void configNoun(int counts, int minutes, boolean operate) {
        if (operate) {
            if (counts > 0) {
                redisTemplate.opsForValue().set(Constants.IPV4_LOCK_ON_COUNTS_REDIS, String.valueOf(counts));
                log.info("Lock_ipv4: config redis-param counts Success, counts:{}", counts);
            }
            if (minutes > 0) {
                redisTemplate.opsForValue().set(Constants.IPV4_LOCK_MINUTES_REDIS, String.valueOf(minutes));
                log.info("Lock_ipv4: config redis-param minutes Success, minutes:{}", minutes);
            }
        } else {
            redisTemplate.delete(Constants.IPV4_LOCK_ON_COUNTS_REDIS);
            redisTemplate.delete(Constants.IPV4_LOCK_MINUTES_REDIS);
            log.info("Lock_ipv4: remove redis-param counts、minutes Success, counts:{},minutes:{}, current default:[counts:{},minutes:{}]", Constants.IPV4_LOCK_ON_FAILED_COUNTS, Constants.IPV4_FAILED_LOCK_MINUTES);
        }
    }

    /**
     * 获取Redis-key
     *
     * @param ipv4
     * @return Redis-key
     */
    private static String getLockIpv4Key(String ipv4) {
        return Constants.IPV4_LOCK + ipv4;
    }

}
