package org.example.Impl;

import jakarta.annotation.PostConstruct;
import jakarta.servlet.http.HttpSession;
import org.example.Entity.BaseResponse.BaseResponse;
import org.example.Utils.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.sql.SQLException;
import java.sql.Types;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.function.Function;

import static com.mysql.cj.util.TimeUtil.DATE_FORMATTER;

@Service
public class SMSImpl extends OptBaseImpl {
    private static final int TOT_SEND_PHONE_CODE_ONE_DAY_ONE_IP = 10;
    private static final Logger log = LoggerFactory.getLogger(SMSImpl.class);

    @Autowired
    private UpdateImpl updateImpl;

    public SMSImpl() {
        super();
//        new UpdateImpl().updatePhoneCode();
    }

    // 初始化方法，在依赖注入完成后执行
    @PostConstruct
    public void init() throws SQLException {
        // 在这里调用 updatePhoneCode 方法
        updateImpl.updatePhoneCode();
    }

    final int TotSendPhoneCodeOneDayOneIP = 10;

    public BaseResponse sendPhoneCode(String phone, String vcode, String imgcode, String codekey) {
        BaseResponse[] response = new BaseResponse[1];
        response[0] = new BaseResponse();
        response[0].setSuccess(false);
        if (NativeExtensionUtils.isNullOrEmpty(phone) || !RegexUtil.PhoneReg.matcher(phone).matches()) {
            response[0].setMsg(LanguageUtil.WrongPhoneNumber);
            return response[0];
        }
        if (NativeExtensionUtils.isNullOrEmpty(imgcode)) {
            response[0].setSuccess(false);
            response[0].setMsg("请输入图片验证码");
            return response[0];
        }
        int checkCodeState = validateCaptcha(imgcode, codekey);
        if (checkCodeState == -1) {
            response[0].setSuccess(false);
            response[0].setMsg("图片验证码已失效，请刷新重试");
            return response[0];
        } else if (checkCodeState == 0) {
            response[0].setSuccess(false);
            response[0].setMsg("图片验证码错误");
            return response[0];
        }
        Date lastTime = getLastCodeSendTime(phone);
        Date now = new Date();
        if (now.getTime() - lastTime.getTime() < 5 * 60 * 1000L) {
            String existVcode = getCtx().getSession().getAttribute("phonevcode") + "";
            if (NativeExtensionUtils.isNullOrEmpty(vcode)) {
                response[0].setData(-2);
                response[0].setMsg("验证码发送频繁请5分钟后再试");
                return response[0];
            } else if (!vcode.equals(existVcode)) {
                response[0].setData(-1);
                response[0].setMsg(LanguageUtil.WrongVCode);
                return response[0];
            }
        }
        getCtx().getSession().removeAttribute("phonevcode");
        String ip = WebUtil.getIP();
        int tottodysend = getTotValidCodeInfoByIp(ip);
        if (tottodysend > TotSendPhoneCodeOneDayOneIP) {
            response[0].setData(-1);
            response[0].setMsg(String.format("发送数量%s", LanguageUtil.LimitOfTheNumber));
        }
        Map<String,Object> dtTable =  getCurrentValidCodeInfo(phone);
        String validcode;
        Boolean result = false;
        double radomtime = 31;
        if (dtTable.size() > 0) {
            Map<String,Object> row = dtTable;
            String sendtime = row.get("sendtime").toString()+"";
            validcode = row.get("validcode").toString()+"";
            if(!NativeExtensionUtils.isNullOrEmpty(sendtime)){
                // 1. 解析时间字符串（等效于C#的DateTime.Parse(sendtime)）
                LocalDateTime date = LocalDateTime.parse(sendtime, DATE_FORMATTER);
                // 2. 获取当前时间（等效于C#的DateTime.Now）
                LocalDateTime date1 = LocalDateTime.now();

                // 3. 计算时间差（分钟）（等效于C#的(date1 - date).TotalMinutes）
                radomtime = Duration.between(date, date1).toMinutes();

                // 4. 判断是否超过30分钟，超过则重新生成6位验证码
                if (radomtime > 30) {
                    // 调用你之前实现的6位随机验证码生成方法（对应C#的randomnumber(6)）
                    validcode = randomnumber(6);;
                }
            }
        }else{
            validcode = randomnumber(6);
        }

        //这里的resultMsg
        String[] resultMsg=new String[1];
        result = sendSMS(phone, validcode,resultMsg);
        if (result) {
            generateCode(phone, validcode, ip);
            log.info(String.format("向“%s”发送短信，ip：%s", phone, ip));
        }
        response[0].setData(1);
        response[0].setSuccess(result);
        response[0].setMsg(resultMsg[0]);
        return response[0];
    }

    private int validateCaptcha(String code, String codeKey) {
        Class<?> stringType = String.class;
        Object obj = CacheUtil.get(codeKey, stringType);
        if (obj == null) {
            return -1;
        }
        String storedCode = obj.toString();
        if (NativeExtensionUtils.isNullOrEmpty(storedCode)) {
            return -1;
        }
        Boolean isValid = storedCode.equalsIgnoreCase(code);
        CacheUtil.remove(codeKey);
        return isValid ? 1 : 0;
    }

