package com.CUST.brain.service.impl;

import com.CUST.brain.common.annotation.RedisCache;
import com.CUST.brain.common.constant.RedisKeyConstants;
import com.CUST.brain.common.exception.BusinessException;
import com.CUST.brain.dao.domain.relation.SysUserRole;
import com.CUST.brain.dao.domain.system.SysPermission;
import com.CUST.brain.dao.domain.system.SysRole;
import com.CUST.brain.dao.domain.system.SysUser;
import com.CUST.brain.dao.mapper.SysUserMapper;
import com.CUST.brain.common.dto.system.LoginRequestDTO;
import com.CUST.brain.common.dto.system.LoginResponseDTO;
import com.CUST.brain.common.dto.system.UserInfoResponseDTO;
import com.CUST.brain.common.dto.system.MenuResponseDTO;
import com.CUST.brain.dao.mapper.SysUserRoleMapper;
import com.CUST.brain.service.SysPermissionService;
import com.CUST.brain.service.SysRoleService;
import com.CUST.brain.service.SysUserService;
import com.CUST.brain.common.utils.JwtTokenUtil;
import com.CUST.brain.common.utils.RedisUtils;
import com.CUST.brain.common.utils.PasswordEncoder;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

import com.CUST.brain.common.utils.CacheCleanUtils;

/**
 * 系统用户服务实现类
 */
