package org.dromara.web.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.constant.Constants;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.core.utils.ip.AddressUtils;
import org.dromara.system.domain.bo.SysLogininforBo;
import org.dromara.system.domain.vo.SysLogininforVo;
import org.dromara.system.service.SysLogininforService;
import org.dromara.web.config.properties.RiskControlProperties;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 风险控制服务
 *
 * @author Pair-Programmer
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class RiskControlService {

    private final SysLogininforService logininforService;
    private final RiskControlProperties properties;

    /**
     * 评估登录风险
     *
     * @param username  用户名
     * @param currentIp 当前IP
     * @param uaStr     User-Agent字符串
     * @return 风险分数 (0-100)
     */
    public int assessRisk(String username, String currentIp, String uaStr) {
        if (!properties.getEnabled()) {
            return 0;
        }

        int score = 0;
        String currentAddress = AddressUtils.getRealAddressByIP(currentIp);
        UserAgent currentUserAgent = UserAgentUtil.parse(uaStr);

        // 1. 获取最近成功的登录记录 (取最近10条)
        SysLogininforBo query = new SysLogininforBo();
        query.setUserName(username);
        query.setStatus(Constants.SUCCESS);
        // 这里假设 selectLogininforList 是按时间倒序的，且我们可以通过PageHelper或流截取
        // 由于是 Service 调用，我们先获取列表然后手动截取，或者依赖 Mapper 的分页（这里简化处理）
        List<SysLogininforVo> historyList = logininforService.selectLogininforList(query);
        
        // 如果是首次登录或无历史记录，视为低风险
        if (CollUtil.isEmpty(historyList)) {
            return 0;
        }

        // 截取最近 10 条作为基线
        List<SysLogininforVo> baseline = historyList.stream().limit(10).collect(Collectors.toList());

        // 2. 检查异地登录 (省份是否变化)
        boolean isGeoAnomaly = true;
        Set<String> validLocations = baseline.stream()
            .map(SysLogininforVo::getLoginLocation)
            .filter(StrUtil::isNotBlank)
            .map(this::extractProvince)
            .collect(Collectors.toSet());
        
        String currentProvince = extractProvince(currentAddress);
        if (validLocations.contains(currentProvince)) {
            isGeoAnomaly = false;
        } else {
            // 特殊处理：如果历史记录都是“未知”或“内网IP”，且当前是公网IP，可能误报，暂且认为是异地
            log.info("风险检测-异地登录: 用户={}, 历史={}, 当前={}", username, validLocations, currentProvince);
        }

        if (isGeoAnomaly) {
            score += properties.getGeoWeight();
        }

        // 3. 检查新设备 (Browser + OS)
        boolean isNewDevice = true;
        Set<String> knownDevices = baseline.stream()
            .map(log -> log.getBrowser() + " on " + log.getOs())
            .collect(Collectors.toSet());
        
        String currentDevice = currentUserAgent.getBrowser().getName() + " on " + currentUserAgent.getOs().getName();
        if (knownDevices.contains(currentDevice)) {
            isNewDevice = false;
        } else {
            log.info("风险检测-新设备: 用户={}, 历史={}, 当前={}", username, knownDevices, currentDevice);
        }

        if (isNewDevice) {
            score += properties.getDeviceWeight();
        }

        // 4. 检查非工作时间 (00:00 - 06:00)
        int hour = DateUtil.hour(new Date(), true);
        if (hour >= 0 && hour < 6) {
            score += properties.getTimeWeight();
        }

        log.info("风险检测完成: 用户={}, 总分={}, 详情(Geo={}, Device={}, Time={})", 
            username, score, isGeoAnomaly, isNewDevice, (hour >= 0 && hour < 6));
        
        return score;
    }

    /**
     * 提取省份 (简单截取)
     */
    private String extractProvince(String location) {
        if (StrUtil.isBlank(location)) {
            return "Unknown";
        }
        // 假设 location 格式为 "XX省 XX市" 或 "XX省"
        // 简单处理：取前2-3个字符，或者根据空格分割
        if (location.contains("省")) {
            return location.substring(0, location.indexOf("省") + 1);
        }
        // 直辖市
        if (location.startsWith("北京") || location.startsWith("上海") || location.startsWith("天津") || location.startsWith("重庆")) {
            return location.substring(0, 2);
        }
        return location; // 无法识别则返回原值
    }
}
