package com.corewing.app.service.impl;

import com.corewing.app.service.VerifyCodeService;
import com.corewing.app.util.EmailUtil;
import com.corewing.app.util.I18nUtil;
import com.corewing.app.util.RedisUtil;
import com.corewing.app.util.SmsBaoUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.Random;
import java.util.regex.Pattern;

/**
 * 验证码服务实现类
 */
@Slf4j
@Service
public class VerifyCodeServiceImpl implements VerifyCodeService {

    private final RedisUtil redisUtil;
    private final SmsBaoUtil smsBaoUtil;
    private final EmailUtil emailUtil;

    /**
     * 验证码有效期（分钟）
     */
    private static final int CODE_EXPIRE_MINUTES = 5;

    /**
     * 验证码长度
     */
    private static final int CODE_LENGTH = 6;

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

    /**
     * 邮箱正则
     */
    private static final Pattern EMAIL_PATTERN = Pattern.compile("^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+$");

    public VerifyCodeServiceImpl(RedisUtil redisUtil, SmsBaoUtil smsBaoUtil, EmailUtil emailUtil) {
        this.redisUtil = redisUtil;
        this.smsBaoUtil = smsBaoUtil;
        this.emailUtil = emailUtil;
    }

    @Override
    public boolean sendCode(String account, String type) {
        if (!StringUtils.hasText(account)) {
            throw new RuntimeException(I18nUtil.getMessage("error.account.empty"));
        }

        if (!StringUtils.hasText(type)) {
            throw new RuntimeException(I18nUtil.getMessage("error.verify.type.empty"));
        }

        // 判断是手机号还是邮箱
        boolean isPhone = PHONE_PATTERN.matcher(account).matches();
        boolean isEmail = EMAIL_PATTERN.matcher(account).matches();

        if (!isPhone && !isEmail) {
            throw new RuntimeException(I18nUtil.getMessage("error.account.format.invalid"));
        }

        // 生成验证码
        String code = generateCode();

        // 保存到 Redis，key格式：verify_code:{type}:{account}
        String redisKey = String.format("verify_code:%s:%s", type, account);
        redisUtil.set(redisKey, code, CODE_EXPIRE_MINUTES * 60);

        log.info("验证码已生成: account={}, type={}, code={}", account, type, code);

        // 发送验证码
        if (isPhone) {
            // 发送短信验证码
            boolean success = smsBaoUtil.sendVerifyCode(account, code);
            if (!success) {
                throw new RuntimeException(I18nUtil.getMessage("error.sms.send.failed"));
            }
        } else {
            // 发送邮件验证码
            boolean success = emailUtil.sendVerifyCode(account, code);
            if (!success) {
                throw new RuntimeException(I18nUtil.getMessage("error.email.send.failed"));
            }
        }

        return true;
    }

    @Override
    public boolean verifyCode(String account, String code, String type) {
        if (!StringUtils.hasText(account) || !StringUtils.hasText(code) || !StringUtils.hasText(type)) {
            return false;
        }

        // 从 Redis 获取验证码
        String redisKey = String.format("verify_code:%s:%s", type, account);
        Object savedCode = redisUtil.get(redisKey);

        if (savedCode == null) {
            log.warn("验证码不存在或已过期: account={}, type={}", account, type);
            return false;
        }

        // 验证码比对
        boolean matched = code.equals(savedCode.toString());

        if (matched) {
            // 验证成功后删除验证码
            redisUtil.del(redisKey);
            log.info("验证码验证成功: account={}, type={}", account, type);
        } else {
            log.warn("验证码错误: account={}, type={}, inputCode={}, savedCode={}",
                    account, type, code, savedCode);
        }

        return matched;
    }

    /**
     * 生成随机验证码
     *
     * @return 验证码
     */
    private String generateCode() {
        Random random = new Random();
        StringBuilder code = new StringBuilder();
        for (int i = 0; i < CODE_LENGTH; i++) {
            code.append(random.nextInt(10));
        }
        return code.toString();
    }
}