@Service
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements SysUserService {

    private static final Logger log = LoggerFactory.getLogger(SysUserServiceImpl.class);

    @Resource
    private PasswordEncoder passwordEncoder;

    @Resource
    private JwtTokenUtil jwtTokenUtil;

    @Resource
    private RedisUtils redisUtils;

    @Resource
    private SysRoleService roleService;

    @Resource
    private SysPermissionService permissionService;

    @Resource
    private SysUserRoleMapper sysUserRoleMapper;

    @Resource
    private CacheCleanUtils cacheCleanUtils;

    @Autowired
    private ObjectMapper objectMapper;

    @Override
    public LoginResponseDTO login(LoginRequestDTO loginDTO) {
        log.debug("login接口开始");
        // 1. 根据用户名查询用户
        SysUser user = getByUsername(loginDTO.getUsername());
        log.debug("用户查找完成");
        if (user == null) {
            log.debug("用户不存在");
            throw new BusinessException(400, "用户名或密码错误");
        }
        
        log.debug("用户登录 - 用户名: {}, 数据库密码: {}", loginDTO.getUsername(), user.getPassword());
        
        // 2. 校验密码
        boolean matches = passwordEncoder.matches(loginDTO.getPassword(), user.getPassword());
        log.debug("密码匹配结果: {}", matches);
        if (!matches) {
            log.debug("密码不匹配");
            throw new BusinessException(400, "用户名或密码错误");
        }
        
        log.debug("准备获取角色...");
        List<SysRole> roles = roleService.getRolesByUserId(user.getUserId());
        if (roles == null || roles.isEmpty()) {
            log.debug("角色列表为空");
            throw new BusinessException(400, "账号未分配角色或者绑定角色被禁用无法登录，请联系管理员处理");
        }
        log.debug("角色获取完成，准备获取权限...");
        List<SysPermission> permissions = permissionService.getPermissionsByUserId(user.getUserId());
        // 4. 校验 loginType（优化：提取为单独方法，增加日志，异常信息更明确）
        Integer requestedLoginType = loginDTO.getLoginType();
        if (!isLoginTypeAllowed(requestedLoginType, roles)) {
            log.debug("loginType 校验不通过，禁止登录");
            throw new BusinessException(400, "该账号不支持此端登录");
        }

        log.debug("loginType 校验通过，准备生成token...");
        // 3. 生成token
        String token = jwtTokenUtil.generateToken(user.getUserId(), user.getUsername());
        log.debug("token生成完成，准备写入Redis...");
        // 4. 将token存入Redis，key为user:token:<userId>，value为token字符串
        String userKey = String.format(RedisKeyConstants.USER_TOKEN, user.getUserId());
        redisUtils.set(userKey, token, 24 * 60 * 60); // 24小时过期
        log.debug("token写入Redis完成，准备构建返回结果...");
        // 5. 构建返回结果（提取为单独方法，减少重复代码）
        LoginResponseDTO response = buildLoginResponse(user, roles, permissions, token);
        log.debug("login接口结束");
        return response;
    }

    // 修改：loginType 为 Integer 类型，例如 1 表示前台、2 表示后台
    private boolean isLoginTypeAllowed(Integer requestedLoginType, List<SysRole> roles) {
        log.debug("校验 loginType: requestedLoginType={}, roles={}", requestedLoginType, roles);
        // 如果角色 loginType 为 2，则前台和后台均可登录；如果为 1，则仅前台可登录
        boolean isAllowed = roles.stream().anyMatch(role -> {
            Integer roleLoginType = role.getLoginType();
            return Integer.valueOf(2).equals(roleLoginType) || (Integer.valueOf(1).equals(roleLoginType) && Integer.valueOf(1).equals(requestedLoginType));
        });
        log.debug("loginType 校验结果: isAllowed={}", isAllowed);
        return isAllowed;
    }

    // 单独构建返回结果的方法（不变）
    private LoginResponseDTO buildLoginResponse(SysUser user, List<SysRole> roles, List<SysPermission> permissions, String token) {
        LoginResponseDTO response = new LoginResponseDTO();
        response.setUserId(user.getUserId());
        response.setUsername(user.getUsername());
        response.setRealName(user.getRealName());
        response.setToken(token);
        response.setRoles(roles.stream().map(SysRole::getRoleCode).collect(Collectors.toList()));
        response.setPermissions(permissions.stream().map(SysPermission::getPermissionCode).collect(Collectors.toList()));
        return response;
    }

    @Override
    public void logout(String token, String userId) {
        // 1. 删除Redis中的用户信息
        String userKey = RedisKeyConstants.USER_TOKEN + token;
        redisUtils.delete(userKey);
        
        // 2. 清理用户相关缓存
        cacheCleanUtils.clearUserCache(userId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean addUser(SysUser user) {
        // 1. 检查用户名是否已存在
        if (getByUsername(user.getUsername()) != null) {
            throw new BusinessException(400, "用户名已存在");
        }
        // 2. 加密密码
        user.setPassword(passwordEncoder.encode(user.getPassword()));
        // 3. 保存用户
        boolean saved = save(user);
        if (!saved) {
            throw new BusinessException(500, "用户创建失败请稍后再试");
        }
        // 4. 自动分配USER基础角色
        SysRole userRole = roleService.getByRoleCode("USER");
        if (userRole == null) {
            throw new BusinessException(400, "基础角色USER不存在，请联系管理员");
        }
        SysUserRole userRoleRel = new SysUserRole();
        userRoleRel.setUserId(user.getId() != null ? user.getId() : user.getUserId());
        userRoleRel.setRoleId(userRole.getId());
        sysUserRoleMapper.insert(userRoleRel);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateUser(SysUser user) {
        // 只在密码不为空时加密更新，否则不动原密码
        if (user.getPassword() != null && !user.getPassword().isEmpty()) {
            user.setPassword(passwordEncoder.encode(user.getPassword()));
        } else {
            user.setPassword(null); // 防止被覆盖
        }
        boolean result = updateById(user);
        if (result) {
            cacheCleanUtils.clearUserCache(user.getUserId());
        }
        return result;
    }

    @Override
//    @RedisCache(key = "'sys:user:' + #p0")
    public SysUser getByUsername(String username) {
        LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SysUser::getUsername, username)
                .eq(SysUser::getFlag, 0);
        return getOne(wrapper);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateStatus(String id, Integer status) {
        SysUser user = new SysUser();
        user.setId(id);
        user.setStatus(status);
        boolean result = updateById(user);
        if (result) {
            // 删除用户缓存
            redisUtils.delete(RedisKeyConstants.SYS_USER + getById(id).getUsername());
        }
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updatePassword(String id, String oldPassword, String newPassword) {
        SysUser user = getById(id);
        if (user == null || !passwordEncoder.matches(oldPassword, user.getPassword())) {
            return false;
        }

        user.setPassword(passwordEncoder.encode(newPassword));
        boolean result = updateById(user);
        if (result) {
            // 删除用户缓存
            redisUtils.delete(RedisKeyConstants.SYS_USER + user.getUsername());
        }
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String resetPassword(String id) {
        // 生成8位随机密码
        String newPassword = UUID.randomUUID().toString().substring(0, 8);
        SysUser user = new SysUser();
        user.setId(id);
        user.setPassword(passwordEncoder.encode(newPassword));
        boolean result = updateById(user);
        if (result) {
            // 删除用户缓存
            redisUtils.delete(RedisKeyConstants.SYS_USER + getById(id).getUsername());
            return newPassword;
        }
        return null;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateLoginInfo(String id, String ip) {
        SysUser user = new SysUser();
        user.setId(id);
        user.setLastLoginTime(LocalDateTime.now());
        user.setLastLoginIp(ip);
        boolean result = updateById(user);
        if (result) {
            // 删除用户缓存
            redisUtils.delete(RedisKeyConstants.SYS_USER + getById(id).getUsername());
        }
        return result;
    }

    @Override
    @RedisCache(key = "'user:info:' + #p0")
    public UserInfoResponseDTO getUserInfo(String userId) {
        log.debug("【getUserInfo】开始获取用户信息，userId={}", userId);
        // 1. 获取用户基本信息
        SysUser user = getById(userId);
        if (user == null) {
            log.warn("【getUserInfo】用户不存在，userId={}", userId);
            throw new BusinessException(400, "用户不存在");
        }
        log.debug("【getUserInfo】获取到用户基本信息，userId={}, username={}", userId, user.getUsername());
        
        // 2. 获取用户角色
        List<SysRole> roles = roleService.getRolesByUserId(userId);
        log.debug("【getUserInfo】获取到用户角色，userId={}, roles={}", userId, roles);
        
        // 3. 获取用户权限
        List<SysPermission> permissions = permissionService.getPermissionsByUserId(userId);
        log.debug("【getUserInfo】获取到用户权限，userId={}, permissions={}", userId, permissions);
        
        // 4. 获取用户菜单
        List<MenuResponseDTO> menus = buildUserMenus(permissions);
        log.debug("【getUserInfo】构建用户菜单完成，userId={}, menus={}", userId, menus);
        
        // 5. 构建返回结果
        UserInfoResponseDTO userInfo = new UserInfoResponseDTO();
        BeanUtils.copyProperties(user, userInfo);
        userInfo.setUserId(user.getUserId());
        userInfo.setRoles(roles.stream().map(SysRole::getRoleCode).collect(Collectors.toList()));
        userInfo.setPermissions(permissions.stream().map(SysPermission::getPermissionCode).collect(Collectors.toList()));
        userInfo.setMenus(menus);
        
        log.debug("【getUserInfo】用户信息获取完成，userId={}, userInfo={}", userId, userInfo);
        return userInfo;
    }

    /**
     * 构建用户菜单树
     */
    private List<MenuResponseDTO> buildUserMenus(List<SysPermission> permissions) {
        // 1. 过滤出菜单类型的权限
        List<SysPermission> menuPermissions = permissions.stream()
                .filter(p -> p.getPermissionType() != null && p.getPermissionType() <= 2) // 只取菜单类型
                .collect(Collectors.toList());
        
        // 2. 转换为菜单DTO
        List<MenuResponseDTO> menuList = menuPermissions.stream().map(p -> {
            MenuResponseDTO menu = new MenuResponseDTO();
            menu.setId(p.getId());
            menu.setParentId(p.getParentId());
            menu.setName(p.getPermissionName());
            menu.setCode(p.getPermissionCode());
            menu.setPath(p.getPath());
            menu.setIcon(p.getIcon());
            menu.setType(p.getPermissionType());
            menu.setSort(p.getSort());
            return menu;
        }).collect(Collectors.toList());
        
        // 3. 构建菜单树
        List<MenuResponseDTO> rootMenus = menuList.stream()
                .filter(m -> m.getParentId() == null || "0".equals(m.getParentId()))
                .collect(Collectors.toList());
        
        rootMenus.forEach(menu -> buildMenuTree(menu, menuList));
        
        return rootMenus;
    }

    /**
     * 递归构建菜单树
     */
    private void buildMenuTree(MenuResponseDTO parent, List<MenuResponseDTO> allMenus) {
        List<MenuResponseDTO> children = allMenus.stream()
                .filter(m -> parent.getId().equals(m.getParentId()))
                .collect(Collectors.toList());
        
        if (!children.isEmpty()) {
            parent.setChildren(children);
            children.forEach(child -> buildMenuTree(child, allMenus));
        } else {
            parent.setChildren(new ArrayList<>());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void assignRoles(String userId, List<String> roleIds) {
        // 1. 删除原有角色
        LambdaQueryWrapper<SysUserRole> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SysUserRole::getUserId, userId);
        sysUserRoleMapper.delete(wrapper);

        // 2. 批量插入新角色
        if (roleIds != null && !roleIds.isEmpty()) {
            List<SysUserRole> userRoles = roleIds.stream()
                    .map(roleId -> {
                        SysUserRole ur = new SysUserRole();
                        ur.setUserId(userId);
                        ur.setRoleId(roleId);
                        return ur;
                    }).collect(Collectors.toList());
            userRoles.forEach(sysUserRoleMapper::insert);
        }

        // 3. 清理用户相关缓存
        cacheCleanUtils.clearUserCache(userId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteUser(String userId) {
        boolean result = removeById(userId);
        if (result) {
            cacheCleanUtils.clearUserCache(userId);
        }
        return result;
    }
} 