package com.hertz.modules.system.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.hertz.common.exception.BusinessException;
import com.hertz.config.AppPathResolver;
import com.hertz.config.AppProperties;
import com.hertz.modules.system.dto.AuthDtos;
import com.hertz.modules.system.entity.SysRole;
import com.hertz.modules.system.entity.SysUser;
import com.hertz.modules.system.entity.SysUserRole;
import com.hertz.modules.system.mapper.SysRoleMapper;
import com.hertz.modules.system.mapper.SysUserMapper;
import com.hertz.modules.system.mapper.SysUserRoleMapper;
import com.hertz.modules.system.service.UserService;
import java.io.File;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class UserServiceImpl implements UserService {
  private final SysUserMapper userMapper;
  private final SysUserRoleMapper userRoleMapper;
  private final SysRoleMapper roleMapper;
  private final PasswordEncoder passwordEncoder;
  private final AppProperties appProperties;
  private final AppPathResolver pathResolver;

  public UserServiceImpl(
      SysUserMapper userMapper,
      SysUserRoleMapper userRoleMapper,
      SysRoleMapper roleMapper,
      PasswordEncoder passwordEncoder,
      AppProperties appProperties,
      AppPathResolver pathResolver
  ) {
    this.userMapper = userMapper;
    this.userRoleMapper = userRoleMapper;
    this.roleMapper = roleMapper;
    this.passwordEncoder = passwordEncoder;
    this.appProperties = appProperties;
    this.pathResolver = pathResolver;
  }

  @Override
  public SysUser register(String username, String rawPassword, String nickname) {
    var exists = userMapper.selectCount(new LambdaQueryWrapper<SysUser>()
        .eq(SysUser::getUsername, username)) > 0;
    if (exists) {
      throw new BusinessException(40002, "用户名已存在");
    }
    var user = new SysUser();
    user.setUsername(username);
    user.setPassword(passwordEncoder.encode(rawPassword));
    user.setNickname(nickname);
    user.setStatus(1);
    userMapper.insert(user);
    return user;
  }

  @Override
  public SysUser findByUsername(String username) {
    return userMapper.selectOne(new LambdaQueryWrapper<SysUser>()
        .eq(SysUser::getUsername, username)
        .last("LIMIT 1"));
  }

  @Override
  public IPage<SysUser> pageUsers(int page, int size, String keyword) {
    var wrapper = new LambdaQueryWrapper<SysUser>()
        .orderByDesc(SysUser::getId);
    if (keyword != null && !keyword.isBlank()) {
      wrapper.and(w -> w.like(SysUser::getUsername, keyword)
          .or().like(SysUser::getNickname, keyword)
          .or().like(SysUser::getPhone, keyword)
          .or().like(SysUser::getEmail, keyword));
    }
    return userMapper.selectPage(Page.of(page, size), wrapper);
  }

  @Override
  @Transactional
  public void createUser(SysUser user) {
    if (userMapper.selectCount(new LambdaQueryWrapper<SysUser>().eq(SysUser::getUsername, user.getUsername())) > 0) {
      throw new BusinessException(400, "用户名已存在");
    }
    user.setPassword(passwordEncoder.encode(user.getPassword()));
    user.setCreatedAt(java.time.LocalDateTime.now());
    user.setUpdatedAt(java.time.LocalDateTime.now());
    userMapper.insert(user);
  }

  @Override
  @Transactional
  public void updateUser(SysUser user) {
    var existing = userMapper.selectById(user.getId());
    if (existing == null) {
      throw new BusinessException(404, "用户不存在");
    }
    existing.setNickname(user.getNickname());
    existing.setPhone(user.getPhone());
    existing.setEmail(user.getEmail());
    existing.setGender(user.getGender());
    existing.setStatus(user.getStatus());
    String oldAvatarPath = existing.getAvatarPath();
    String newAvatarPath = user.getAvatarPath();
    existing.setAvatarPath(newAvatarPath);
    existing.setUpdatedAt(java.time.LocalDateTime.now());

    if (user.getPassword() != null && !user.getPassword().isBlank()) {
      existing.setPassword(passwordEncoder.encode(user.getPassword()));
    }

    userMapper.updateById(existing);
    deleteOldAvatarIfReplaced(oldAvatarPath, newAvatarPath);
  }

  @Override
  @Transactional
  public void updateProfile(Long userId, AuthDtos.UpdateProfileRequest req) {
    var existing = userMapper.selectById(userId);
    if (existing == null) {
      throw new BusinessException(404, "用户不存在");
    }
    existing.setNickname(req.nickname());
    String oldAvatarPath = existing.getAvatarPath();
    String newAvatarPath = oldAvatarPath;
    if (req.avatarPath() != null) {
        newAvatarPath = req.avatarPath();
        existing.setAvatarPath(req.avatarPath());
    }
    existing.setPhone(req.phone());
    existing.setEmail(req.email());
    existing.setGender(req.gender());
    existing.setUpdatedAt(java.time.LocalDateTime.now());
    userMapper.updateById(existing);
    deleteOldAvatarIfReplaced(oldAvatarPath, newAvatarPath);
  }

  @Override
  @Transactional
  public void updatePassword(Long userId, AuthDtos.UpdatePasswordRequest req) {
    var existing = userMapper.selectById(userId);
    if (existing == null) {
      throw new BusinessException(404, "用户不存在");
    }
    if (!passwordEncoder.matches(req.oldPassword(), existing.getPassword())) {
      throw new BusinessException(400, "旧密码错误");
    }
    existing.setPassword(passwordEncoder.encode(req.newPassword()));
    existing.setUpdatedAt(java.time.LocalDateTime.now());
    userMapper.updateById(existing);
  }

  @Override
  @Transactional
  public void deleteUser(Long id) {
    userMapper.deleteById(id);
    userRoleMapper.delete(new LambdaQueryWrapper<SysUserRole>().eq(SysUserRole::getUserId, id));
  }

  @Override
  @Transactional
  public void deleteUsers(List<Long> ids) {
    if (ids == null || ids.isEmpty()) {
      return;
    }
    userMapper.deleteByIds(ids);
    userRoleMapper.delete(new LambdaQueryWrapper<SysUserRole>().in(SysUserRole::getUserId, ids));
  }

  @Override
  @Transactional
  public void updateUserRoles(long userId, List<Long> roleIds) {
    userRoleMapper.delete(new LambdaQueryWrapper<SysUserRole>().eq(SysUserRole::getUserId, userId));
    if (roleIds == null || roleIds.isEmpty()) {
      return;
    }
    for (var roleId : roleIds.stream().filter(Objects::nonNull).distinct().collect(Collectors.toList())) {
      var ur = new SysUserRole();
      ur.setUserId(userId);
      ur.setRoleId(roleId);
      userRoleMapper.insert(ur);
    }
  }

  @Override
  public List<Long> getUserRoleIds(long userId) {
    var list = userRoleMapper.selectList(new LambdaQueryWrapper<SysUserRole>().eq(SysUserRole::getUserId, userId));
    return list.stream().map(SysUserRole::getRoleId).filter(Objects::nonNull).distinct().toList();
  }

  @Override
  public List<SysRole> getUserRoles(long userId) {
    return roleMapper.selectRolesByUserId(userId);
  }

  private void deleteOldAvatarIfReplaced(String oldAvatarPath, String newAvatarPath) {
    if (oldAvatarPath == null || oldAvatarPath.isBlank()) {
      return;
    }
    if (newAvatarPath == null || newAvatarPath.isBlank()) {
      return;
    }
    if (Objects.equals(oldAvatarPath, newAvatarPath)) {
      return;
    }

    String avatarPrefix = "/uploads/" + normalizeRelativePrefix(appProperties.getUpload().getAvatarPath());
    if (!oldAvatarPath.replace("\\", "/").startsWith(avatarPrefix)) {
      return;
    }

    String relative = oldAvatarPath.replace("\\", "/");
    if (!relative.startsWith("/uploads/")) {
      return;
    }
    relative = relative.substring("/uploads/".length());

    File root = new File(pathResolver.resolve(appProperties.getUpload().getRootPath()));
    File file = new File(root, relative.replace("/", File.separator));
    try {
      String rootCanonical = root.getCanonicalPath();
      String fileCanonical = file.getCanonicalPath();
      if (!fileCanonical.startsWith(rootCanonical)) {
        return;
      }
    } catch (Exception ignored) {
      return;
    }

    if (file.exists()) {
      file.delete();
    }
  }

  private String normalizeRelativePrefix(String path) {
    if (path == null) return "";
    String p = path.replace("\\", "/");
    if (!p.endsWith("/")) {
      p = p + "/";
    }
    return p;
  }
}

