package com.bonus.auth.service;

import com.bonus.common.core.constant.CacheConstants;
import com.bonus.common.core.constant.UserConstants;
import com.bonus.common.core.domain.R;
import com.bonus.common.core.enums.UserStatus;
import com.bonus.common.core.exception.CaptchaException;
import com.bonus.common.core.exception.ServiceException;
import com.bonus.common.core.text.Convert;
import com.bonus.common.core.utils.StringUtils;
import com.bonus.common.core.utils.encryption.Sm4Utils;
import com.bonus.common.core.utils.ip.IpUtils;
import com.bonus.common.core.web.domain.AjaxResult;
import com.bonus.common.redis.service.RedisService;
import com.bonus.config.SystemConfig;
import com.bonus.system.api.RemoteUserService;
import com.bonus.system.api.domain.SysUser;
import com.bonus.system.api.model.LoginUser;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

@Component
public class PasswordValidatorService {

    @Resource
    private SystemConfig systemConfig;

    @Resource
    private RedisService redisService;

    @Resource
    private SysRecordLogService recordLogService;

    @Resource
    private RemoteUserService remoteUserService;

    /**
     * 对新密码进行校验，返回布尔值表示密码是否符合要求。
     *
     * @param username    用户名，不能包含在密码中
     * @param decryptedNewPassword 新密码
     * @return 如果密码符合策略要求，则返回 AjaxResult.success，否则返回错误提示
     */
    public AjaxResult validatePassword(String username, String decryptedNewPassword) {
        String newPassword = Sm4Utils.decrypt(decryptedNewPassword);
        if (!isPasswordLengthValid(newPassword)) {
            return AjaxResult.error("密码长度应为" + systemConfig.getPasswordConfig().getMinLength() + "至" + systemConfig.getPasswordConfig().getMaxLength() + "位！");
        }

        if (!containsRequiredCharacters(newPassword)) {
            return AjaxResult.error(getCharacterRequirementErrorMessage(newPassword));
        }

        if (containsWeakPassword(newPassword)) {
            return AjaxResult.error("密码包含常见的弱密码片段！");
        }

        if (containsConsecutiveCharacters(newPassword.toLowerCase(), systemConfig.getPasswordConfig().getMaxConsecutiveChars())) {
            return AjaxResult.error("密码不能包含超过" + systemConfig.getPasswordConfig().getMaxConsecutiveChars() + "个连续相同字符、连续递增/递减的数字或字母（不区分大小写）");
        }

        if (newPassword.toLowerCase().contains(username.toLowerCase())) {
            return AjaxResult.error("密码不能包含账号！");
        }

        return AjaxResult.success();
    }

    /**
     * 检查密码长度是否符合配置要求
     */
    private boolean isPasswordLengthValid(String password) {
        return password.length() >= systemConfig.getPasswordConfig().getMinLength() && password.length() <= systemConfig.getPasswordConfig().getMaxLength();
    }

    /**
     * 检查密码是否包含必需的字符类型
     */
    private boolean containsRequiredCharacters(String password) {
        boolean hasUpperCase = false, hasLowerCase = false, hasDigit = false, hasSpecialChar = false;

        for (char c : password.toCharArray()) {
            if (Character.isUpperCase(c)) {
                hasUpperCase = true;
            }
            if (Character.isLowerCase(c)) {
                hasLowerCase = true;
            }
            if (Character.isDigit(c)) {
                hasDigit = true;
            }
            if ("!@#$%^&*()-_=+[{]};:'\",<.>/?".indexOf(c) >= 0) {
                hasSpecialChar = true;
            }
        }

        return (!systemConfig.getPasswordConfig().isRequireUpperCase() || hasUpperCase) &&
                (!systemConfig.getPasswordConfig().isRequireLowerCase() || hasLowerCase) &&
                (!systemConfig.getPasswordConfig().isRequireDigit() || hasDigit) &&
                (!systemConfig.getPasswordConfig().isRequireSpecialChar() || hasSpecialChar);
    }

