package com.agileboot.domain.system.user;

import cn.hutool.core.util.StrUtil;
import cn.hutool.core.convert.Convert;
import com.agileboot.common.core.page.PageDTO;
import com.agileboot.common.enums.common.ConfigKeyEnum;
import com.agileboot.common.enums.common.GenderEnum;
import com.agileboot.common.enums.common.UserStatusEnum;
import com.agileboot.common.exception.ApiException;
import com.agileboot.common.exception.error.ErrorCode;
import com.agileboot.domain.common.cache.CacheCenter;
import com.agileboot.domain.common.cache.GuavaCacheService;
import com.agileboot.domain.common.command.BulkOperationCommand;
import com.agileboot.domain.common.dto.CurrentLoginUserDTO;
import com.agileboot.domain.system.role.dto.RoleDTO;
import com.agileboot.domain.system.user.command.AddUserCommand;
import com.agileboot.domain.system.user.command.ChangeStatusCommand;
import com.agileboot.domain.system.user.command.RegisterUserCommand;
import com.agileboot.domain.system.user.command.ResetPasswordCommand;
import com.agileboot.domain.system.user.command.UpdateProfileCommand;
import com.agileboot.domain.system.user.command.UpdateUserAvatarCommand;
import com.agileboot.domain.system.user.command.UpdateUserCommand;
import com.agileboot.domain.system.user.command.UpdateUserPasswordCommand;
import com.agileboot.domain.system.user.db.SearchUserDO;
import com.agileboot.domain.system.user.dto.UserDTO;
import com.agileboot.domain.system.user.dto.UserDetailDTO;
import com.agileboot.domain.system.user.dto.UserProfileDTO;
import com.agileboot.domain.system.user.model.UserModel;
import com.agileboot.domain.system.user.model.UserModelFactory;
import com.agileboot.domain.system.user.query.SearchUserQuery;
import com.agileboot.infrastructure.user.web.SystemLoginUser;
import com.agileboot.domain.system.role.db.SysRoleEntity;
import com.agileboot.domain.system.user.db.SysUserEntity;
import com.agileboot.domain.system.role.db.SysRoleService;
import com.agileboot.domain.system.user.db.SysUserService;
import com.agileboot.infrastructure.user.AuthenticationUtils;
import com.agileboot.infrastructure.user.web.DataScopeEnum;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import java.util.List;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

/**
 * @author valarchie
 */
@Service
@RequiredArgsConstructor
public class UserApplicationService {

    private static final String DEFAULT_REGISTER_ROLE_KEY = "common";

    private final SysUserService userService;

    private final SysRoleService roleService;

    private final UserModelFactory userModelFactory;

    private final GuavaCacheService guavaCacheService;


    public PageDTO<UserDTO> getUserList(SearchUserQuery<SearchUserDO> query) {
        applyOnlySelfScope(query);
        Page<SearchUserDO> userPage = userService.getUserList(query);
        List<UserDTO> userDTOList = userPage.getRecords().stream().map(UserDTO::new).collect(Collectors.toList());
        return new PageDTO<>(userDTOList, userPage.getTotal());
    }

    public UserProfileDTO getUserProfile(Long userId) {

        SysUserEntity userEntity = userService.getById(userId);
        SysRoleEntity roleEntity = userService.getRoleOfUser(userId);

        return new UserProfileDTO(userEntity, roleEntity);
    }


    /**
     * 获取当前登录用户信息
     *
     * @return 当前登录用户信息
     */
    public CurrentLoginUserDTO getLoginUserInfo(SystemLoginUser loginUser) {
        CurrentLoginUserDTO permissionDTO = new CurrentLoginUserDTO();

        permissionDTO.setUserInfo(new UserDTO(CacheCenter.userCache.getObjectById(loginUser.getUserId())));
        permissionDTO.setRoleKey(loginUser.getRoleInfo().getRoleKey());
        permissionDTO.setPermissions(loginUser.getRoleInfo().getMenuPermissions());

        return permissionDTO;
    }


    public void updateUserProfile(UpdateProfileCommand command) {
        UserModel userModel = userModelFactory.loadById(command.getUserId());
        userModel.loadUpdateProfileCommand(command);

        userModel.checkPhoneNumberIsUnique();
        userModel.checkEmailIsUnique();

        userModel.updateById();

        CacheCenter.userCache.delete(userModel.getUserId());
    }

    public UserDetailDTO getUserDetailInfo(Long userId) {
        SysUserEntity userEntity = userService.getById(userId);
        UserDetailDTO detailDTO = new UserDetailDTO();

        LambdaQueryWrapper<SysRoleEntity> roleQuery = new LambdaQueryWrapper<SysRoleEntity>()
            .orderByAsc(SysRoleEntity::getRoleSort);
        List<RoleDTO> roleDtoList = roleService.list(roleQuery).stream().map(RoleDTO::new).collect(Collectors.toList());
        detailDTO.setRoleOptions(roleDtoList);

        if (userEntity != null) {
            detailDTO.setUser(new UserDTO(userEntity));
            detailDTO.setRoleId(userEntity.getRoleId());
        }
        return detailDTO;
    }

