package com.bonus.system.service.impl;

import com.bonus.common.core.exception.ServiceException;
import com.bonus.common.core.utils.DateUtils;
import com.bonus.common.core.web.domain.AjaxResult;
import com.bonus.common.security.utils.SecurityUtils;
import com.bonus.config.SystemConfig;
import com.bonus.system.api.domain.SysUser;
import com.bonus.system.api.model.LoginUser;
import com.bonus.system.domain.UserPasswordHistory;
import com.bonus.system.mapper.PasswordValidatorMapper;
import com.bonus.system.service.ISysConfigService;
import com.bonus.system.service.PasswordValidatorService;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

@Service
public class PasswordValidatorServiceImpl implements PasswordValidatorService {

    @Resource
    private SystemConfig systemConfig;

    @Resource
    private ISysConfigService configService;

    @Resource
    private PasswordValidatorMapper passwordValidatorMapper;

    @Override
    public AjaxResult validatePassword(Long userId, String username, String oldPassword, String newPassword) {
        // 1. 检查密码长度
        if (!isPasswordLengthValid(newPassword)) {
            return AjaxResult.error("密码长度应为" + systemConfig.getPasswordConfig().getMinLength() + "至" + systemConfig.getPasswordConfig().getMaxLength() + "位！");
        }

        // 2. 检查密码字符类型
        isPasswordCharacterValid(newPassword);
        // 3. 检查常见弱密码
        if (containsWeakPassword(newPassword.toLowerCase())) {
            return AjaxResult.error("密码包含常见的弱密码片段！");
        }

        // 4. 检查连续字符
        if (systemConfig.getPasswordConfig().isRestrictConsecutiveChars() && containsConsecutiveCharacters(newPassword.toLowerCase(), systemConfig.getPasswordConfig().getMaxConsecutiveChars())) {
            return AjaxResult.error("密码不能包含超过" + systemConfig.getPasswordConfig().getMaxConsecutiveChars() + "位连续字符！");
        }

        // 5. 检查用户名
        if (systemConfig.getPasswordConfig().isExcludeUsernameInPassword() && newPassword.toLowerCase().contains(username.toLowerCase())) {
            return AjaxResult.error("密码不能包含账号！");
        }

        // 6. 检查新旧密码是否相同
        if (SecurityUtils.matchesPassword(newPassword, oldPassword)) {
            return AjaxResult.error("新密码不能与原密码相同！");
        }

        // 7. 检查密码历史
        if (isPasswordInHistory(userId, newPassword)) {
            return AjaxResult.error("新密码不能与最近的" + systemConfig.getPasswordConfig().getPasswordHistoryLimit() + "个旧密码相同！");
        }

        return AjaxResult.success();
    }

    private boolean isPasswordLengthValid(String password) {
        return password.length() >= systemConfig.getPasswordConfig().getMinLength() && password.length() <= systemConfig.getPasswordConfig().getMaxLength();
    }

    /**
     * 判断密码是否规范
     * @param password
     */
    private void isPasswordCharacterValid(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;
            }
        }

        if (systemConfig.getPasswordConfig().isRequireUpperCase() && !hasUpperCase) {
            throw new ServiceException("新密码必须包含大写字母！");
        }
        if (systemConfig.getPasswordConfig().isRequireLowerCase() && !hasLowerCase) {
            throw new ServiceException("新密码必须包含小写字母！");
        }
        if (systemConfig.getPasswordConfig().isRequireDigit() && !hasDigit) {
            throw new ServiceException("新密码必须包含数字！");
        }
        if (systemConfig.getPasswordConfig().isRequireSpecialChar() && !hasSpecialChar) {
            throw new ServiceException("新密码必须包含特殊字符！");
        }
    }

    private boolean containsWeakPassword(String password) {
        Set<String> weakPasswords = new HashSet<>(systemConfig.getPasswordConfig().getWeakPasswords());
        for (String weakPwd : weakPasswords) {
            if (password.contains(weakPwd)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 根据配置返回密码不符合要求时的错误提示信息
     */
    private String getCharacterRequirementErrorMessage() {
        if (systemConfig.getPasswordConfig().isRequireUpperCase()) {
            return "密码必须包含大写字母！";
        }
        if (systemConfig.getPasswordConfig().isRequireLowerCase()) {
            return "密码必须包含小写字母！";
        }
        if (systemConfig.getPasswordConfig().isRequireDigit()) {
            return "密码必须包含数字！";
        }
        if (systemConfig.getPasswordConfig().isRequireSpecialChar()) {
            return "密码必须包含特殊字符！";
        }
        return "密码不符合字符要求！";
    }

    private boolean isPasswordInHistory(Long userId, String newPassword) {
        List<UserPasswordHistory> userPasswordHistories = passwordValidatorMapper.checkPasswordExpiry(userId);
        if (userPasswordHistories.isEmpty()) return false;

        // 只取最近的五次修改记录
        int limit = Math.min(userPasswordHistories.size(), systemConfig.getPasswordConfig().getPasswordHistoryLimit());
        for (int i = 0; i < limit; i++) {
            UserPasswordHistory history = userPasswordHistories.get(i);
            if (SecurityUtils.matchesPassword(newPassword, history.getNewPassword())) {
                return true;
            }
        }
        return false;
    }

    @Override
    public boolean checkFirstLogin() {
        if (!systemConfig.getPasswordConfig().isForcePasswordChangeOnFirstLogin()) {
            return false;
        }
        LoginUser loginUser = SecurityUtils.getLoginUser();
        SysUser sysUser = loginUser.getSysUser();
        String initialPassword = configService.selectConfigByKey("sys.user.initPassword");
        return SecurityUtils.matchesPassword(initialPassword, sysUser.getPassword());
    }

    @Override
    public boolean checkPasswordExpiry() {
        if (!systemConfig.getPasswordConfig().isEnableRegularlyChangePassword()) {
            return false;
        }
        Long userId = SecurityUtils.getUserId();
        List<UserPasswordHistory> userPasswordHistories = passwordValidatorMapper.checkPasswordExpiry(userId);
        if (userPasswordHistories.isEmpty()) {
            return false;
        }
        UserPasswordHistory userPasswordHistory = userPasswordHistories.get(0);
        Date currentDate = DateUtils.getNowDate();
        Date changeDate = DateUtils.toDate(userPasswordHistory.getChangeTimestamp());

        long daysSinceChange = DateUtils.daysBetween(changeDate, currentDate);
        return daysSinceChange > systemConfig.getPasswordConfig().getRegularlyChangePassword();
    }

    @Override
    public AjaxResult isPasswordInRecentHistory(Long userId, String newPassword) {
        List<UserPasswordHistory> userPasswordHistories = passwordValidatorMapper.checkPasswordExpiry(userId);
        if (userPasswordHistories.isEmpty()) {
            return AjaxResult.success("没有找到密码历史记录。");
        }

        int limit = Math.min(userPasswordHistories.size(), systemConfig.getPasswordConfig().getPasswordHistoryLimit());
        for (int i = 0; i < limit; i++) {
            UserPasswordHistory history = userPasswordHistories.get(i);
            if (SecurityUtils.matchesPassword(newPassword, history.getNewPassword())) {
                return AjaxResult.error("新密码不能与最近的密码相同。");
            }
        }

        return AjaxResult.success("新密码有效。");
    }

    @Override
    public int addPasswordExpiry(UserPasswordHistory userPasswordHistory) {
        return passwordValidatorMapper.addPasswordExpiry(userPasswordHistory);
    }

    /**
     * 检查密码中是否包含超过 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;
    }
}
