package com.vetti.hotake.service.impl;

import com.vetti.common.core.domain.model.LoginUser;
import com.vetti.common.core.service.BaseServiceImpl;
import com.vetti.common.enums.FillTypeEnum;
import com.vetti.common.exception.ServiceException;
import com.vetti.common.utils.MessageUtils;
import com.vetti.common.utils.SecurityUtils;
import com.vetti.common.utils.ServletUtils;
import com.vetti.common.utils.StringUtils;
import com.vetti.common.utils.ip.AddressUtils;
import com.vetti.common.utils.ip.IpUtils;
import com.vetti.common.constant.CacheConstants;
import com.vetti.common.core.redis.RedisCache;
import com.vetti.hotake.domain.HotakeSecurityLoginSessions;
import com.vetti.hotake.domain.HotakeSecurityPasswordHistory;
import com.vetti.hotake.domain.HotakeSecuritySecurityLogs;
import com.vetti.hotake.domain.HotakeSecuritySettings;
import com.vetti.hotake.domain.HotakeSecurityTrustedDevices;
import com.vetti.hotake.domain.dto.SecurityChangePasswordDto;
import com.vetti.hotake.domain.vo.SecuritySessionVo;
import com.vetti.hotake.domain.vo.SecurityTrustedDeviceVo;
import com.vetti.hotake.mapper.HotakeSecurityLoginSessionsMapper;
import com.vetti.hotake.mapper.HotakeSecurityPasswordHistoryMapper;
import com.vetti.hotake.mapper.HotakeSecuritySecurityLogsMapper;
import com.vetti.hotake.mapper.HotakeSecuritySettingsMapper;
import com.vetti.hotake.mapper.HotakeSecurityTrustedDevicesMapper;
import com.vetti.hotake.service.IHotakeSecurityService;
import com.vetti.system.service.ISysUserService;
import eu.bitwalker.useragentutils.Browser;
import eu.bitwalker.useragentutils.OperatingSystem;
import eu.bitwalker.useragentutils.UserAgent;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 安全设置Service业务层处理
 *
 * @author vetti
 * @date 2026-02-02
 */
@Service
public class HotakeSecurityServiceImpl extends BaseServiceImpl implements IHotakeSecurityService
{
    @Autowired
    private HotakeSecuritySettingsMapper securitySettingsMapper;

    @Autowired
    private HotakeSecurityLoginSessionsMapper loginSessionsMapper;

    @Autowired
    private HotakeSecurityPasswordHistoryMapper passwordHistoryMapper;

    @Autowired
    private HotakeSecuritySecurityLogsMapper securityLogsMapper;

    @Autowired
    private HotakeSecurityTrustedDevicesMapper trustedDevicesMapper;

    @Autowired
    private ISysUserService userService;

    @Autowired
    private RedisCache redisCache;

    @Value("${token.expireTime}")
    private int expireTime;