    /**
     * 根据配置返回密码不符合要求时的错误提示信息
     */
    private String getCharacterRequirementErrorMessage(String newPassword) {
        boolean hasUpperCase = false, hasLowerCase = false, hasDigit = false, hasSpecialChar = false;
        for (char c : newPassword.toCharArray()) {
            if (Character.isUpperCase(c)) hasUpperCase = true;
            if (Character.isLowerCase(c)) hasLowerCase = true;
            if (Character.isDigit(c)) hasDigit = true;
            if ("!@#$%^&*()-_=+[{]};:'\",<.>/?".indexOf(c) >= 0) hasSpecialChar = true;
        }
        if (systemConfig.getPasswordConfig().isRequireUpperCase() && !hasUpperCase) {
            return "密码必须包含大写字母！";
        }
        if (systemConfig.getPasswordConfig().isRequireLowerCase() && !hasLowerCase) {
            return "密码必须包含小写字母！";
        }
        if (systemConfig.getPasswordConfig().isRequireDigit() && !hasDigit) {
            return "密码必须包含数字！";
        }
        if (systemConfig.getPasswordConfig().isRequireSpecialChar() && !hasSpecialChar) {
            return "密码必须包含特殊字符！";
        }
        return "密码不符合字符要求！";
    }

    /**
     * 检查密码是否包含常见的弱密码
     */
    private boolean containsWeakPassword(String password) {
        for (String weakPwd : systemConfig.getPasswordConfig().getWeakPasswords()) {
            if (password.toLowerCase().contains(weakPwd)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 检查密码中是否包含超过 n 个连续相同字符、连续递增/递减的数字或字母（不区分大小写）
     */
    private boolean containsConsecutiveCharacters(String password, int n) {
        // 检查连续相同字符
        n = n + 1;
        for (int i = 0; i <= password.length() - n; i++) {
            boolean consecutiveSameChar = true;
            for (int j = 1; j < n; j++) {
                if (password.charAt(i + j) != password.charAt(i)) {
                    consecutiveSameChar = false;
                    break;
                }
            }
            if (consecutiveSameChar) {
                return true; // 包含超过 n 个连续相同字符
            }
        }

        // 检查连续递增或递减的数字
        for (int i = 0; i <= password.length() - n; i++) {
            boolean consecutiveIncreasing = true;
            boolean consecutiveDecreasing = true;
            for (int j = 1; j < n; j++) {
                char currentChar = password.charAt(i);
                char nextChar = password.charAt(i + j);

                // 检查数字递增或递减
                if (Character.isDigit(currentChar) && Character.isDigit(nextChar)) {
                    if (nextChar != currentChar + j) {
                        consecutiveIncreasing = false;
                    }
                    if (nextChar != currentChar - j) {
                        consecutiveDecreasing = false;
                    }
                } else {
                    consecutiveIncreasing = false;
                    consecutiveDecreasing = false;
                    break;
                }
            }
            if (consecutiveIncreasing || consecutiveDecreasing) {
                return true; // 包含超过 n 个递增或递减的连续数字
            }
        }

        // 检查连续递增或递减的字母（不区分大小写）
        for (int i = 0; i <= password.length() - n; i++) {
            boolean consecutiveIncreasing = true;
            boolean consecutiveDecreasing = true;
            for (int j = 1; j < n; j++) {
                char currentChar = Character.toLowerCase(password.charAt(i)); // 转为小写
                char nextChar = Character.toLowerCase(password.charAt(i + j)); // 转为小写

                // 检查字母递增或递减
                if (Character.isLetter(currentChar) && Character.isLetter(nextChar)) {
                    if (nextChar != currentChar + j) {
                        consecutiveIncreasing = false;
                    }
                    if (nextChar != currentChar - j) {
                        consecutiveDecreasing = false;
                    }
                } else {
                    consecutiveIncreasing = false;
                    consecutiveDecreasing = false;
                    break;
                }
            }
            if (consecutiveIncreasing || consecutiveDecreasing) {
                // 包含超过 n 个递增或递减的连续字母
                return true;
            }
        }
        // 不包含连续相同字符、数字或字母序列
        return false;
    }


    /**
     * 验证登录参数
     */
    public void validateLoginParameters(String username, String password) {
        if (StringUtils.isAnyBlank(username, password)) {
            logAndThrowError(username, "用户名/密码必须填写", "用户名/密码为空");
        }
        if (!isPasswordLengthValid(password)) {
            logAndThrowError(username, "密码格式不正确", "密码格式不正确");
        }
        if (username.length() < UserConstants.USERNAME_MIN_LENGTH || username.length() > UserConstants.USERNAME_MAX_LENGTH) {
            logAndThrowError(username, "用户名格式不正确", "用户名格式不正确");
        }
    }

    /**
     * 验证IP黑名单
     */
    public void validateIpBlacklist(String username) {
        long startTime = System.currentTimeMillis();
        try {
            String blackStr = Convert.toStr(redisService.getCacheObject(CacheConstants.SYS_LOGIN_BLACKIPLIST));
            String ip = IpUtils.getIpAddr();
            if (IpUtils.isMatchedIp(blackStr,ip )) {
                logAndThrowError(username, "访问IP已被列入系统黑名单", "访问IP已被列入系统黑名单");
            }
        } catch (Exception e) {
            logAndThrowError(username, "IP黑名单校验失败，请稍后重试", e.getMessage());
        }
    }

    /**
     * 验证用户查询结果
     */
    public void validateUserResult(String username, R<LoginUser> userResult) {
        if (userResult == null || userResult.getData() == null || R.FAIL == userResult.getCode()) {
            if (Objects.nonNull(userResult)) {
                logAndThrowError(username, userResult.getMsg(), userResult.getMsg());
            }
            logAndThrowError(username, "用户名/密码错误", "登录用户不存在");
        }
    }

    /**
     * 验证用户状态
     */
    public void validateUserStatus(String username, SysUser user) {
        if (UserStatus.DELETED.getCode().equals(user.getDelFlag()) || UserStatus.DISABLE.getCode().equals(user.getStatus())) {
            logAndThrowError(username, "账号已被删除或停用", "账号已被删除或停用");
        }
    }

    /**
     * 处理IP校验
     */
    public void handleIpValidation(String username, SysUser user) {
        try {
            String nowIp = IpUtils.getIpAddr();
            String hisIp = redisService.getCacheObject("IP:" + user.getUserId());
            if (!nowIp.equals(hisIp)) {
                recordLogService.saveErrorLogs(username, System.currentTimeMillis(), user.getUserId().toString(),"用户连续两次在不同IP登录");
            }
            redisService.setCacheObject("IP:" + user.getUserId(), nowIp, 5L, TimeUnit.MINUTES);
        } catch (Exception e) {
            logAndThrowError(username, "IP校验失败，请稍后重试", e.getMessage());
        }
    }

    public void processLoginBlackList(SysUser user){
        // 获取黑名单列表
        List<Map<String, Object>> cacheList = redisService.getCacheObject(CacheConstants.SYS_LOGIN_BLACKIPLIST);

        // 获取客户端的 IP 地址
        String ip = IpUtils.getIpAddr();

        // 遍历黑名单
        for (Map<String, Object> map : cacheList) {
            String ipAddress = (String) map.getOrDefault("ipAddress", null);
            String ipRangeEnd = (String) map.getOrDefault("ipRangeEnd", null);
            String ipRangeStart = (String) map.getOrDefault("ipRangeStart", null);
            String accessStartTime = (String) map.getOrDefault("accessStartTime", null);
            String accessEndTime = (String) map.getOrDefault("accessEndTime", null);

            // 如果 ipAddress 为空，检查是否在 ip 范围内
            if (ObjectUtils.isEmpty(ipAddress)) {
                if (isIpInRange(ip, ipRangeStart, ipRangeEnd)) {
                    boolean result = handleAccessTimeCheck(user, accessStartTime, accessEndTime);
                    if (!result) {
                        throw new ServiceException("ip地址异常");
                    }else{
                        return;
                    }
                }
            } else if (ipAddress.equals(ip)) {
                boolean result = handleAccessTimeCheck(user, accessStartTime, accessEndTime);
                if (!result) {
                    throw new ServiceException("ip地址异常");
                }else{
                    return;
                }

            }
        }

    }


    /**
     * 检查当前时间是否在有效的访问时间范围内
     * @param accessStartTime 访问开始时间
     * @param accessEndTime 访问结束时间
     */
    private boolean handleAccessTimeCheck(SysUser user, String accessStartTime, String accessEndTime) {
        if (ObjectUtils.isNotEmpty(accessStartTime)) {
            boolean currentTimeInRange = isCurrentTimeInRange(accessStartTime, accessEndTime);
            if (!currentTimeInRange) {
                recordLogService.saveErrorLogs(user.getUserName(), System.currentTimeMillis(), user.getUserId().toString(),"IP地址异常");
                return false;
            } else {
                return true;
            }
        } else {
            return true;
        }
    }

    /**
     * 处理正常情况
     */
    private void handleNormalProcess() {
        // 正常处理的具体逻辑
        System.out.println("IP access is within the allowed time range.");
    }

    /**
     * 检查给定的IP地址是否在指定的网段区间内
     *
     * @param ip         要检查的IP地址，例如 "192.168.1.10"
     * @param startIp    区间开始的IP地址，例如 "192.168.1.0"
     * @param endIp      区间结束的IP地址，例如 "192.168.1.255"
     * @return true 如果IP在区间内；否则返回 false
     */
    public static boolean isIpInRange(String ip, String startIp, String endIp) {
        try {
            // 将 IP 地址、起始 IP 和结束 IP 转换为整数
            long ipToCheck = ipToLong(InetAddress.getByName(ip));
            long start = ipToLong(InetAddress.getByName(startIp));
            long end = ipToLong(InetAddress.getByName(endIp));

            // 检查 IP 是否在区间内
            return ipToCheck >= start && ipToCheck <= end;
        } catch (UnknownHostException e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 将IP地址转换为整数
     *
     * @param inetAddress IP地址对象
     * @return 转换后的长整数
     */
    private static long ipToLong(InetAddress inetAddress) {
        byte[] octets = inetAddress.getAddress();
        long result = 0;
        for (byte octet : octets) {
            result = (result << 8) | (octet & 0xFF);
        }
        return result;
    }
    public static boolean isCurrentTimeInRange(String startDateTime, String endDateTime) {
        // 定义日期时间格式
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

        // 将字符串转换为 LocalDateTime
        LocalDateTime start = LocalDateTime.parse(startDateTime, formatter);
        LocalDateTime end = LocalDateTime.parse(endDateTime, formatter);

        // 获取当前日期和时间
        LocalDateTime currentDateTime = LocalDateTime.now();

        // 检查当前日期和时间是否在指定的范围内
        return !currentDateTime.isBefore(start) && !currentDateTime.isAfter(end);
    }


    public void validateApprovalStatus(String username, SysUser user) {
        if ("0".equals(user.getApprovalStatus())) {
            logAndThrowError(username, "账号未审批", "用户不存在");
        }
    }

    /**
     * 记录错误日志并抛出异常
     */
    private void logAndThrowError(String username, String message, String logMessage) {
        long startTime = System.currentTimeMillis();
        recordLogService.saveLogs(username, startTime, logMessage, message, null, "失败");
        throw new ServiceException(message);
    }

    /**
     * 校验手机验证码
     */
    public void checkPhoneCaptcha(String phone, String code) throws CaptchaException {
        if (StringUtils.isEmpty(code)) {
            throw new CaptchaException("手机验证码不能为空");
        }
        if (StringUtils.isEmpty(phone)) {
            throw new CaptchaException("手机号不能为空");
        }
        String verifyKey = CacheConstants.VERIFICATION_CODE + StringUtils.nvl(phone, "");
        String captcha = redisService.getCacheObject(verifyKey);
        if (captcha == null) {
            throw new CaptchaException("手机验证码已失效");
        }

        if (!code.equalsIgnoreCase(captcha)) {
            throw new CaptchaException("手机验证码错误");
        }else {
            redisService.deleteObject(verifyKey);
        }
    }


}
