package com.zsc.edu.gateway.modules.system.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zsc.edu.gateway.exception.ConstraintException;
import com.zsc.edu.gateway.framework.security.SecurityUtil;
import com.zsc.edu.gateway.framework.security.UserDetailsImpl;
import com.zsc.edu.gateway.modules.operationLog.entity.OperationLogAnnotation;
import com.zsc.edu.gateway.modules.system.dto.UserCreateDto;
import com.zsc.edu.gateway.modules.system.dto.UserSelfUpdateDto;
import com.zsc.edu.gateway.modules.system.dto.UserSelfUpdatePasswordDto;
import com.zsc.edu.gateway.modules.system.dto.UserUpdateDto;
import com.zsc.edu.gateway.modules.system.entity.*;
import com.zsc.edu.gateway.modules.system.mapper.UserMapper;
import com.zsc.edu.gateway.modules.system.repo.*;
import com.zsc.edu.gateway.modules.system.service.MenuService;
import com.zsc.edu.gateway.modules.system.service.UserService;
import com.zsc.edu.gateway.modules.system.utils.sendMail;
import lombok.AllArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author Yao
 * @since 2023-04-06
 */
@AllArgsConstructor
@Service
public class UserServiceImpl extends ServiceImpl<UserRepository, User> implements UserService {

    private final PasswordEncoder passwordEncoder;
    private final sendMail send;
    private final UserMapper userMapper;
    private final RoleRepository roleRepository;
    private final MenuRepository menuRepository;
    private final UserRolesRepository userRolesRepository;

    @Override
    @Transactional
    public Boolean create(UserCreateDto dto) {
        User user = new User();
        dto.setRoleId(dto.getRoleIds().get(0));
        userMapper.convert(dto, user);
        boolean existsByPhone = count(new LambdaQueryWrapper<User>().eq(User::getPhone, dto.getPhone())) > 0;
        boolean existsByEmail = count(new LambdaQueryWrapper<User>().eq(User::getEmail, dto.getEmail())) > 0;
        if (user.getPhone().equals(dto.getPhone()) && existsByPhone) {
            throw new ConstraintException("phone", dto.phone, "手机号已存在");
        }
        if (user.getEmail().equals(dto.getEmail()) && existsByEmail) {
            throw new ConstraintException("email", dto.email, "邮箱地址已存在");
        }
        boolean saveSuccess = save(user);
        if (!saveSuccess) {
            return false;
        }
        if (dto.getRoleIds() != null && !dto.getRoleIds().isEmpty()) {
            addUserRole(dto.getRoleIds(), user.getId());
        }
        return true;
    }
    @Override
    @Transactional
    public Boolean update(UserUpdateDto dto, Long id) {
        User user = getById(id);
        if (dto.getRoleIds() != null && !dto.getRoleIds().isEmpty()) {
            userRolesRepository.delete(new LambdaQueryWrapper<UserRole>().eq(UserRole::getUserId, id));
            dto.setRoleId(dto.getRoleIds().get(0));
            addUserRole(dto.getRoleIds(), user.getId());
        }
        BeanUtils.copyProperties(dto, user);
        return updateById(user);
    }

    @Override
    @Transactional
    public Boolean updatePassword(String password, Long id) {
        User user = getById(id);
        user.setPassword(passwordEncoder.encode(password));
        return updateById(user);
    }

    @Override
    @Transactional
    public Boolean toggle(Long id) {
        User user = getById(id);
        user.setEnableState(!user.getEnableState());
        return updateById(user);
    }

    @Override
    public String sendEmail(String email) {
        send.sendEmailMessage(email);
        return "发送成功";
    }


    @Override
    public User selfDetail(UserDetailsImpl userDetails) {
        return getById(userDetails.getId());
    }

    @Override
    @Transactional
    public Boolean selfUpdate(UserDetailsImpl userDetails, UserSelfUpdateDto dto) {
        User user = getById(userDetails.getId());
        boolean existsByPhone = count(new LambdaQueryWrapper<User>().ne(User::getId, userDetails.getId()).eq(User::getPhone, dto.getPhone())) > 0;
        if (existsByPhone) {
            throw new ConstraintException("phone", dto.phone, "手机号已存在");
        }
        boolean existsByEmail = count(new LambdaQueryWrapper<User>().ne(User::getId, userDetails.getId()).eq(User::getEmail, dto.getEmail())) > 0;
        if (existsByEmail) {
            throw new ConstraintException("email", dto.email, "邮箱地址已存在");
        }
        BeanUtils.copyProperties(dto, user);

        return updateById(user);
    }

    @Override
    @Transactional
    public Boolean selfUpdatePassword(UserDetailsImpl userDetails, UserSelfUpdatePasswordDto dto) {
        User user = getById(userDetails.getId());
        if (!passwordEncoder.matches(dto.oldPassword, user.password)) {
            throw new ConstraintException("旧密码不对");
        }
        user.setPassword(passwordEncoder.encode(dto.password));
        return updateById(user);
    }

    @Override
    @Transactional
    public UserDetailsImpl switchRole(Long roleId) {
        Role role = roleRepository.selectById(roleId);
        if (role == null) {
            throw new ConstraintException("角色不存在");
        }
        UserDetailsImpl userDetails = SecurityUtil.getUserInfo();
        boolean updated = lambdaUpdate().eq(User::getId, userDetails.getId())
                .set(User::getRoleId, roleId)
                .update();
        userDetails.setRole(role);
        if (updated) {
            Set<String> permissions = menuRepository.selectByRoleId(roleId).stream()
                    .map(Menu::getPermissions)
                    .collect(Collectors.toSet());
            userDetails.setPermissions(permissions);
            SecurityUtil.setUserInfo(userDetails);
        }
        return userDetails;
    }

    public Boolean addUserRole(List<Long> roleIds, Long userId) {
        List<UserRole> userRoles = roleIds.stream()
                .map(roleId -> {
                    UserRole userRole = new UserRole();
                    userRole.setUserId(userId);
                    userRole.setRoleId(roleId);
                    return userRole;
                })
                .collect(Collectors.toList());
        userRolesRepository.insert(userRoles);
        return true;
    }
}
