package com.agileboot.system.user.service.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.BCrypt;
import com.agileboot.common.core.enums.common.UserStatusEnum;
import com.agileboot.common.core.exception.BizException;
import com.agileboot.common.core.exception.error.ErrorCode;
import com.agileboot.common.mybatis.core.page.PageQuery;
import com.agileboot.common.satoken.pojo.LoginUser;
import com.agileboot.common.satoken.utils.LoginHelper;
import com.agileboot.system.user.dto.*;
import com.agileboot.system.user.entity.SysUser;
import com.agileboot.system.user.mapper.SysUserMapper;
import com.agileboot.system.user.service.ISysUserService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

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

@Service
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements ISysUserService {

    @Override
    public LoginUser getUserInfo(String username) {
        SysUser sysUser = super.baseMapper.selectOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getUsername, username));
        if (ObjectUtil.isNull(sysUser)) {
            throw new BizException("user.not.exists", username);
        }
        if (UserStatusEnum.DISABLED.getValue().equals(sysUser.getStatus())) {
            throw new BizException("user.blocked", username);
        } else if (UserStatusEnum.FROZEN.getValue().equals(sysUser.getStatus())) {
            throw new BizException("user.freeze", username);
        } else if (!UserStatusEnum.NORMAL.getValue().equals(sysUser.getStatus())) {
            throw new BizException("user.status.error", username);
        }
        LoginUser loginUser = new LoginUser();
        loginUser.setUserId(sysUser.getUserId());
        loginUser.setDeptId(sysUser.getDeptId());
        loginUser.setPassword(sysUser.getPassword());
        loginUser.setUserType(sysUser.getUserType());
        loginUser.setUsername(sysUser.getUsername());
        loginUser.setNickname(sysUser.getNickname());
        loginUser.setPassword(sysUser.getPassword());
        loginUser.setIsAdmin(sysUser.getIsAdmin());
        loginUser.setRoleId(sysUser.getRoleId());
        loginUser.setClientId(sysUser.getClientId());
        loginUser.setStatus(sysUser.getStatus());

        return loginUser;
    }

    @Override
    public boolean registerUserInfo(SysUser sysUser) {
//        if (!("true".equals(configService.selectConfigByKey("sys.account.registerUser")))) {
//            throw new BizException("当前系统没有开启注册功能");
//        }
        if (super.baseMapper.exists(new LambdaQueryWrapper<SysUser>().eq(SysUser::getUsername, sysUser.getUsername()))) {
            throw new BizException("user.register.save.error", sysUser.getUsername());
        }
        sysUser.setCreateBy(0L);
        sysUser.setUpdateBy(0L);
        return baseMapper.insert(sysUser) > 0;
    }


    // --------------------------------------

    @Override
    public IPage<UserInfo> getUserList(SearchUserQuery query) {

        String username = query.getUsername();
        String phoneNumber = query.getPhoneNumber();
        Long userId = query.getUserId();
        Integer status = query.getStatus();
        Long deptId = query.getDeptId();

        QueryWrapper<SysUser> queryWrapper = new QueryWrapper<>();
        queryWrapper.like(StrUtil.isNotEmpty(username), "username", username)
                .like(StrUtil.isNotEmpty(phoneNumber), "u.phone_number", phoneNumber)
                .eq(userId != null, "u.user_id", userId)
                .eq(status != null, "u.status", status)
                .eq("u.deleted", 0)
                .and(deptId != null, o ->
                        o.eq("u.dept_id", deptId)
                                .or()
                                .apply("u.dept_id IN ( SELECT t.dept_id FROM sys_dept t WHERE find_in_set(" + deptId
                                        + ", ancestors))"));
        query.setOrderByColumn("u.create_time");
        Page<SysUser> userPage = baseMapper.getUserList(query.toPage(), queryWrapper);
        IPage<UserInfo> convert = userPage.convert(UserInfo::new);
        return convert;
    }

    @Override
    public void addUser(AddUserCommand command) {
        SysUser entity = new SysUser();
        if (command == null) {
            throw new BizException("user is null");
        }
        BeanUtils.copyProperties(command, entity, "userId");

        LambdaQueryWrapper<SysUser> isUserNameDuplicated = Wrappers.lambdaQuery(SysUser.class)
                .eq(SysUser::getUsername, command.getUsername());
        if (this.baseMapper.exists(isUserNameDuplicated)) {
            throw new BizException(ErrorCode.Business.USER_NAME_IS_NOT_UNIQUE);
        }
        Long userId = LoginHelper.getUserId();
        LambdaQueryWrapper<SysUser> isPhoneDuplicated = Wrappers.lambdaQuery(SysUser.class)
                .ne(userId != null, SysUser::getUserId, userId)
                .eq(SysUser::getPhoneNumber, command.getPhoneNumber());
        if (StringUtils.isNotBlank(command.getPhoneNumber()) && baseMapper.exists(isPhoneDuplicated)) {
            throw new BizException(ErrorCode.Business.USER_PHONE_NUMBER_IS_NOT_UNIQUE);
        }
        LambdaQueryWrapper<SysUser> isEmailDuplicated = Wrappers.lambdaQuery(SysUser.class)
                .ne(userId != null, SysUser::getUserId, userId)
                .eq(SysUser::getEmail, command.getEmail());
        if (StringUtils.isNotBlank(command.getEmail()) && baseMapper.exists(isEmailDuplicated)) {
            throw new BizException(ErrorCode.Business.USER_EMAIL_IS_NOT_UNIQUE);
        }
        // password encrypt
        entity.setPassword(command.getPassword());
        entity.setClientId(command.getClientId());
        super.baseMapper.insert(entity);
    }

    @Override
    public UserDetailDTO getUserDetailInfo(Long userId) {
        return null;
    }

    @Override
    public void updateUser(UpdateUserCommand command) {

    }

    @Override
    public void resetUserPassword(ResetPasswordCommand command) {
//        if (!LoginHelper.isSuperAdmin() || !LoginHelper.isTenantAdmin()) {
//            throw new BizException("permission.denied");
//        }
        SysUser sysUser = new SysUser();
        String hashpw = BCrypt.hashpw(command.getPassword());
        sysUser.setPassword(command.getPassword());
        sysUser.setUserId(command.getUserId());
        super.updateById(sysUser);
    }

    @Override
    public void resetUserPasswordByAdmin(ResetPasswordCommand command) {
        SysUser sysUser = new SysUser();
        String hashpw = BCrypt.hashpw(command.getPassword());
        sysUser.setPassword(command.getPassword());
        sysUser.setUserId(LoginHelper.getUserId());
        super.updateById(sysUser);
    }

    @Override
    public void changeUserStatus(ChangeStatusCommand command) {
        SysUser sysUser = new SysUser();
        sysUser.setStatus(command.getStatus());
        sysUser.setUserId(command.getUserId());
        super.updateById(sysUser);
    }

    @Override
    public void deleteUsers(Set<Long> longs) {
//        if (!LoginHelper.isSuperAdmin() || !LoginHelper.isTenantAdmin()) {
//            throw new BizException("permission.denied");
//        }
        super.removeByIds(longs);
    }

    @Override
    public Page<SysUser> getUserListByRole(PageQuery<?> query) {
        return baseMapper.getUserListByRole(query.toPage(), query.toQueryWrapper());
    }

    @Override
    public boolean isAssignedToUsers(Long roleId) {
        return super.exists(Wrappers.lambdaQuery(SysUser.class).eq(SysUser::getRoleId, roleId));
    }

    @Override
    public List<SysUser> assignedToUsersByRoleIds(List<Long> collect) {
        if (collect.isEmpty()) {
            return List.of();
        }
        return super.list(Wrappers.lambdaQuery(SysUser.class).in(SysUser::getRoleId, collect));
    }

    @Override
    public void updateBatchRoleId(Long roleId, List<Long> userIds) {
        for (Long userId : userIds) {
            SysUser sysUser = super.getById(userId);
            if (sysUser == null) {
                throw new BizException(ErrorCode.Business.COMMON_OBJECT_NOT_FOUND, userId, "用户");
            }
            sysUser.setRoleId(roleId);
            if (sysUser.getIsAdmin() == 1) {
                throw new BizException(ErrorCode.Business.USER_ADMIN_CAN_NOT_BE_MODIFY);
            }
            super.updateById(sysUser);
        }
    }

    @Override
    public void deleteBatchRoleId(List<Long> userIds) {
        LambdaUpdateWrapper<SysUser> updateWrapper = Wrappers.lambdaUpdate(SysUser.class)
                .set(SysUser::getRoleId, null)
                .in(SysUser::getUserId, userIds);
        super.update(updateWrapper);
    }

    @Override
    public void checkDeptAssignedToUsers(Long deptId) {
        LambdaQueryWrapper<SysUser> queryWrapper = Wrappers.lambdaQuery(SysUser.class)
                .eq(SysUser::getDeptId, deptId);
        if (super.exists(queryWrapper)) {
            throw new BizException(ErrorCode.Business.DEPT_EXIST_LINK_USER_NOT_ALLOW_DELETE);
        }
    }

    /**
     * 检测职位是否分配给用户
     */
    @Override
    public void checkAnyPostIsAssignedToUser(List<Long> ids) {
        LambdaQueryWrapper<SysUser> queryWrapper = Wrappers.lambdaQuery(SysUser.class)
                .in(SysUser::getPostId, ids);
        if (super.exists(queryWrapper)) {
            throw new BizException(ErrorCode.Business.POST_ALREADY_ASSIGNED_TO_USER_CAN_NOT_BE_DELETED);
        }
    }

    @Override
    public Map<Long, String> geIdNameByIds(Set<Long> userIds) {
        if (userIds.isEmpty()) {
            return Map.of();
        }
        return super.list(Wrappers.lambdaQuery(SysUser.class)
                        .select(SysUser::getUserId, SysUser::getUsername)
                        .in(SysUser::getUserId, userIds))
                .stream()
                .collect(Collectors.toMap(SysUser::getUserId, SysUser::getUsername));
    }
}
