package com.huiju.estateagents.common.smsService;

import com.alibaba.fastjson.JSONObject;
import lombok.Data;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.util.Random;

@Slf4j
@Component
@EnableScheduling
public class Captcha {
    @Value("${sms-service.captcha.code}")
    String code;

    @Value("${sms-service.captcha.product}")
    String product;

    @Value("${sms-service.captcha.sign}")
    String sign;

    @Autowired
    Service service;

    // 默认超时时间, 单位秒
    Integer expireSec = 5 * 60;

    // 缓存电话列表
    Hashtable<String, Phone> allPhones = new Hashtable<>();

    /**
     * 发送验证码
     * @param tel
     * @param captcha
     * @throws Exception
     */
    public void send(String tel, String captcha) throws Exception {
        if (null == captcha) {
            throw new Exception("没有有效的验证码");
        }

        Map<String, String> content = new HashMap<>();
        content.put("code", captcha);
        content.put("product", product);

        Phone phone = new Phone()
                .setNumber(tel)
                .setCaptcha(captcha)
                .setExpire(expireTime());

        Service.Message message = new Service.Message()
                .setCode(code)
                .setSign(sign)
                .setTel(tel)
                .setContent(JSONObject.toJSONString(content));

        try {
            service.sendMessage(message);
        } catch (Exception e) {
            // 修改友好点的错误
            throw new Exception("发送验证码失败");
        }

        log.info(String.format("发送验证码 %s 到手机 %s", captcha, tel));

        // 发送成功, 写入缓存
        toCache(phone);
    }

    /**
     * 校验验证码
     * @param tel
     * @param captcha
     * @return
     */
    public boolean validate(String tel, String captcha) {
        String universalCode = getUniversalCode();
        if (universalCode.equals(captcha)) {
            return true;
        }

        Phone phone = fromCache(tel);
        if (null == phone) {
            return false;
        }

        if (null == captcha || !captcha.equals(phone.getCaptcha())) {
            return false;
        }

        // 过期则清除缓存
        boolean res = checkExpire(phone);
        if (res) {
            clearCache(tel);
        }

        return !res;
    }

    private String getUniversalCode() {
        LocalDateTime now = LocalDateTime.now();
        int month = now.getMonthValue();
        String monthStr = month > 9 ? String.valueOf(month) : "0" + String.valueOf(month);

        return String.valueOf(now.getYear()) + monthStr;
    }

    /**
     * 辅助方法: 随机生成验证码
     * @param length
     * @return
     */
    public String randCaptcha(int length) {
        Random random = new Random();
        String res = "";
        for (int i = 0; i < length; i ++) {
            int num = random.nextInt(10);
            res += String.valueOf(num);
        }
        return res;
    }

    /**
     * 定时任务 每半小时 清除一次不用的手机号
     */
    @Scheduled(fixedRate = 1000 * 60 * 30)
    public void clearUnused() {
        for (Phone phone: allPhones.values()) {
            if (checkExpire(phone)) {
                clearCache(phone.getNumber());
            }
        }
    }

    private Phone fromCache(String phone) {
        return allPhones.get(phone);
    }
    private void toCache(Phone phone) {
        allPhones.put(phone.getNumber(), phone);
    }
    private void clearCache(String key) { allPhones.remove(key); }

    private LocalDateTime expireTime() {
        return LocalDateTime.now().plusSeconds(expireSec);
    }

    // 过期返回 true
    private boolean checkExpire(Phone phone) {
        return LocalDateTime.now().isAfter(phone.getExpire());
    }

    @Data
    @Accessors(chain = true)
    public static class Phone {
        String number;
        String captcha;
        LocalDateTime expire;
    }

}