    /**
     * 获取当前用户的安全设置
     * 如果不存在则自动创建默认设置
     *
     * @return 安全设置
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public HotakeSecuritySettings getCurrentUserSecuritySettings()
    {
        Long userId = SecurityUtils.getUserId();
        
        // 查询用户的安全设置
        HotakeSecuritySettings settings = securitySettingsMapper.selectHotakeSecuritySettingsByUserId(userId);
        
        // 如果不存在，创建默认设置
        if (settings == null)
        {
            settings = new HotakeSecuritySettings();
            settings.setUserId(userId);
            settings.setTwoFactorEnabled(0);
            settings.setTwoFactorType("TOTP");
            settings.setFailedLoginAttempts(0);
            
            fill(FillTypeEnum.INSERT.getCode(), settings);
            securitySettingsMapper.insertHotakeSecuritySettings(settings);
        }
        
        return settings;
    }

    /**
     * 更新两步验证设置
     *
     * @param enabled 是否启用
     * @return 结果
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public int updateTwoFactorEnabled(boolean enabled)
    {
        Long userId = SecurityUtils.getUserId();
        HotakeSecuritySettings settings = getCurrentUserSecuritySettings();
        
        settings.setTwoFactorEnabled(enabled ? 1 : 0);
        fill(FillTypeEnum.UPDATE.getCode(), settings);
        
        // 记录安全日志
        String eventType = enabled ? "2FA_ENABLED" : "2FA_DISABLED";
        String eventDesc = enabled ? 
            MessageUtils.messageCustomize("HotakeSecurityServiceImpl10006") : 
            MessageUtils.messageCustomize("HotakeSecurityServiceImpl10007");
        recordSecurityLog(userId, eventType, eventDesc, "SUCCESS");
        
        return securitySettingsMapper.updateHotakeSecuritySettings(settings);
    }

    /**
     * 修改密码
     *
     * @param dto 修改密码DTO
     * @return 结果
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void changePassword(SecurityChangePasswordDto dto)
    {
        Long userId = SecurityUtils.getUserId();
        String username = SecurityUtils.getUsername();
        
        // 验证新密码和确认密码是否一致
        if (!dto.getNewPassword().equals(dto.getConfirmPassword()))
        {
            throw new ServiceException(MessageUtils.messageCustomize("HotakeSecurityServiceImpl10001"));
        }
        
        // 验证当前密码是否正确
        if (!SecurityUtils.matchesPassword(dto.getCurrentPassword(), SecurityUtils.getLoginUser().getPassword()))
        {
            // 记录失败日志
            recordSecurityLog(userId, "PASSWORD_CHANGE", 
                MessageUtils.messageCustomize("HotakeSecurityServiceImpl10005"), "FAILED");
            throw new ServiceException(MessageUtils.messageCustomize("HotakeSecurityServiceImpl10002"));
        }
        
        // TODO: 安全功能集成 - 检查新密码是否与历史密码重复
        // 查询最近5次的密码历史
        List<HotakeSecurityPasswordHistory> historyList = passwordHistoryMapper.selectRecentPasswordHistory(userId, 5);
        String newPasswordHash = SecurityUtils.encryptPassword(dto.getNewPassword());
        
        for (HotakeSecurityPasswordHistory history : historyList)
        {
            if (SecurityUtils.matchesPassword(dto.getNewPassword(), history.getPasswordHash()))
            {
                throw new ServiceException(MessageUtils.messageCustomize("HotakeSecurityServiceImpl10003"));
            }
        }
        
        // 修改密码
        userService.resetUserPwd(userId, newPasswordHash);
        
        // 记录密码历史
        HotakeSecurityPasswordHistory history = new HotakeSecurityPasswordHistory();
        history.setUserId(userId);
        history.setPasswordHash(newPasswordHash);
        history.setChangedAt(new Date());
        history.setChangedBy("USER");
        history.setIpAddress(IpUtils.getIpAddr());
        fill(FillTypeEnum.INSERT.getCode(), history);
        passwordHistoryMapper.insertHotakeSecurityPasswordHistory(history);
        
        // 更新安全设置中的密码修改时间
        HotakeSecuritySettings settings = getCurrentUserSecuritySettings();
        settings.setPasswordChangedAt(new Date());
        fill(FillTypeEnum.UPDATE.getCode(), settings);
        securitySettingsMapper.updateHotakeSecuritySettings(settings);
        
        // 记录安全日志
        recordSecurityLog(userId, "PASSWORD_CHANGE", 
            MessageUtils.messageCustomize("HotakeSecurityServiceImpl10008"), "SUCCESS");
    }

    /**
     * 获取当前用户的活跃会话列表
     *
     * @return 会话列表
     */
    @Transactional(readOnly = true)
    @Override
    public List<SecuritySessionVo> getActiveSessions()
    {
        Long userId = SecurityUtils.getUserId();
        LoginUser loginUser = SecurityUtils.getLoginUser();
        String currentToken = loginUser.getToken();
        
        // 查询用户的活跃会话
        List<HotakeSecurityLoginSessions> sessionsList = loginSessionsMapper.selectActiveSessionsByUserId(userId);
        
        // 转换为VO
        List<SecuritySessionVo> voList = new ArrayList<>();
        for (HotakeSecurityLoginSessions session : sessionsList)
        {
            SecuritySessionVo vo = new SecuritySessionVo();
            BeanUtils.copyProperties(session, vo);
            
            // 组合设备名称
            String deviceName = "";
            if (StringUtils.isNotEmpty(session.getBrowser()))
            {
                deviceName = session.getBrowser();
            }
            if (StringUtils.isNotEmpty(session.getOs()))
            {
                deviceName += (StringUtils.isNotEmpty(deviceName) ? " / " : "") + session.getOs();
            }
            vo.setDeviceName(deviceName);
            
            // 组合地理位置
            String location = "";
            if (StringUtils.isNotEmpty(session.getLocationCity()))
            {
                location = session.getLocationCity();
            }
            if (StringUtils.isNotEmpty(session.getLocationCountry()))
            {
                location += (StringUtils.isNotEmpty(location) ? ", " : "") + session.getLocationCountry();
            }
            vo.setLocation(location);
            
            // 判断是否为当前会话
            vo.setIsCurrent(session.getSessionToken().equals(currentToken));
            vo.setIsActive(session.getIsActive() == 1);
            
            voList.add(vo);
        }
        
        return voList;
    }

