package shop.alien.store.util.ali;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.time.Duration;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.TimeUnit;

/**
 * 用户端忘记密码短信每日发送次数限制（仅 appType=0, businessType=6）
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class UserForgetPasswordSmsDailyLimiter {

    private static final int MAX_DAILY_COUNT = 5;
    private static final int USER_APP_TYPE = 0;
    private static final int FORGET_PASSWORD_BUSINESS_TYPE = 6;
    private static final String KEY_PREFIX = "sms_user_forget_password_daily:";
    private static final ZoneId ZONE = ZoneId.of("Asia/Shanghai");

    private final StringRedisTemplate stringRedisTemplate;

    public boolean isUserForgetPassword(Integer appType, Integer businessType) {
        return USER_APP_TYPE == appType && FORGET_PASSWORD_BUSINESS_TYPE == businessType;
    }

    /**
     * 发送前校验，超限则抛出异常
     */
    public void checkLimit(String phone) {
        if (phone == null || phone.trim().isEmpty()) {
            return;
        }
        String key = buildDailyKey(phone.trim());
        String countStr = stringRedisTemplate.opsForValue().get(key);
        int count = 0;
        if (countStr != null && !countStr.isEmpty()) {
            try {
                count = Integer.parseInt(countStr);
            } catch (NumberFormatException e) {
                log.warn("UserForgetPasswordSmsDailyLimiter invalid count, key={}, value={}", key, countStr);
            }
        }
        if (count >= MAX_DAILY_COUNT) {
            throw new SmsSendLimitException("今日忘记密码验证码发送次数已达上限，请明日再试");
        }
    }

    /**
     * 短信发送成功后累计次数
     */
    public void recordSend(String phone) {
        if (phone == null || phone.trim().isEmpty()) {
            return;
        }
        String key = buildDailyKey(phone.trim());
        Long count = stringRedisTemplate.opsForValue().increment(key);
        if (count != null && count == 1) {
            stringRedisTemplate.expire(key, secondsUntilEndOfDay(), TimeUnit.SECONDS);
        }
    }

    private String buildDailyKey(String phone) {
        String date = LocalDate.now(ZONE).format(DateTimeFormatter.BASIC_ISO_DATE);
        return KEY_PREFIX + phone + ":" + date;
    }

    private long secondsUntilEndOfDay() {
        ZonedDateTime now = ZonedDateTime.now(ZONE);
        ZonedDateTime endOfDay = now.toLocalDate().plusDays(1).atStartOfDay(ZONE);
        return Math.max(Duration.between(now, endOfDay).getSeconds() + 1, 1);
    }
}