    private String randomnumber(){
        return randomnumber(4);
    }

    private String randomnumber(int length){
        Random rd = new Random();
        StringBuffer str = new StringBuffer();
        // 循环直到字符串长度达到指定值
        while (str.length() < length) {
            // 生成0-9的随机整数（等效于C#的rd.Next(0, 10)）
            int temp = rd.nextInt(10);
            // 检查是否已包含该数字（等效于C#的!str.Contains(temp + "")）
            String tempStr = String.valueOf(temp);
            if (str.indexOf(tempStr) == -1) { // 不存在则追加
                str.append(temp);
            }
        }

        return str.toString();
    }


    public BaseResponse sendPhoneCode(String phone, String vcode, Function<String, String> getTemplate) {
        BaseResponse response = new BaseResponse();
        response.setSuccess(false);

        // 验证手机号格式
        if (phone == null || phone.isEmpty() || !RegexUtil.PhoneReg.matcher(phone).matches()) {
            response.setMsg(LanguageUtil.WrongPhoneNumber);
            return response;
        }

        // 验证模板函数
        if (getTemplate == null) {
            response.setMsg(String.format("模板%s", LanguageUtil.NotNull));
            return response;
        }

        // 检查5分钟内是否需要验证码
        Date lastTime = getLastCodeSendTime(phone);
        long minutesDiff = (new Date().getTime() - lastTime.getTime()) / (60 * 1000);

        if (minutesDiff < 5) {
            HttpSession session = getSession();
            String existVcode = (String) session.getAttribute("phonevcode");

            if (vcode == null || vcode.isEmpty()) {
                response.setData(-2);
                return response;
            } else if (!vcode.equals(existVcode)) {
                response.setData(-1);
                response.setMsg(LanguageUtil.WrongVCode);
                return response;
            }
        }

        // 清除session中的验证码
        getSession().removeAttribute("phonevcode");

        // 验证IP发送上限
        String ip = WebUtil.getIP();
        int todaySendCount = getTotValidCodeInfoByIp(ip);
        if (todaySendCount > TOT_SEND_PHONE_CODE_ONE_DAY_ONE_IP) {
            response.setData(-1);
            response.setMsg(String.format("发送数量%s", LanguageUtil.LimitOfTheNumber));
            return response;
        }

        // 处理验证码生成逻辑
        String validcode;
        Map<String, Object> currentCodeInfo = getCurrentValidCodeInfo(phone);

        if (currentCodeInfo != null) {
            String sendtime = (String) currentCodeInfo.get("sendtime");
            validcode = (String) currentCodeInfo.get("validcode");

            if (sendtime != null) {
                try {
                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    Date lastSendTime = sdf.parse(sendtime);
                    long minutes = (new Date().getTime() - lastSendTime.getTime()) / (60 * 1000);

                    if (minutes > 30) {
                        validcode = randomNumber(6);
                    }
                } catch (Exception e) {
                    validcode = randomNumber(6);
                }
            }
        } else {
            validcode = randomNumber(6);
        }

        // 发送短信
        String[] resultMsg = new String[1];
        boolean result = sendSMS(phone, getTemplate.apply(validcode), resultMsg);

        if (result) {
            generateCode(phone, validcode, ip);
            log.info(String.format("向“%s”发送短信，ip：%s", phone, ip));
        }

        response.setData(1);
        response.setSuccess(result);
        response.setMsg(resultMsg[0]);
        return response;
    }


    public boolean generateCode(String phone, String validcode, String ip) {
        String sql = "INSERT INTO P_ValidCode(phone, sendtime, validcode, ip) " +
                "VALUES(?, GETDATE(), ?, ?)";

        return jdbcTemplate.update(sql, phone, validcode, ip) > 0;
    }


    public boolean sendSMS(String phone, String msg, String[] resultMsg) {
        resultMsg[0] = "";
        if (WebConfigUtil.getSMSPwd() == null || WebConfigUtil.getSMSPwd().isEmpty() ||
                WebConfigUtil.getSMSUserName() == null || WebConfigUtil.getSMSUserName().isEmpty()) {
            resultMsg[0] = "向发送短信失败，未配置短信平台用户名密码";
            log.debug(resultMsg[0]);
            return false;
        }
//
//        String result = SMSService.SMS.postMethodToObj(
//                WebConfigUtil.getSMSUserName(),
//                WebConfigUtil.getSMSPwd(),
//                phone,
//                msg
//        );

        return true;
    }

    private String randomNumber(int length) {
        if (length <= 0) length = 4;
        Random rd = new Random();
        StringBuilder str = new StringBuilder();

        while (str.length() < length) {
            int temp = rd.nextInt(10);
            if (str.indexOf(String.valueOf(temp)) == -1) {
                str.append(temp);
            }
        }
        return str.toString();
    }