    /**
     * 终止指定会话
     *
     * @param sessionId 会话ID
     * @return 结果
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public int terminateSession(Long sessionId)
    {
        Long userId = SecurityUtils.getUserId();
        
        // 查询会话信息
        HotakeSecurityLoginSessions session = loginSessionsMapper.selectHotakeSecurityLoginSessionsById(sessionId);
        if (session == null || !session.getUserId().equals(userId))
        {
            throw new ServiceException(MessageUtils.messageCustomize("HotakeSecurityServiceImpl10004"));
        }
        
        // TODO: 安全功能集成 - 从Redis中删除对应的token缓存
        // 这样可以立即使该会话失效
        // 使用RedisCache直接删除，避免依赖TokenService
        String userKey = CacheConstants.LOGIN_TOKEN_KEY + session.getSessionToken();
        redisCache.deleteObject(userKey);
        
        // 更新会话状态
        int result = loginSessionsMapper.terminateSession(sessionId, userId);
        
        // 记录安全日志
        recordSecurityLog(userId, "SESSION_TERMINATE", 
            MessageUtils.messageCustomize("HotakeSecurityServiceImpl10009") + "：" + session.getDeviceName(), 
            "SUCCESS");
        
        return result;
    }

    /**
     * 终止所有其他会话（除了当前会话）
     *
     * @return 结果
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public int terminateAllOtherSessions()
    {
        Long userId = SecurityUtils.getUserId();
        LoginUser loginUser = SecurityUtils.getLoginUser();
        String currentToken = loginUser.getToken();
        
        // TODO: 安全功能集成 - 从Redis中批量删除其他会话的token缓存
        // 查询所有其他活跃会话
        List<HotakeSecurityLoginSessions> otherSessions = loginSessionsMapper.selectActiveSessionsByUserId(userId);
        for (HotakeSecurityLoginSessions session : otherSessions)
        {
            if (!session.getSessionToken().equals(currentToken))
            {
                // 使用RedisCache直接删除，避免依赖TokenService
                String userKey = CacheConstants.LOGIN_TOKEN_KEY + session.getSessionToken();
                redisCache.deleteObject(userKey);
            }
        }
        
        // 更新会话状态
        int result = loginSessionsMapper.terminateOtherSessions(userId, currentToken);
        
        // 记录安全日志
        recordSecurityLog(userId, "SESSION_TERMINATE_ALL", 
            MessageUtils.messageCustomize("HotakeSecurityServiceImpl10010"), 
            "SUCCESS");
        
        return result;
    }

    /**
     * 记录登录会话
     *
     * @param userId 用户ID
     * @param sessionToken 会话令牌
     * @param ipAddress IP地址
     * @param userAgent 用户代理
     * @return 结果
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public int recordLoginSession(Long userId, String sessionToken, String ipAddress, String userAgent)
    {
        // 解析User-Agent
        UserAgent ua = UserAgent.parseUserAgentString(userAgent);
        Browser browser = ua.getBrowser();
        OperatingSystem os = ua.getOperatingSystem();
        
        // 获取地理位置
        String location = AddressUtils.getRealAddressByIP(ipAddress);
        String[] locationParts = location.split(" ");
        String country = locationParts.length > 0 ? locationParts[0] : "";
        String city = locationParts.length > 1 ? locationParts[1] : "";
        
        // 判断设备类型
        String deviceType = "Desktop";
        if (os.getDeviceType() != null)
        {
            deviceType = os.getDeviceType().getName();
        }
        
        // 创建会话记录
        HotakeSecurityLoginSessions session = new HotakeSecurityLoginSessions();
        session.setUserId(userId);
        session.setSessionId(sessionToken);
        session.setSessionToken(sessionToken);
        session.setDeviceType(deviceType);
        session.setDeviceName(browser.getName() + " / " + os.getName());
        session.setBrowser(browser.getName());
        session.setBrowserVersion(ua.getBrowserVersion() != null ? ua.getBrowserVersion().getVersion() : "");
        session.setOs(os.getName());
        session.setOsVersion("");
        session.setIpAddress(ipAddress);
        session.setLocationCountry(country);
        session.setLocationCity(city);
        session.setIsActive(1);
        session.setLoginTime(new Date());
        session.setLastActivityTime(new Date());
        
        // 计算过期时间
        Date expiresAt = new Date(System.currentTimeMillis() + expireTime * 60 * 1000L);
        session.setExpiresAt(expiresAt);
        
        fill(FillTypeEnum.INSERT.getCode(), session);
        return loginSessionsMapper.insertHotakeSecurityLoginSessions(session);
    }

    /**
     * 更新会话活动时间
     *
     * @param sessionToken 会话令牌
     * @return 结果
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public int updateSessionActivity(String sessionToken)
    {
        HotakeSecurityLoginSessions session = loginSessionsMapper.selectHotakeSecurityLoginSessionsByToken(sessionToken);
        if (session != null)
        {
            session.setLastActivityTime(new Date());
            fill(FillTypeEnum.UPDATE.getCode(), session);
            return loginSessionsMapper.updateHotakeSecurityLoginSessions(session);
        }
        return 0;
    }

    /**
     * 记录安全日志
     *
     * @param userId 用户ID
     * @param eventType 事件类型
     * @param eventDescription 事件描述
     * @param status 状态
     * @return 结果
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public int recordSecurityLog(Long userId, String eventType, String eventDescription, String status)
    {
        HotakeSecuritySecurityLogs log = new HotakeSecuritySecurityLogs();
        log.setUserId(userId);
        log.setEventType(eventType);
        log.setEventDescription(eventDescription);
        log.setIpAddress(IpUtils.getIpAddr());
        log.setUserAgent(StringUtils.substring(ServletUtils.getRequest().getHeader("User-Agent"), 0, 500));
        log.setLocation(AddressUtils.getRealAddressByIP(IpUtils.getIpAddr()));
        log.setStatus(status);
        
        fill(FillTypeEnum.INSERT.getCode(), log);
        return securityLogsMapper.insertHotakeSecuritySecurityLogs(log);
    }

    /**
     * 更新会话为已登出状态
     *
     * @param sessionToken 会话令牌
     * @return 结果
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public int updateSessionLogout(String sessionToken)
    {
        // 查询会话信息
        HotakeSecurityLoginSessions session = loginSessionsMapper.selectHotakeSecurityLoginSessionsByToken(sessionToken);
        if (session != null && session.getIsActive() == 1)
        {
            // 更新会话状态
            session.setIsActive(0);
            session.setLogoutTime(new Date());
            fill(FillTypeEnum.UPDATE.getCode(), session);
            
            // 记录安全日志
            recordSecurityLog(session.getUserId(), "LOGOUT", 
                MessageUtils.messageCustomize("HotakeSecurityServiceImpl10011"), "SUCCESS");
            
            return loginSessionsMapper.updateHotakeSecurityLoginSessions(session);
        }
        return 0;
    }

    /**
     * 获取当前用户的可信设备列表
     *
     * @return 可信设备列表
     */
    @Transactional(readOnly = true)
    @Override
    public List<SecurityTrustedDeviceVo> getTrustedDevices()
    {
        Long userId = SecurityUtils.getUserId();
        
        // 查询用户的可信设备
        List<HotakeSecurityTrustedDevices> devicesList = trustedDevicesMapper.selectTrustedDevicesByUserId(userId);
        
        // 获取当前设备标识（可以从请求头或Cookie中获取）
        String currentDeviceId = getCurrentDeviceId();
        
        // 转换为VO
        List<SecurityTrustedDeviceVo> voList = new ArrayList<>();
        for (HotakeSecurityTrustedDevices device : devicesList)
        {
            SecurityTrustedDeviceVo vo = new SecurityTrustedDeviceVo();
            vo.setId(device.getId());
            vo.setDeviceId(device.getDeviceId());
            vo.setDeviceName(device.getDeviceName());
            vo.setDeviceType(device.getDeviceType());
            vo.setBrowser(device.getBrowser());
            vo.setOs(device.getOs());
            vo.setIpAddress(device.getIpAddress());
            vo.setLocation(device.getLocation());
            vo.setTrustExpiresAt(device.getTrustExpiresAt());
            vo.setCreateTime(device.getCreateTime());
            vo.setIsCurrent(device.getDeviceId().equals(currentDeviceId));
            
            voList.add(vo);
        }
        
        return voList;
    }

