package com.jwipc.sys.logic.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Pair;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.jwipc.constant.FlagConst;
import com.jwipc.constant.SysConfigKey;
import com.jwipc.exception.DaoException;
import com.jwipc.page.PageUtil;
import com.jwipc.page.PageView;
import com.jwipc.prop.SysProp;
import com.jwipc.security.LoginUtil;
import com.jwipc.supplier.client.SupplierClient;
import com.jwipc.sys.constant.UserType;
import com.jwipc.sys.convert.PurchaseGroupConvert;
import com.jwipc.sys.convert.RoleConvert;
import com.jwipc.sys.convert.UserConvert;
import com.jwipc.sys.convert.UserPurchaseGroupConvert;
import com.jwipc.sys.dto.*;
import com.jwipc.sys.entity.*;
import com.jwipc.sys.logic.SysConfigLogic;
import com.jwipc.sys.logic.UserLogic;
import com.jwipc.sys.model.query.UserListQuery;
import com.jwipc.sys.service.*;
import com.jwipc.util.PasswordUtilSHA256;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;


/**
 * <p> </p>
 *
 * @author BOGON
 * @version 1.0
 * @since JDK 1.8
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class UserLogicImpl implements UserLogic {
    private final IUserService userService;
    private final UserConvert userConvert;
    private final IRoleService roleService;
    private final IPurchaseGroupService purchaseGroupService;
    private final RoleConvert roleConvert;
    private final PurchaseGroupConvert purchaseGroupConvert;
    private final SysProp sysProp;
    private final IUserRoleService userRoleService;
    private final IUserOrgService userOrgService;
    private final IUserPurchaseGroupService userPurchaseGroupService;
    private final UserPurchaseGroupConvert userPurchaseGroupConvert;
    private final IUserFactoryService userFactoryService;
    private final IUserPurchaseOrgService userPurchaseOrgService;
    private final SupplierClient supplierClient;
    private final SysConfigLogic sysConfigLogic;

    @Override
    public UserPageAttrDTO renderHanlder() {
        final List<PurchaseGroup> purchaseGroups = purchaseGroupService.list();
        final List<Role> roleRes = roleService.list();
        final UserPageAttrDTO.UserPageAttrDTOBuilder builder = UserPageAttrDTO.builder();
        builder.roles(roleConvert.entitis2DTOS(roleRes));
        builder.purchaseGroups(purchaseGroupConvert.po2PageListDTO(purchaseGroups));
        return builder.build();
    }

    @Override
    public PageView findByPage(UserListQuery query) {
        final Page<User> dbPage = PageUtil.toPage(query);
        //需要根据角色查询角色ID
        if (StrUtil.isNotEmpty(query.getRoleCodes())) {
            final List<String> split = StrUtil.split(query.getRoleCodes(), StrUtil.COMMA);
            final List<Role> roles = roleService.list(
                    new LambdaQueryWrapper<Role>().in(Role::getRolekey, split));
            if (CollUtil.isNotEmpty(roles)) {
                query.setRole(roles.stream().map(Role::getId).map(String::valueOf)
                        .collect(Collectors.joining(StrUtil.COMMA)));
            }
        }
        List<User> users = userService.findPageByQuery(dbPage, query);
        //需要查询用户分配的角色
        final List<Role> list = roleService.list();
        final Map<Long, String> roleMap = list.stream().collect(Collectors.toMap(Role::getId, Role::getName));
        final List<Long> userIds = users.stream().map(User::getId).collect(Collectors.toSet()).stream()
                .toList();
        Map<Long, List<Long>> b = Maps.newHashMap();
        if (CollUtil.isNotEmpty(userIds)) {
            final List<UserRole> userRoles = userRoleService.list(
                    new LambdaQueryWrapper<UserRole>().in(UserRole::getUserId, userIds));
            b = userRoles.stream()
                    .collect(Collectors.groupingBy(UserRole::getUserId,
                            Collectors.mapping(UserRole::getRoleId, Collectors.toList())));
        }


        List<UserPageListDTO> userPageListDTOS = userConvert.entities2PageListDTO(users);
        for (UserPageListDTO userPageListDTO : userPageListDTOS) {
            final Long userId = userPageListDTO.getId();
            final List<Long> roleIds = b.get(userId);
            if (CollUtil.isEmpty(roleIds)) {
                continue;
            }
            Set<String> roleNames = Sets.newConcurrentHashSet();
            for (Long roleId : roleIds) {
                roleNames.add(roleMap.get(roleId));
            }
            userPageListDTO.setDispatchRoles(roleNames.stream().collect(Collectors.joining(StrUtil.COMMA)));
        }
        return PageUtil.getPV(userPageListDTOS, dbPage.getTotal());
    }

    @Override
    public List<UserPageListDTO> findForExport(UserListQuery query) {
        List<User> users = userService.findPageByQuery(query);
        return userConvert.entities2PageListDTO(users);
    }

    @Override
    public List<UserPageListDTO> listAll(UserListQuery query) {
        List<User> users = userService.findPageByQuery(null, query);
        return userConvert.entities2PageListDTO(users);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void add(UserAddDTO userAddDTO) {
        validateAccountName(userAddDTO.getAccountName());
        User user = userConvert.addDto2Entity(userAddDTO);
        String md5Password = sysProp.getDefaultPassword();
        user.setCreateTime(LocalDateTime.now());
        setPassword(user, md5Password);
        if (Objects.nonNull(user.getSupplierId())) {
            user.setUserType(UserType.SUPPLIER.getType());
        }
        userService.save(user);
    }

    private void validateAccountName(String accountName) {
        //查询账号是否存在
        final LambdaQueryWrapper<User> eq = new LambdaQueryWrapper<User>().eq(User::getAccountName, accountName);
        final User one = userService.getOne(eq);
        if (Objects.nonNull(one)) {
            throw new DaoException("用户已存在!");
        }
    }

    @Override
    public UserEditPageDTO getForEditPage(Long id) {
        final User user = userService.getById(id);
        //如果是供应商用户，需要取供应商的名称
        UserEditPageDTO pageDTO = userConvert.entity2EditPageDTO(user);
        if (user.getUserType() == UserType.SUPPLIER.getType()) {
            final String supplierName = supplierClient.getSupplierName(user.getSupplierId());
            pageDTO.setSupplierName(supplierName);
        }

        return pageDTO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(UserEditDTO userEditDTO) {
        User user = userConvert.editDTO2entity(userEditDTO);
        userService.updateById(user);
    }

    @Override
    public Pair<List<RoleDTO>, List<RoleDTO>> getForUserRoleAdd(Long userId) {
        final LambdaQueryWrapper<UserRole> userRoleLambdaQueryWrapper = new LambdaQueryWrapper<UserRole>().eq(
                UserRole::getUserId, userId);
        final List<UserRole> list = userRoleService.list(userRoleLambdaQueryWrapper);
        final List<Role> allRoles = roleService.list();
        //需要需要处理一下当前用户有的角色
        final List<Long> userRoleIds = list.stream().map(UserRole::getRoleId).toList();
        List<Role> userRoles = allRoles.stream().filter(a -> userRoleIds.contains(a.getId()))
                .collect(Collectors.toList());
        List<Role> notUserRoles = allRoles.stream().filter(a -> !userRoleIds.contains(a.getId()))
                .collect(Collectors.toList());
        return Pair.of(roleConvert.entitis2DTOS(userRoles), roleConvert.entitis2DTOS(notUserRoles));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateRoleRel(Long userId, String roleIds) {
        final LambdaUpdateWrapper<UserRole> userRoleLambdaUpdateWrapper = new LambdaUpdateWrapper<UserRole>().eq(
                UserRole::getUserId, userId);
        userRoleService.remove(userRoleLambdaUpdateWrapper);
        final List<String> split = StrUtil.split(roleIds, StrUtil.COMMA);
        List<UserRole> insertList = Lists.newArrayList();
        for (String roleId : split) {
            UserRole userRole = new UserRole();
            userRole.setUserId(userId);
            userRole.setRoleId(Long.valueOf(roleId));
            insertList.add(userRole);
        }
        if (CollUtil.isNotEmpty(insertList)) {
            userRoleService.saveBatch(insertList, 300);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateOrgRel(Long userId, String orgIds) {
        final LambdaUpdateWrapper<UserOrg> userOrgLambdaUpdateWrapper = new LambdaUpdateWrapper<UserOrg>().eq(
                UserOrg::getUserId, userId);
        userOrgService.remove(userOrgLambdaUpdateWrapper);
        List<UserOrg> userOrgs = Lists.newArrayList();
        for (String orgId : orgIds.split(StrUtil.COMMA)) {
            UserOrg userOrg = new UserOrg();
            userOrg.setOrgId(orgId);
            userOrg.setUserId(userId);
            userOrgs.add(userOrg);
        }
        if (CollUtil.isNotEmpty(userOrgs)) {
            userOrgService.saveBatch(userOrgs);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updatePurchaseGroupRel(List<UserPurchaseGroupUpdateDTO> purchaseGroupUpdateDTOS) {
        final Long loginUserId = LoginUtil.getCurrentUser().getUserId();
        List<UserPurchaseGroup> insertList = userPurchaseGroupConvert.updateDTOS2entities(purchaseGroupUpdateDTOS);
        if (CollUtil.isNotEmpty(insertList)) {
            final Long userId = insertList.get(0).getUserId();
            final LambdaUpdateWrapper<UserPurchaseGroup> updateWrapper =
                    new LambdaUpdateWrapper<UserPurchaseGroup>().eq(
                            UserPurchaseGroup::getUserId, userId);
            userPurchaseGroupService.remove(updateWrapper);
            for (UserPurchaseGroup userPurchaseGroup : insertList) {
                userPurchaseGroup.setCreateId(loginUserId);
                userPurchaseGroup.setCreateTime(LocalDateTime.now());
            }
            userPurchaseGroupService.saveBatch(insertList);
        }

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateFactoryRel(Long userId, String factoryCodes) {
        final LambdaUpdateWrapper<UserFactory> userFactoryLambdaUpdateWrapper =
                new LambdaUpdateWrapper<UserFactory>().eq(
                        UserFactory::getUserId,
                        userId);
        userFactoryService.remove(userFactoryLambdaUpdateWrapper);
        final List<String> split = StrUtil.split(factoryCodes, StrUtil.COMMA);
        if (CollUtil.isEmpty(split)) {
            return;
        }
        final List<UserFactory> insertLists = Lists.newArrayList();
        for (String s : split) {
            UserFactory userFactory = new UserFactory();
            userFactory.setUserId(userId);
            userFactory.setFactoryCode(s);
            insertLists.add(userFactory);
        }
        userFactoryService.saveBatch(insertLists);

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updatePurchaseOrgRel(Long userId, String purchaseOrgCodes) {
        userPurchaseOrgService.remove(new LambdaQueryWrapper<UserPurchaseOrg>().eq(UserPurchaseOrg::getUserId, userId));
        final List<String> split = StrUtil.split(purchaseOrgCodes, StrUtil.COMMA);
        if (CollUtil.isEmpty(split)) {
            return;
        }
        final List<UserPurchaseOrg> insertLists = Lists.newArrayList();
        for (String s : split) {
            UserPurchaseOrg userFactory = new UserPurchaseOrg();
            userFactory.setUserId(userId);
            userFactory.setPurchaseOrgCode(s);
            insertLists.add(userFactory);
        }
        userPurchaseOrgService.saveBatch(insertLists);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateStatus(Long userId, Integer status) {
        userService.update(new LambdaUpdateWrapper<User>().eq(User::getId, userId)
                .set(User::getDeleteStatus, status == 1 ? 1 : 0));

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void restPassword(Long userId) {
        User user = new User();
        user.setId(userId);
        String md5Password = sysProp.getDefaultPassword();
        setPassword(user, md5Password);
        user.setIsFirstLogin(FlagConst.Y.getCode());
        userService.updateById(user);
    }

    private void setPassword(User user, String md5Password) {
        final String randomSalt = RandomUtil.randomString(16).toUpperCase();
        final byte[] bytes = randomSalt.getBytes();
        //生成随机字节
        String password = PasswordUtilSHA256.password(bytes, md5Password);
        //将字节编码成字符串设为盐值
        user.setPassword(password);
        user.setCredentialsSalt(randomSalt);
    }

    @Override
    public UserEditPageDTO getForCurrentUserDetail() {
        final User user = userService.getById(LoginUtil.getUserId());
        //如果是供应商用户，需要取供应商的名称
        UserEditPageDTO pageDTO = userConvert.entity2EditPageDTO(user);
        if (user.getUserType() == UserType.SUPPLIER.getType()) {
            final String supplierName = supplierClient.getSupplierName(user.getSupplierId());
            pageDTO.setSupplierName(supplierName);
        }
        return pageDTO;
    }

    @Override
    public void updatePassword(UpdatePasswordDTO updatePasswordDTO) {
        //更新用户密码
        final User user = userService.getById(updatePasswordDTO.getId());
        if (Objects.isNull(user)) {
            throw new DaoException("用户不存在!");
        }
        if (!updatePasswordDTO.getPwd().equals(updatePasswordDTO.getRePwd())) {
            throw new DaoException("确认密码和密码不一致!");
        }
        final String password = user.getPassword();
        final String credentialsSalt = user.getCredentialsSalt();
        final byte[] bytes = Objects.isNull(credentialsSalt) ? null : credentialsSalt.getBytes();
        String userPass = PasswordUtilSHA256.password(bytes, updatePasswordDTO.getOldPwd());
        if (!StrUtil.equals(userPass, password)) {
            throw new DaoException("用户名或密码错误");
        }
        setPassword(user, updatePasswordDTO.getPwd());
        userService.updateById(user);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createSupplierUser(CreateUserDTO createUserDTO) {
        validateAccountName(createUserDTO.getAccountName());
        User user = userConvert.createUserDto2Entities(createUserDTO);
        user.setUserType(UserType.SUPPLIER.getType());
        String md5Password = sysProp.getDefaultPassword();
        user.setCreateTime(LocalDateTime.now());
        setPassword(user, md5Password);
        userService.save(user);
        return user.getId();
    }

    @Override
    public void updatePasswordFirstLogin(UpdatePasswordDTO updatePasswordDTO) {
        final Long userId = LoginUtil.getUserId();
        final User user = userService.getById(userId);
        if (Objects.isNull(user)) {
            throw new DaoException("用户不存在");
        }
        final Integer isFirstLogin = user.getIsFirstLogin();
        if (isFirstLogin.equals(FlagConst.N.getCode())) {
            throw new DaoException("用户不是首次登录");
        }
        setPassword(user, updatePasswordDTO.getPwd());
        user.setIsFirstLogin(FlagConst.N.getCode());
        userService.updateById(user);
    }

    @Override
    public String getPwdReg() {
        final String textConfig = sysConfigLogic.getTextConfig(SysConfigKey.PWD_REG.getCode());
        if (StrUtil.isNotEmpty(textConfig)) {
            return textConfig;
        }
        return "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)[a-zA-Z\\d]{8,}$";
    }


}