    public int getTotValidCodeInfoByIp(String ip) {
        if (ip == null || ip.isEmpty()) return 0;

        String sql = "SELECT COUNT(1) FROM P_ValidCode " +
                "WHERE ip = ? " +
                "AND sendtime >= ? " +
                "AND sendtime < ?";

        LocalDateTime now = LocalDateTime.now();
        LocalDateTime startOfDay = now.toLocalDate().atStartOfDay();
        LocalDateTime startOfNextDay = startOfDay.plusDays(1);

        Date start = Date.from(startOfDay.atZone(ZoneId.systemDefault()).toInstant());
        Date NextDay = Date.from(startOfNextDay.atZone(ZoneId.systemDefault()).toInstant());

        return jdbcTemplate.queryForObject(sql, new Object[]{ip, start, NextDay}, Integer.class);
    }


    public Map<String, Object> getCurrentValidCodeInfo(String phone) {
        String sql = "SELECT TOP 1 * FROM P_ValidCode " +
                "WHERE phone = ? " +
                "AND sendtime >= ? " +
                "AND ISNULL(used, 0) = 0 " +
                "ORDER BY sendtime DESC";

        LocalDateTime thirtyMinutesAgo = LocalDateTime.now().minusMinutes(30);
        // 转换为java.util.Date（与数据库字段类型匹配）
        Date thirtyMinutesAgoDate = Date.from(
                thirtyMinutesAgo.atZone(ZoneId.systemDefault()).toInstant()
        );

        try {
            return jdbcTemplate.queryForMap(sql, phone, thirtyMinutesAgoDate);
        } catch (Exception e) {
            return null;
        }
    }


    public Date getLastCodeSendTime(String phone) {
        String sql = "SELECT TOP 1 sendtime FROM P_ValidCode " +
                "WHERE phone = ? " +  // 第一个参数：phone
                "AND sendtime >= ? " +  // 第二个参数：30分钟前的时间
                "ORDER BY sendtime DESC";

        LocalDateTime thirtyMinutesAgo = LocalDateTime.now().minusMinutes(30);
        // 转换为java.util.Date（与数据库字段类型匹配）
        Date thirtyMinutesAgoDate = Date.from(
                thirtyMinutesAgo.atZone(ZoneId.systemDefault()).toInstant()
        );

        try {
            return jdbcTemplate.queryForObject(sql, new Object[]{phone, thirtyMinutesAgoDate}, Date.class);
        } catch (Exception e) {
            return Date.from(thirtyMinutesAgo.atZone(ZoneId.systemDefault()).toInstant());
        }
    }


    public boolean verfyCode(String phone, String code, boolean useOld) {
        if (phone == null || phone.isEmpty() || code == null || code.isEmpty()) {
            return false;
        }

        String sql;
        List<Object> params = new ArrayList<>();
        // 先添加共用参数（phone和code）
        params.add(phone);
        params.add(code);

        if (useOld) {
            // 查询条件：10分钟内有效且匹配手机号和验证码
            sql = "SELECT TOP 1 validcode, id FROM P_ValidCode " +
                    "WHERE phone = ? " +
                    "AND validcode = ? " +
                    "AND sendtime >= ? " +
                    "ORDER BY sendtime DESC";
            // 添加时间参数（10分钟前）
            LocalDateTime tenMinutesAgo = LocalDateTime.now().minusMinutes(10);
            params.add(Date.from(tenMinutesAgo.atZone(ZoneId.systemDefault()).toInstant()));
        } else {
            // 查询条件：30分钟内有效、未使用且匹配手机号
            sql = "SELECT TOP 1 validcode, id FROM P_ValidCode " +
                    "WHERE ISNULL(used, 0) = 0 " +
                    "AND phone = ? " +
                    "AND sendtime >= ? " +
                    "ORDER BY sendtime DESC";
            // 添加时间参数（30分钟前）
            LocalDateTime thirtyMinutesAgo = LocalDateTime.now().minusMinutes(30);
            params.add(Date.from(thirtyMinutesAgo.atZone(ZoneId.systemDefault()).toInstant()));
        }

        try {
            // 执行查询（参数按顺序传入）
            Map<String, Object> result = jdbcTemplate.queryForMap(sql, params.toArray());

            if (useOld) {
                // 旧逻辑：只要查询到记录就返回true
                return true;
            } else {
                // 新逻辑：验证验证码并标记为已使用
                String dbCode = (String) result.get("validcode");
                if (code.equals(dbCode)) {
                    // 更新验证码状态（使用?占位符传递id）
                    String updateSql = "UPDATE P_ValidCode SET used = 1 WHERE id = ?";
                    jdbcTemplate.update(updateSql, result.get("id"));
                    return true;
                }
            }
        } catch (Exception e) {
            // 未查询到记录或异常时返回false
        }

        return false;
    }

    public boolean verfyCode(String phone, String code) {
        return verfyCode(phone, code, false);
    }

    // 获取当前会话
    private HttpSession getSession() {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes == null) {
            return null;
        }
        return attributes.getRequest().getSession();
    }
}