    /**
     * 添加可信设备
     *
     * @param deviceId 设备唯一标识
     * @param deviceName 设备名称
     * @return 结果
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public int addTrustedDevice(String deviceId, String deviceName)
    {
        Long userId = SecurityUtils.getUserId();
        
        // 检查是否已存在
        HotakeSecurityTrustedDevices existDevice = trustedDevicesMapper.selectByUserIdAndDeviceId(userId, deviceId);
        if (existDevice != null)
        {
            // 已存在，更新信任状态
            existDevice.setIsTrusted(1);
            existDevice.setDeviceName(deviceName);
            // 设置信任过期时间（30天）
            existDevice.setTrustExpiresAt(new Date(System.currentTimeMillis() + 30L * 24 * 60 * 60 * 1000));
            fill(FillTypeEnum.UPDATE.getCode(), existDevice);
            
            recordSecurityLog(userId, "TRUSTED_DEVICE_UPDATE", 
                MessageUtils.messageCustomize("HotakeSecurityServiceImpl10012") + "：" + deviceName, "SUCCESS");
            
            return trustedDevicesMapper.updateHotakeSecurityTrustedDevices(existDevice);
        }
        
        // 解析User-Agent
        String userAgent = ServletUtils.getRequest().getHeader("User-Agent");
        UserAgent ua = UserAgent.parseUserAgentString(userAgent);
        Browser browser = ua.getBrowser();
        OperatingSystem os = ua.getOperatingSystem();
        
        // 获取IP和位置
        String ipAddress = IpUtils.getIpAddr();
        String location = AddressUtils.getRealAddressByIP(ipAddress);
        
        // 判断设备类型
        String deviceType = "Desktop";
        if (os.getDeviceType() != null)
        {
            deviceType = os.getDeviceType().getName();
        }
        
        // 创建新的可信设备记录
        HotakeSecurityTrustedDevices device = new HotakeSecurityTrustedDevices();
        device.setUserId(userId);
        device.setDeviceId(deviceId);
        device.setDeviceName(StringUtils.isNotEmpty(deviceName) ? deviceName : browser.getName() + " / " + os.getName());
        device.setDeviceType(deviceType);
        device.setBrowser(browser.getName());
        device.setOs(os.getName());
        device.setIpAddress(ipAddress);
        device.setLocation(location);
        device.setIsTrusted(1);
        // 设置信任过期时间（30天）
        device.setTrustExpiresAt(new Date(System.currentTimeMillis() + 30L * 24 * 60 * 60 * 1000));
        
        fill(FillTypeEnum.INSERT.getCode(), device);
        
        // 记录安全日志
        recordSecurityLog(userId, "TRUSTED_DEVICE_ADD", 
            MessageUtils.messageCustomize("HotakeSecurityServiceImpl10013") + "：" + device.getDeviceName(), "SUCCESS");
        
        return trustedDevicesMapper.insertHotakeSecurityTrustedDevices(device);
    }

    /**
     * 移除可信设备
     *
     * @param id 设备记录ID
     * @return 结果
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public int removeTrustedDevice(Long id)
    {
        Long userId = SecurityUtils.getUserId();
        
        // 查询设备信息
        HotakeSecurityTrustedDevices device = trustedDevicesMapper.selectHotakeSecurityTrustedDevicesById(id);
        if (device == null || !device.getUserId().equals(userId))
        {
            throw new ServiceException(MessageUtils.messageCustomize("HotakeSecurityServiceImpl10014"));
        }
        
        // 删除设备
        int result = trustedDevicesMapper.removeTrustedDevice(id, userId);
        
        // 记录安全日志
        recordSecurityLog(userId, "TRUSTED_DEVICE_REMOVE", 
            MessageUtils.messageCustomize("HotakeSecurityServiceImpl10015") + "：" + device.getDeviceName(), "SUCCESS");
        
        return result;
    }

    /**
     * 检查设备是否为可信设备
     *
     * @param userId 用户ID
     * @param deviceId 设备唯一标识
     * @return 是否可信
     */
    @Transactional(readOnly = true)
    @Override
    public boolean isTrustedDevice(Long userId, String deviceId)
    {
        if (StringUtils.isEmpty(deviceId))
        {
            return false;
        }
        
        HotakeSecurityTrustedDevices device = trustedDevicesMapper.selectByUserIdAndDeviceId(userId, deviceId);
        if (device == null || device.getIsTrusted() != 1)
        {
            return false;
        }
        
        // 检查是否过期
        if (device.getTrustExpiresAt() != null && device.getTrustExpiresAt().before(new Date()))
        {
            return false;
        }
        
        return true;
    }

    /**
     * 获取当前设备标识
     * 可以从请求头或Cookie中获取，这里简单实现
     */
    private String getCurrentDeviceId()
    {
        HttpServletRequest request = ServletUtils.getRequest();
        // 优先从请求头获取
        String deviceId = request.getHeader("X-Device-Id");
        if (StringUtils.isEmpty(deviceId))
        {
            // 从Cookie获取
            javax.servlet.http.Cookie[] cookies = request.getCookies();
            if (cookies != null)
            {
                for (javax.servlet.http.Cookie cookie : cookies)
                {
                    if ("device_id".equals(cookie.getName()))
                    {
                        deviceId = cookie.getValue();
                        break;
                    }
                }
            }
        }
        return deviceId;
    }
}
