package com.fs.common.utils;

import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson2.JSON;
import com.fs.common.config.SmsProperties;
import com.fs.common.core.domain.SmsResponse;
import com.fs.common.core.redis.RedisCache;
import com.fs.common.exception.ServiceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;

/**
 * 短信验证码工具类
 *
 * @author your-name
 */
@Component
public class SmsVerifyCodeUtils {

    private static final Logger log = LoggerFactory.getLogger(SmsVerifyCodeUtils.class);

    /** 验证码缓存前缀 */
    private static final String SMS_CODE_KEY = "sms:verify:code:";

    /** 发送间隔缓存前缀 */
    private static final String SMS_INTERVAL_KEY = "sms:verify:interval:";

    /** 手机号正则 */
    private static final Pattern PHONE_PATTERN = Pattern.compile("^1[3-9]\\d{9}$");

    @Autowired
    private SmsProperties smsProperties;

    @Autowired
    private RedisCache redisCache;

    /**
     * 发送短信验证码
     *
     * @param phone 手机号
     * @return 发送结果
     */
    public SmsResponse sendVerifyCode(String phone) {
        validatePhone(phone);

        checkSendInterval(phone);

        String code = generateCode(smsProperties.getCodeLength());

        String content = buildContent(code);

        SmsResponse response = doSend(phone, content);

        if (response.isSuccess()) {
            cacheVerifyCode(phone, code);
            setSendInterval(phone);
            log.info("短信验证码发送成功，手机号：{}，验证码：{}", phone, code);
        } else {
            log.error("短信验证码发送失败，手机号：{}，响应：{}", phone, response);
        }
        response.setCode(code);

        return response;
    }

    /**
     * 校验验证码
     *
     * @param phone 手机号
     * @param code  验证码
     * @return true-校验通过，false-校验失败
     */
    public boolean verifyCode(String phone, String code) {
        if (StringUtils.isEmpty(phone) || StringUtils.isEmpty(code)) {
            return false;
        }

        String cacheKey = SMS_CODE_KEY + phone;
        String cachedCode = redisCache.getCacheObject(cacheKey);

        if (StringUtils.isEmpty(cachedCode)) {
            log.warn("验证码已过期或不存在，手机号：{}", phone);
            return false;
        }

        boolean matched = code.equals(cachedCode);
        if (matched) {
            // 验证成功后删除缓存，防止重复使用
            redisCache.deleteObject(cacheKey);
            log.info("验证码校验成功，手机号：{}", phone);
        } else {
            log.warn("验证码校验失败，手机号：{}，输入：{}，正确：{}", phone, code, cachedCode);
        }

        return matched;
    }

    /**
     * 校验验证码（带异常抛出）
     *
     * @param phone 手机号
     * @param code  验证码
     */
    public void verifyCodeWithException(String phone, String code) {
        if (!verifyCode(phone, code)) {
            throw new ServiceException("验证码错误或已过期");
        }
    }

    /**
     * 生成随机验证码
     *
     * @param length 验证码长度
     * @return 验证码
     */
    public String generateCode(int length) {
        Random random = new Random();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < length; i++) {
            sb.append(random.nextInt(10));
        }
        return sb.toString();
    }

    /**
     * 获取剩余有效时间（秒）
     *
     * @param phone 手机号
     * @return 剩余秒数，-1表示不存在
     */
    public long getCodeExpireSeconds(String phone) {
        String cacheKey = SMS_CODE_KEY + phone;
        Long expire = redisCache.getExpire(cacheKey);
        return expire != null ? expire : -1;
    }

    /**
     * 获取距离下次可发送的剩余秒数
     *
     * @param phone 手机号
     * @return 剩余秒数，0表示可以发送
     */
    public long getSendIntervalRemain(String phone) {
        String intervalKey = SMS_INTERVAL_KEY + phone;
        Long expire = redisCache.getExpire(intervalKey);
        return expire != null && expire > 0 ? expire : 0;
    }

    // ==================== 私有方法 ====================

    /**
     * 校验手机号格式
     */
    private void validatePhone(String phone) {
        if (StringUtils.isEmpty(phone)) {
            throw new ServiceException("手机号不能为空");
        }
        if (!PHONE_PATTERN.matcher(phone).matches()) {
            throw new ServiceException("手机号格式不正确");
        }
    }

    /**
     * 检查发送频率
     */
    private void checkSendInterval(String phone) {
        String intervalKey = SMS_INTERVAL_KEY + phone;
        if (redisCache.hasKey(intervalKey)) {
            Long remainSeconds = redisCache.getExpire(intervalKey);
            throw new ServiceException("发送过于频繁，请" + remainSeconds + "秒后再试");
        }
    }

    /**
     * 构建短信内容
     */
    private String buildContent(String code) {
        String template = smsProperties.getTemplate();
        String content = template
                .replace("{code}", code)
                .replace("{expire}", String.valueOf(smsProperties.getExpireMinutes()));
        // 拼接签名
        return smsProperties.getSign() + content;
    }

    /**
     * 执行发送请求
     */
    private SmsResponse doSend(String phone, String content) {
        Map<String, Object> params = new HashMap<>(8);
        params.put("action", "send");
        params.put("account", smsProperties.getAccount());
        params.put("password", smsProperties.getPassword());
        params.put("mobile", phone);
        params.put("content", content);
        params.put("extno", smsProperties.getExtno());
        params.put("rt", "json");

        try {
            log.info("发送短信请求，手机号：{}，内容：{}", phone, content);
            String result = HttpUtil.post(smsProperties.getUrl(), params);
            log.info("短信网关响应：{}", result);
            return JSON.parseObject(result, SmsResponse.class);
        } catch (Exception e) {
            log.error("短信发送异常", e);
            throw new ServiceException("短信发送失败，请稍后重试");
        }
    }

    /**
     * 缓存验证码
     */
    private void cacheVerifyCode(String phone, String code) {
        String cacheKey = SMS_CODE_KEY + phone;
        redisCache.setCacheObject(cacheKey, code, smsProperties.getExpireMinutes(), TimeUnit.MINUTES);
    }

    /**
     * 设置发送间隔标记
     */
    private void setSendInterval(String phone) {
        String intervalKey = SMS_INTERVAL_KEY + phone;
        redisCache.setCacheObject(intervalKey, "1", smsProperties.getIntervalSeconds(), TimeUnit.SECONDS);
    }
}