    public void addUser(AddUserCommand command) {
        UserModel model = userModelFactory.create();
        model.loadAddUserCommand(command);

        model.checkUsernameIsUnique();
        model.checkPhoneNumberIsUnique();
        model.checkEmailIsUnique();
        model.checkFieldRelatedEntityExist();
        model.resetPassword(command.getPassword());

        model.insert();
    }

    public void registerUser(RegisterUserCommand command) {
        checkRegisterIsOpen();
        checkRegisterPassword(command);

        UserModel model = userModelFactory.create();
        loadRegisterCommand(model, command);

        model.checkUsernameIsUnique();
        model.checkPhoneNumberIsUnique();
        model.checkEmailIsUnique();
        model.resetPassword(command.getPassword());
        model.insert();
    }

    public void updateUser(UpdateUserCommand command) {
        UserModel model = userModelFactory.loadById(command.getUserId());
        model.loadUpdateUserCommand(command);

        model.checkPhoneNumberIsUnique();
        model.checkEmailIsUnique();
        model.checkFieldRelatedEntityExist();
        model.updateById();

        CacheCenter.userCache.delete(model.getUserId());
    }

    public void deleteUsers(SystemLoginUser loginUser, BulkOperationCommand<Long> command) {
        for (Long id : command.getIds()) {
            UserModel userModel = userModelFactory.loadById(id);
            userModel.checkCanBeDelete(loginUser);
            userModel.deleteById();
        }
    }

    public void updatePasswordBySelf(SystemLoginUser loginUser, UpdateUserPasswordCommand command) {
        UserModel userModel = userModelFactory.loadById(command.getUserId());
        userModel.modifyPassword(command);
        userModel.updateById();

        CacheCenter.userCache.delete(userModel.getUserId());
    }

    public void resetUserPassword(ResetPasswordCommand command) {
        UserModel userModel = userModelFactory.loadById(command.getUserId());

        userModel.resetPassword(command.getPassword());
        userModel.updateById();

        CacheCenter.userCache.delete(userModel.getUserId());
    }

    public void changeUserStatus(ChangeStatusCommand command) {
        UserModel userModel = userModelFactory.loadById(command.getUserId());

        userModel.setStatus(Convert.toInt(command.getStatus()));
        userModel.updateById();

        CacheCenter.userCache.delete(userModel.getUserId());
    }

    public void updateUserAvatar(UpdateUserAvatarCommand command) {
        UserModel userModel = userModelFactory.loadById(command.getUserId());

        userModel.setAvatar(command.getAvatar());
        userModel.updateById();

        CacheCenter.userCache.delete(userModel.getUserId());
    }

    private void checkRegisterIsOpen() {
        Boolean isRegisterUserOn = Convert.toBool(guavaCacheService.configCache.get(ConfigKeyEnum.REGISTER.getValue()));
        if (!Boolean.TRUE.equals(isRegisterUserOn)) {
            throw new ApiException(ErrorCode.Business.USER_REGISTER_IS_CLOSED);
        }
    }

    private void checkRegisterPassword(RegisterUserCommand command) {
        if (!StrUtil.equals(command.getPassword(), command.getConfirmPassword())) {
            throw new ApiException(ErrorCode.Business.USER_REGISTER_PASSWORD_NOT_MATCH);
        }
    }

    private void applyOnlySelfScope(SearchUserQuery<SearchUserDO> query) {
        SystemLoginUser loginUser = AuthenticationUtils.getSystemLoginUser();
        if (loginUser == null || loginUser.getRoleInfo() == null) {
            return;
        }

        if (DataScopeEnum.ONLY_SELF.equals(loginUser.getRoleInfo().getDataScope())) {
            query.setUserId(loginUser.getUserId());
        }
    }

    private void loadRegisterCommand(UserModel model, RegisterUserCommand command) {
        SysRoleEntity role = getDefaultRegisterRole();
        model.setUsername(command.getUsername());
        model.setNickname(getRegisterNickname(command));
        model.setEmail(command.getEmail());
        model.setPhoneNumber(command.getPhoneNumber());
        model.setRoleId(role.getRoleId());
        model.setStatus(UserStatusEnum.NORMAL.getValue());
        model.setUserType(0);
        model.setSex(GenderEnum.UNKNOWN.getValue());
        model.setIsAdmin(false);
        model.setRemark("自助注册用户");
    }

    private SysRoleEntity getDefaultRegisterRole() {
        SysRoleEntity role = roleService.getEnabledRoleByKey(DEFAULT_REGISTER_ROLE_KEY);
        if (role == null) {
            throw new ApiException(ErrorCode.Business.COMMON_OBJECT_NOT_FOUND, DEFAULT_REGISTER_ROLE_KEY, "普通角色");
        }
        return role;
    }

    private String getRegisterNickname(RegisterUserCommand command) {
        if (StrUtil.isNotBlank(command.getNickname())) {
            return command.getNickname();
        }
        return command.getUsername();
    }


}
