package com.ssyf.module.auth.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.DesensitizedUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.ssyf.module.auth.domain.entity.*;
import com.ssyf.module.auth.domain.query.DataRelationQuery;
import com.ssyf.module.auth.domain.query.UserPageQuery;
import com.ssyf.module.auth.domain.query.UserQuery;
import com.ssyf.module.auth.emuns.DataRelationEnum;
import com.ssyf.module.auth.emuns.UserTypeEnum;
import com.ssyf.module.auth.helper.UserHelper;
import com.ssyf.common.core.enums.StatusAccountEnum;
import com.ssyf.common.core.util.EntityUtil;
import com.ssyf.plugs.mysql.helper.MybatisPlusHelper;
import com.ssyf.module.auth.service.*;
import com.ssyf.common.core.enums.StatusDeleteMarkEnum;
import com.ssyf.common.core.helper.PasswordHelper;
import com.ssyf.common.core.util.AssertUtil;
import com.ssyf.common.security.helper.ClientContextHelper;
import com.ssyf.common.security.domain.ClientInfo;
import com.ssyf.module.auth.mapper.UserMapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ssyf.common.security.helper.LoginHelper;

import com.ssyf.common.web.apicode.ApiCodeUser;
import com.ssyf.common.web.error.ServiceRuntimeException;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 用户信息表，存储和用户相关的所有一对一的数据信息；
 * <p>
 * 设计考虑：
 * 简化关联复杂查询，提升查询效率；
 * 将用户关联信息分布在列中存储，将多行数据转为列数据，可简化关联查询复杂层度，也可提升查询效率；
 * 问题：不同的需求和功能的情况下用户关联信息不一致，最初设计不可能考虑到所有情况，而解决方式是：当有新的关联数据且是一对一的情况下，对该表进行字段添加即可，注意设置默认值 服务实现类
 * </p>
 *
 * @author auth
 * @since 2022-11-20
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Lazy
    @Autowired
    private UserRoleRelationService userRoleRelationService;
    @Lazy
    @Autowired
    private RoleService roleService;
    @Lazy
    @Autowired
    private UserPermissionRelationService userPermissionRelationService;
    @Lazy
    @Autowired
    private RoleMenuPermissionRelationService roleMenuPermissionRelationService;
    @Lazy
    @Autowired
    private DataRelationService dataRelationService;

    @Lazy
    @Autowired
    private MenuPermissionService menuPermissionService;

    @Override
    public User getOneByUsername(String username) {
        return this.getOne(createBaseQueryWrapper().eq(User::getUsername, username));
    }

    @Override
    public User getOneByMail(String mail) {
        return this.getOne(createBaseQueryWrapper().eq(User::getMail, mail));
    }

    @Override
    public User getOneByPhone(String phone) {
        return this.getOne(createBaseQueryWrapper().eq(User::getPhone, phone));
    }

    /**
     * 用户数据为敏感数据，当前删除做标记删除处理，真实删除由数据维护定时删除
     * @param uid
     */
    @Override
    public void markDelete(String uid) {
        User user = this.checkedAndGetByUid(uid);
        user.setDeleteMark(StatusDeleteMarkEnum.delete.getValue()).setDeleteTime(LocalDateTime.now());
        user.setStatus(StatusAccountEnum.freeze.getValue());
        this.updateById(user);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void removeByUid(String uid) {
        User user = this.getOne(new LambdaQueryWrapper<User>().eq(User::getUid, uid));
        AssertUtil.isNotNull(user, ServiceRuntimeException.instance(ApiCodeUser.PARAMETER_ERROR));
        //删除关联
        this.userRoleRelationService.removeByUserUid(user.getUid());
        this.userPermissionRelationService.removeByUserUid(user.getUid());
        this.dataRelationService.removeBy(new DataRelationQuery().setHostId(uid));
        //删除自身
        this.remove(new LambdaQueryWrapper<User>().eq(User::getUid, user.getUid()));
    }

    @Override
    public User getOneByUid(String uid) {
        return this.getOne(createBaseQueryWrapper().eq(User::getUid, uid));
    }


    @Override
    public User checkedAndGetByUid(String uid) {
        User user = this.getOneByUid(uid);
        AssertUtil.isNotNull(user, ServiceRuntimeException.instance(ApiCodeUser.PARAMETER_ERROR));
        return user;
    }

    @Override
    public User registerByPhone(String phone) {
        UserQuery createQuery = new UserQuery();
        createQuery.setUsername(phone).setPhone(phone);
        return this.innerCreateUser(createQuery);
    }

    @Override
    public User registerByMail(String mail) {
        UserQuery createQuery = new UserQuery();
        createQuery.setUsername(mail).setMail(mail);
        return this.innerCreateUser(createQuery);
    }

    @Override
    public User registerByUsername(String username, String password) {
        UserQuery createQuery = new UserQuery();
        createQuery.setUsername(username).setPassword(password);
        return this.innerCreateUser(createQuery);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public User loginByUsername(String username, String password) {
        User user = this.getOneByUsername(username);
        if(user == null){
            ServiceRuntimeException.throw0(ApiCodeUser.PARAMETER_ERROR.getCode() , "用户不存在");
        }
        //验证密码
        if(!PasswordHelper.valid(password, user.getPassword())){
            ServiceRuntimeException.throw0(ApiCodeUser.PARAMETER_ERROR.getCode() , "密码错误");
        }
        return this.innerLogin(user);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public User loginByMail(String mail) {
        User user = this.getOneByMail(mail);
        return this.innerLogin(user);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public User loginByPhone(String phone) {
        User user = this.getOneByPhone(phone);
        return this.innerLogin(user);
    }

    @Override
    public String loginAndGetToken(User user) {
        LoginHelper.login(BeanUtil.toBean(user, ClientInfo.class).setId(user.getUid()).setIntId(user.getId()));
        StpUtil.getSession().set("type" , user.getType());
        return StpUtil.getTokenValue();
    }

    @Override
    public void updateByUid(String userUid, UserQuery query) {
        User user = this.checkedAndGetByUid(userUid);
        User updateUser = BeanUtil.copyProperties(query, User.class);
        String phone = StrUtil.isBlank(updateUser.getPhone()) || updateUser.getPhone().equals(user.getPhone()) ? null : updateUser.getPhone();
        String mail = StrUtil.isBlank(updateUser.getMail()) || updateUser.getMail().equals(user.getMail()) ? null : updateUser.getMail();
        String username = StrUtil.isBlank(updateUser.getUsername()) || updateUser.getUsername().equals(user.getUsername()) ? null : updateUser.getUsername();
        //重复校验
        this.checkedRepeatUser(phone, mail, username);
        //更新密码
        if (StrUtil.isNotBlank(query.getPassword())) {
            updateUser.setPassword(PasswordHelper.encode(query.getPassword()));
        }
        //基本信息设置，用于更新时做相关校验
        updateUser.setId(user.getId()).setUid(user.getUid());
        //更新
        this.updateById(updateUser);
    }

    @Override
    public boolean updateById(User entity) {
        return this.updateBatchById(Arrays.asList(entity));
    }

    @Override
    public boolean updateBatchById(Collection<User> entityList) {
        return super.updateBatchById(entityList);
    }

    @Override
    public PageInfo<User> getPage(UserPageQuery query) {
        //分页
        PageHelper.startPage(query.getPageNum(), query.getPageSize());
        LambdaQueryWrapper<User> queryWrapper = this.createQueryWrapper(query);
        List<User> list = this.list(queryWrapper);
        return new PageInfo<>(list);
    }

    @Override
    public List<User> getList(UserPageQuery query) {
        LambdaQueryWrapper<User> queryWrapper = this.createQueryWrapper(query);
        return this.list(queryWrapper);
    }

    @Override
    public User createUser(UserQuery query) {
        return this.innerCreateUser(query);
    }

    @Override
    public User createYunying(UserQuery query) {
        query.setType(UserTypeEnum.Yunying.getCode());
        User user = this.innerCreateUser(query);
        //创建默认权限
        Role role = this.roleService.getOneByName("运营");
        if(role == null){
            return user;
        }
        this.assignOrRemoveRole(user.getUid() ,Arrays.asList(role.getUid()));
        return user;
    }

    @Override
    public User getByCurrent() {
        return this.getOneByUid(ClientContextHelper.getClientInfo().getId().toString());
    }

    @Override
    public void checkedRepeatUser(String phone ,String mail ,String username) {
        if (StrUtil.isNotBlank(phone)) {
            User old = this.getOneByPhone(phone);
            AssertUtil.isNull(old, ServiceRuntimeException.instance(ApiCodeUser.PARAMETER_ERROR));
        }

        if (StrUtil.isNotBlank(mail)) {
            User old = this.getOneByMail(mail);
            AssertUtil.isNull(old, ServiceRuntimeException.instance(ApiCodeUser.PARAMETER_ERROR));
        }

        if (StrUtil.isNotBlank(username)) {
            User old = this.getOneByUsername(username);
            AssertUtil.isNull(old, ServiceRuntimeException.instance(ApiCodeUser.PARAMETER_ERROR));
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void assignOrRemoveRole(String userUid, List<String> roleUids) {
        this.userRoleRelationService.assignAndRemoveOld(userUid ,roleUids);

        this.dataRelationService.reset(DataRelationEnum.userRoleShare.getValue(),userUid ,roleUids);

        List<MenuPermission> menuPermissionList = this.menuPermissionService.getListByRoleUid(roleUids);

        List<String> menuPermissionUids = menuPermissionList.stream().map(MenuPermission::getUid).collect(Collectors.toList());
        this.dataRelationService.reset(DataRelationEnum.userMenuPermissionShare.getValue(), userUid ,menuPermissionUids);

        this.userPermissionRelationService.assignOrRemoveOldByUserUid(userUid ,roleUids ,menuPermissionList);


        this.roleService.clearSuperAdminCacheForUsers(Arrays.asList(userUid));
    }

    @Override
    public boolean authAnd(String userUid, List<String> permissions) {
        List<String> permissionList = getPermissionByUserUid(userUid);
        if(CollectionUtil.isEmpty(permissionList)){
            return false;
        }
        return permissionList.containsAll(permissions);
    }

    @Override
    public boolean authOr(String userUid, List<String> permissions) {
        List<String> permissionList = getPermissionByUserUid(userUid);
        if(CollectionUtil.isEmpty(permissionList)){
            return false;
        }
        for(String permission : permissions){
            if(permissionList.contains(permission)){
                return true;
            }
        }
        return false;
    }

    @Override
    public List<String> getPermissionByUserUid(String userUid) {
        List<UserPermissionRelation> menuPermissionList = this.userPermissionRelationService.getListByUserUid(userUid);
        if(CollectionUtil.isEmpty(menuPermissionList)){
            return Collections.emptyList();
        }
        return EntityUtil.extractProperty(menuPermissionList ,UserPermissionRelation::getPermission);
    }

    @Override
    public List<String> getRoleByUserUid(String userUid) {
        List<Role> roleList = this.roleService.getRelationListByUserUid(userUid ,null);
        if(CollectionUtil.isEmpty(roleList)){
            return Collections.emptyList();
        }
        return EntityUtil.extractProperty(roleList,Role::getCode);
    }

    @Override
    public User desensitize(User user) {
        //手机
        user.setPhone(StrUtil.isNotBlank(user.getPhone()) ? DesensitizedUtil.mobilePhone(user.getPhone()): user.getPhone());
        //邮箱
        DesensitizedUtil.email(StrUtil.isNotBlank(user.getMail()) ? DesensitizedUtil.email(user.getMail()) : user.getMail());
        return user;
    }

    @Override
    public User checkedExistAndGet(String uuid) {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getUid, uuid);
        User old = this.getOne(queryWrapper);
        if (old == null) {
            ServiceRuntimeException.throw0(ApiCodeUser.PARAMETER_ERROR.getCode(), "用户不存在");
        }
        return old;
    }

    /**
     * 创建用户
     * @param query
     * @return
     */
    private User innerCreateUser(UserQuery query) {
        User newUser = BeanUtil.copyProperties(query, User.class);
        //重复判断
        this.checkedRepeatUser(newUser.getPhone() ,newUser.getMail() ,newUser.getUsername());
        //设置初始值
        String clientIp = ClientContextHelper.getClientIp();
        newUser.setUid(IdUtil.simpleUUID());
        newUser.setRegisterTime(LocalDateTime.now()).setRegisterIp(clientIp);
        newUser.setCreatedTime(LocalDateTime.now()).setDeleteMark(StatusDeleteMarkEnum.exist.getValue());
        if (StrUtil.isNotBlank(query.getPassword())) {
            newUser.setPassword(PasswordHelper.encode(query.getPassword()));
        }
        if (newUser.getStatus() == null) {
            newUser.setStatus(StatusAccountEnum.normal.getValue());
        }
        if (StrUtil.isBlank(query.getUsername())) {
            newUser.setUsername(UserHelper.createUsername(query.getPhone(), query.getMail()));
        }
        if (StrUtil.isBlank(query.getNickname())) {
            newUser.setNickname(UserHelper.createNickname(newUser.getUsername()));
        }
        //设置创建人
        ClientInfo clientInfo = ClientContextHelper.getClientInfo();
        if(clientInfo != null){
            newUser.setCreatorName(clientInfo.getNickname()).setCreatorId(clientInfo.getId().toString());
        }

        this.save(newUser);
        return newUser;

    }

    private LambdaQueryWrapper<User> createBaseQueryWrapper() {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getDeleteMark, StatusDeleteMarkEnum.exist.getValue());
        return queryWrapper;
    }

    private User innerLogin(User user) {
        //判空
        AssertUtil.isNotNull(user, ServiceRuntimeException.instance(ApiCodeUser.PARAMETER_ERROR.getCode() ,"用户不存在"));
        //删除校验
        AssertUtil.isFalse(StatusDeleteMarkEnum.isDelete(user.getDeleteMark()), ServiceRuntimeException.instance(ApiCodeUser.PARAMETER_ERROR.getCode() ,"用户已删除"));
        //正则状态
        AssertUtil.isTrue(StatusAccountEnum.isNormal(user.getStatus()), ServiceRuntimeException.instance(ApiCodeUser.PARAMETER_ERROR.getCode() ,"用户已禁用"));

        String clientIp = ClientContextHelper.getClientIp();
        user.setLastLoginIp(clientIp).setLastLoginTime(LocalDateTime.now());
        //调用父类方法，跳过本地权限验证
        super.updateById(user);
        return user;
    }

    private LambdaQueryWrapper<User> createQueryWrapper(UserPageQuery query) {
        LambdaQueryWrapper<User> queryWrapper =
                MybatisPlusHelper.createAndAutomaticRelationOrDefault(User.class
                        , query
                        , MybatisPlusHelper.WrapperRelationBuilder.create(User.class, UserPageQuery.class)
                                .likeRight(User::getNickname)
                                .likeRight(User::getUsername)
                                .likeRight(User::getPhone)
                                .ge(User::getRegisterTime, UserPageQuery::getRegisterStartTime)
                                .ge(User::getLastLoginTime, UserPageQuery::getLoginStartTime)
                                .le(User::getRegisterTime, UserPageQuery::getRegisterEndTime)
                                .le(User::getLastLoginTime, UserPageQuery::getLoginEndTime)
                                .buildArray()
                        , User::getDeleteMark
                );

        //批量查询
        queryWrapper.in(CollectionUtil.isNotEmpty(query.getIds()) ,User::getId ,query.getIds());

        //删除与存在用户
        queryWrapper.eq(User::getDeleteMark, query.isDeleteMark() ? StatusDeleteMarkEnum.delete.getValue() : StatusDeleteMarkEnum.exist.getValue());

        //排序
        if(CollectionUtil.isNotEmpty(query.getSorts())) {
            MybatisPlusHelper.orderAndIgnoreError(User.class, queryWrapper, query.getSorts());
        }
        return queryWrapper;
    }
}
