package com.xy.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xy.annotation.LogOperate;
import com.xy.collections.list.JArrayList;
import com.xy.collections.list.JList;
import com.xy.collections.map.JHashMap;
import com.xy.collections.map.JMap;
import com.xy.dto.SysMenuDto;
import com.xy.dto.SysWorkUser.*;
import com.xy.entity.*;
import com.xy.enums.LogEnum;
import com.xy.mapper.*;
import com.xy.redis.RedisCache;
import com.xy.service.CaptchaService;
import com.xy.service.SysUserDeptService;
import com.xy.service.SysUserRoleService;
import com.xy.service.SysWorkUserService;
import com.xy.utils.Beans;
import com.xy.utils.FunctionUtils;
import com.xy.utils.R;
import icu.mhb.mybatisplus.plugln.core.JoinLambdaWrapper;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
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.*;
import java.util.stream.Collectors;


/**
 * <p>
 * 业务用户信息表 服务实现类
 * </p>
 *
 * @author lijin
 * @since 2022-12-09
 */
@Slf4j
@Service
@RequiredArgsConstructor
@Api(tags = "业务用户管理")
public class SysWorkUserServiceImpl extends ServiceImpl<SysWorkUserMapper, SysWorkUser> implements SysWorkUserService {

    private final SysUserRoleService sysUserRoleService;

    private final SysUserDeptService sysUserDeptService;

    private final SysUserRoleMapper sysUserRoleMapper;

    private final SysUserDeptMapper sysUserDeptMapper;

    private final SysRoleMapper sysRoleMapper;

    private final SysDeptMapper sysDeptMapper;

    private final SysDeptRoleMapper sysDeptRoleMapper;

    private final CaptchaService captchaService;

    private final SysMenuServiceImpl sysMenuService;

    @Override
    @ApiOperation(value = "注册", notes = "注册")
    @Transactional(rollbackFor = Exception.class)
    public R<Long> register(AddDto addDto) {
        // 校验
        LambdaQueryWrapper<SysWorkUser> lambdaQueryWrapper = new LambdaQueryWrapper<SysWorkUser>()
                .eq(SysWorkUser::getAccount, addDto.getAccount());
        long count = count(lambdaQueryWrapper);
        if (count > 0) {
            return R.fail("账号已存在");
        }
        long count2 = count(new LambdaQueryWrapper<SysWorkUser>().eq(SysWorkUser::getPhone, addDto.getPhone()));
        if (count2 > 0) {
            return R.fail("手机号已存在");
        }
        long count3 = count(new LambdaQueryWrapper<SysWorkUser>().eq(SysWorkUser::getEmail, addDto.getEmail()));
        if (count3 > 0) {
            return R.fail("邮箱已存在");
        }
        // 新增业务用户
        SysWorkUser sysWorkUser = Beans.copy(SysWorkUser.class, addDto)
                .create(RedisCache.getLoginId(Long.class))
                .setPassword(SecureUtil.md5(addDto.getPassword()))
                .setPhone(count2 > 0 ? null : addDto.getPhone())
                .setEmail(count3 > 0 ? null : addDto.getEmail());
        save(sysWorkUser);
        List<Long> roleIds = addDto.getRoleIds();
        if (CollUtil.isNotEmpty(roleIds)) {
            List<SysRole> sysRoles = sysRoleMapper.selectList(new LambdaQueryWrapper<SysRole>().in(SysRole::getId, roleIds));
            JMap<Long, SysRole> sysRolesJMaps = new JArrayList<>(sysRoles).toMap(SysRole::getId).cover();
            List<SysUserRole> sysUserRoleList = roleIds.stream().map(roleId -> new SysUserRole().createId()
                    .setRoleId(roleId).setWorkUserId(sysWorkUser.getId()).setSysId(sysRolesJMaps.get(roleId).getSysId())).collect(Collectors.toList());
            // 保存用户角色绑定信息
            sysUserRoleService.saveBatch(sysUserRoleList);
        }
        List<Long> deptIds = addDto.getDeptIds();
        if (CollUtil.isNotEmpty(deptIds)) {
            List<SysDept> sysDepts = sysDeptMapper.selectList(new LambdaQueryWrapper<SysDept>().in(SysDept::getId, deptIds));
            JMap<Long, SysDept> sysDeptsJMaps = new JArrayList<>(sysDepts).toMap(SysDept::getId).cover();
            // 关联部门
            List<SysUserDept> sysUserDeptList = deptIds.stream().map(deptId -> new SysUserDept().createId()
                    .setDeptId(deptId).setWorkUserId(sysWorkUser.getId()).setSysId(sysDeptsJMaps.get(deptId).getSysId())).collect(Collectors.toList());
            // 保存用户部门 绑定信息
            sysUserDeptService.saveBatch(sysUserDeptList);
        }
        return R.ok(sysWorkUser.getId());
    }


    @Override
    @ApiOperation(value = "修改", notes = "修改")
    @Transactional(rollbackFor = Exception.class)
    public R<String> update(UpdateDto updateDto) {
        String account = updateDto.getAccount();
        log.info("权限用户信息修改：{}", JSONUtil.toJsonPrettyStr(updateDto));
        // 校验
        SysWorkUser byId = getById(updateDto.getId());
        if (StrUtil.isNotEmpty(updateDto.getPassword())) {
            updateById(new SysWorkUser().setId(updateDto.getId()).setPassword(SecureUtil.md5(updateDto.getPassword())));
        }
        if (!StringUtils.isEmpty(account)) {
            if (!account.equals(byId.getAccount())) {
                long count = count(new LambdaQueryWrapper<SysWorkUser>().eq(SysWorkUser::getAccount, account));
                if (count > 0) {
                    return R.ok("登录名已存在");
                }
            }
        }
        if (!StringUtils.isEmpty(updateDto.getPhone())) {
            if (!updateDto.getPhone().equals(byId.getPhone())) {
                long count = count(new LambdaQueryWrapper<SysWorkUser>().eq(SysWorkUser::getPhone, updateDto.getPhone()));
                if (count > 0) {
                    return R.ok("手机号已存在");

                }
            }
        }
        if (!StringUtils.isEmpty(updateDto.getEmail())) {
            if (!updateDto.getEmail().equals(byId.getEmail())) {
                long count = count(new LambdaQueryWrapper<SysWorkUser>().eq(SysWorkUser::getEmail, updateDto.getEmail()));
                if (count > 0) {
                    return R.ok("邮箱已存在");
                }
            }
        }
        // 更新用户信息
        SysWorkUser sysWorkUser = Beans.copy(SysWorkUser.class, updateDto)
                .update(RedisCache.getLoginId(Long.class))
                .setPassword(StringUtils.isNotEmpty(updateDto.getPassword()) ? SecureUtil.md5(updateDto.getPassword()) : null);
        updateById(sysWorkUser);
        // 更新角色信息
        List<Long> roleIds = updateDto.getRoleIds();
        if (CollUtil.isNotEmpty(roleIds)) {
            List<SysRole> sysRoles = sysRoleMapper.selectList(new LambdaQueryWrapper<SysRole>().in(SysRole::getId, roleIds));
            JMap<Long, SysRole> sysRolesJMaps = new JArrayList<>(sysRoles).toMap(SysRole::getId).cover();
            sysUserRoleService.remove(Wrappers.<SysUserRole>lambdaUpdate().eq(SysUserRole::getWorkUserId, sysWorkUser.getId()));
            List<SysUserRole> sysUserRoleList = roleIds.stream().map(roleId -> new SysUserRole().createId()
                    .setRoleId(roleId).setWorkUserId(sysWorkUser.getId()).setSysId(sysRolesJMaps.get(roleId).getSysId())).collect(Collectors.toList());
            // 保存用户角色绑定信息
            sysUserRoleService.saveBatch(sysUserRoleList);
        } else {
            if (roleIds != null && roleIds.size() == 0) {
                // 清理角色
                sysUserRoleService.remove(Wrappers.<SysUserRole>lambdaUpdate().eq(SysUserRole::getWorkUserId, sysWorkUser.getId()));
                log.info("{},清理角色", sysWorkUser.getAccount());

            }
        }
        // 更新部门信息
        List<Long> deptIds = updateDto.getDeptIds();
        if (CollUtil.isNotEmpty(deptIds)) {
            List<SysDept> sysDepts = sysDeptMapper.selectList(new LambdaQueryWrapper<SysDept>().in(SysDept::getId, deptIds));
            JMap<Long, SysDept> sysDeptsJMaps = new JArrayList<>(sysDepts).toMap(SysDept::getId).cover();
            sysUserDeptService.remove(Wrappers.<SysUserDept>lambdaUpdate()
                    .eq(SysUserDept::getWorkUserId, sysWorkUser.getId()));
            List<SysUserDept> newSysUserDeptList = deptIds.stream().map(deptId -> new SysUserDept().createId()
                    .setDeptId(deptId).setWorkUserId(sysWorkUser.getId()).setSysId(sysDeptsJMaps.get(deptId).getSysId())).collect(Collectors.toList());
            // 保存用户部门绑定信息
            sysUserDeptService.saveBatch(newSysUserDeptList);
        } else {
            if (deptIds != null && deptIds.size() == 0) {
                // 清理部门
                sysUserDeptService.remove(Wrappers.<SysUserDept>lambdaUpdate()
                        .eq(SysUserDept::getWorkUserId, sysWorkUser.getId()));
                log.info("{},清理部门", sysWorkUser.getAccount());
            }
        }
        return R.ok();
    }

    @Override
    @ApiOperation(value = "删除", notes = "删除")
    @Transactional(rollbackFor = Exception.class)
    public R del(DelDto delDto) {
        // 删除用户角色
        sysUserRoleMapper.delete(new LambdaQueryWrapper<SysUserRole>().in(SysUserRole::getWorkUserId, delDto.getId()));
        // 删除用户部门
        sysUserDeptMapper.delete(new LambdaQueryWrapper<SysUserDept>().in(SysUserDept::getWorkUserId, delDto.getId()));
        // 删除用户
        removeBatchByIds(delDto.getId());
        return R.ok();
    }

    @Override
    @ApiOperation("登录")
    @LogOperate(logType = LogEnum.LogType.V1, optType = LogEnum.OptType.V3, logContent = "登录")
    public R<String> login(LoginDto loginDto) {
        SysWorkUser sysWorkUser;
        // 手机短信登录
        if (!StringUtils.isEmpty(loginDto.getCode())) {
            // todo 验证短信
            if (false) {
                return R.fail("短信验证码错误");
            }
            sysWorkUser = getOne(new LambdaQueryWrapper<SysWorkUser>().eq(SysWorkUser::getPhone, loginDto.getLoginName()));
        } else {
            String clientType = loginDto.getClientType();
            // TODO 暂定类型1需要滑块验证码，这里后续定义好类型了需要改为枚举
            if ("1".equals(clientType)) {
                // 验证码滑块二次校验后端
                String pointJson = loginDto.getPointJson();
                log.info("滑块验证点坐标信息：{}", pointJson);
                if (StrUtil.isEmpty(pointJson)) {
                    return R.fail("请进行登录滑块验证码验证！");
                }
                R<String> reVerification = captchaService.reVerification(pointJson);
                String msg = reVerification.getMsg();
                if (200 != reVerification.getCode() && StrUtil.isNotEmpty(msg)) {
                    // 交易错误信息发送给前端
                    return reVerification;
                }
            }
            // 账号、邮箱、手机、密码登录
            FunctionUtils.ParamsResult<List<SFunction<SysWorkUser, String>>, SysWorkUser> fun = sFunctions -> {
                for (SFunction<SysWorkUser, String> property : sFunctions) {
                    LambdaQueryWrapper<SysWorkUser> lambdaQueryWrapper = new LambdaQueryWrapper<SysWorkUser>()
                            .eq(property, loginDto.getLoginName())
                            .eq(SysWorkUser::getPassword, SecureUtil.md5(loginDto.getPassword()));
                    SysWorkUser user = getOne(lambdaQueryWrapper);
                    if (user != null) {
                        return user;
                    }
                }
                return null;
            };
            sysWorkUser = fun.run(Arrays.asList(SysWorkUser::getAccount, SysWorkUser::getPhone, SysWorkUser::getEmail));
        }
        if (sysWorkUser == null) {
            return R.fail("登录名或密码错误");
        }
        if (!sysWorkUser.getStatus()) {
            return R.fail("账号已被封禁");
        }
        // 生成token
        StpUtil.login(sysWorkUser.getId(), loginDto.getClientType());
        // 更新登录信息
        SysWorkUser workUser = new SysWorkUser()
                .setId(sysWorkUser.getId())
                .setLastLoginId(loginDto.getLoginName())
                .setLastLoginTime(LocalDateTime.now())
                .setLoginNum(sysWorkUser.getLoginNum() + 1);
        updateById(workUser);
        // 缓存权限相关到redis
        RedisCache.workCache(sysWorkUser.getId());
        return R.ok(StpUtil.getTokenValue()).setLogMsg("登录名：" + loginDto.getLoginName());
    }

    @Override
    public R logout() {
        StpUtil.logoutByTokenValue(RedisCache.getToken());
        return R.ok();
    }

    @Override
    @ApiOperation("集合查询")
    public R<List<SysWorkVo>> list(ListDto listDto) {
        Long sysId = listDto.getSysId();

        // 条件构造
        JoinLambdaWrapper<SysWorkUser> joinLambdaWrapper = new JoinLambdaWrapper<>(SysWorkUser.class)
                .in(SysWorkUser::getId, listDto.getId())
                // .leftJoin(SysUserRole.class, SysUserRole::getWorkUserId, SysWorkUser::getId).eq(sysId != null, SysUserRole::getSysId, sysId)
                .leftJoin(SysUserRole.class, SysUserRole::getWorkUserId, SysWorkUser::getId)
                .manyToManySelect(SysWorkVo::getRoleIds, Long.class, cb -> cb.add(SysUserRole::getRoleId))
                .end();
        List<Long> wUserIds = listDto.getId();
        Map<Long, List<SysUserDept>> deptUserMap = MapUtil.newHashMap();
        List<SysUserDept> sysUserDepts = new ArrayList<>();
        if (CollUtil.isNotEmpty(wUserIds)) {
            // 部门分配为可选，先判断是否存在
            sysUserDepts = sysUserDeptService.list(Wrappers.<SysUserDept>lambdaQuery().eq(sysId != null, SysUserDept::getSysId, sysId).in(SysUserDept::getWorkUserId, listDto.getId()));
            // 存在部门的
            if (CollUtil.isNotEmpty(sysUserDepts)) {
                deptUserMap = sysUserDepts.stream().collect(Collectors.groupingBy(SysUserDept::getWorkUserId));
            }

        }

        // if (CollUtil.isNotEmpty(sysUserDepts)) {
        //     joinLambdaWrapper.leftJoin(SysUserDept.class, SysUserDept::getWorkUserId, SysWorkUser::getId).eq(sysId != null, SysUserDept::getSysId, sysId)
        //             .manyToManySelect(SysWorkVo::getDeptIds, Long.class, cb -> cb.add(SysUserDept::getDeptId))
        //             .end();
        // }

        // 查询数据
        List<SysWorkVo> sysWorkVos = baseMapper.joinSelectList(joinLambdaWrapper, SysWorkVo.class);
        if (sysWorkVos.size() > 0) {
            // 查询角色信息
            JList<List<Long>> roleIdss = new JArrayList<>(sysWorkVos).getProperty(SysWorkVo::getRoleIds);
            List<Long> roleIds = new ArrayList<>();
            roleIdss.forEach(longs -> roleIds.addAll(longs));
            JMap<Long, SysRole> sysRoleJMap = new JHashMap<>();
            if (roleIds.size() > 0) {
                List<SysRole> sysRoles = sysRoleMapper.selectList(new LambdaQueryWrapper<SysRole>().in(SysRole::getId, roleIds));
                sysRoleJMap = new JArrayList<>(sysRoles).toMap(SysRole::getId).cover();
            }
            // 查询部门信息
            // JList<List<Long>> deptIdss = new JArrayList<>(sysWorkVos).getProperty(SysWorkVo::getDeptIds);
            List<Long> deptIds = new ArrayList<>();
            // if (CollUtil.isNotEmpty(deptIdss)) {
            //     deptIdss.forEach(longs -> deptIds.addAll(longs));
            // }
            if (!deptUserMap.isEmpty()) {
                List<Long> departmentIds = sysUserDepts.stream().map(SysUserDept::getDeptId).distinct().collect(Collectors.toList());
                deptIds.addAll(departmentIds);
            }
            JMap<Long, SysDept> sysDeptJMap = new JHashMap<>();
            if (deptIds.size() > 0) {
                List<SysDept> sysDepts = sysDeptMapper.selectList(new LambdaQueryWrapper<SysDept>().in(SysDept::getId, deptIds));
                sysDeptJMap = new JArrayList<>(sysDepts).toMap(SysDept::getId).cover();
            }
            // 处理结果值
            for (SysWorkVo sysWorkVo : sysWorkVos) {
                List<Long> workVoRoleIds = sysWorkVo.getRoleIds();
                sysWorkVo.setRoleNames(new ArrayList<>());
                sysWorkVo.setRoleCodes(new ArrayList<>());
                for (Long workVoRoleId : workVoRoleIds) {
                    SysRole sysRole = sysRoleJMap.get(workVoRoleId);
                    if (sysRole == null) {
                        break;
                    }
                    sysWorkVo.getRoleNames().add(sysRole.getName());
                    sysWorkVo.getRoleCodes().add(sysRole.getCode());
                }
                // List<Long> workVoDeptIds = sysWorkVo.getDeptIds();
                sysWorkVo.setDeptNames(new ArrayList<>());
                sysWorkVo.setDeptIds(new ArrayList<>());
                Set<Long> workVoDeptIds = new HashSet<>();
                Set<String> userDeptNames = new HashSet<>();
                Set<Long> userDeptIds = new HashSet<>();
                if (!deptUserMap.isEmpty()) {
                    List<SysUserDept> sysUserDeptList = deptUserMap.get(sysWorkVo.getId());
                    if (CollUtil.isNotEmpty(sysUserDeptList)) {
                        for (SysUserDept sysUserDept : sysUserDeptList) {
                            SysDept sysDept = sysDeptJMap.get(sysUserDept.getDeptId());
                            if (sysDept != null) {
                                workVoDeptIds.add(sysDept.getId());
                                userDeptNames.add(sysDept.getName());
                                userDeptIds.add(sysDept.getId());
                            }
                        }
                    }
                }
                sysWorkVo.setDeptIds(CollUtil.newArrayList(userDeptIds));
                sysWorkVo.setDeptNames(CollUtil.newArrayList(userDeptNames));
                List<Long> deptRoleIds = new ArrayList<>();
                List<String> deptRoleNames = new ArrayList<>();
                List<String> deptRoleCodes = new ArrayList<>();
                if (CollUtil.isNotEmpty(workVoDeptIds)) {
                    // 部门角色
                    List<SysDeptRole> sysDeptRoles = sysDeptRoleMapper.selectList(Wrappers.<SysDeptRole>lambdaQuery()
                            .eq(sysId != null, SysDeptRole::getSysId, sysId)
                            .in(SysDeptRole::getDeptId, workVoDeptIds)
                    );
                    if (CollUtil.isNotEmpty(sysDeptRoles)) {
                        deptRoleIds = sysDeptRoles.stream().map(SysDeptRole::getRoleId).distinct().collect(Collectors.toList());
                        List<SysRole> sysRoles = sysRoleMapper.selectList(new LambdaQueryWrapper<SysRole>().in(SysRole::getId, deptRoleIds));
                        if (CollUtil.isNotEmpty(sysRoles)) {
                            deptRoleNames = sysRoles.stream().map(SysRole::getName).distinct().collect(Collectors.toList());
                            deptRoleCodes = sysRoles.stream().map(SysRole::getCode).distinct().collect(Collectors.toList());
                        }
                    }
                }
                sysWorkVo.setDeptRoleCodes(deptRoleCodes);
                sysWorkVo.setDeptRoleIds(deptRoleIds);
                sysWorkVo.setDeptRoleNames(deptRoleNames);

            }
        }
        sysWorkVos.forEach(sysWorkVo -> sysWorkVo.setPassword(null));
        return R.ok(sysWorkVos);
    }


    @ApiOperation("根据角色编号获取权限用户ID")
    @Override
    public R<List<Long>> getUserIdListByRole(ListByRoleDto listDto) {
        String roleCode = listDto.getRoleCode();
        Long sysId = listDto.getSysId();
        SysRole sysRole = sysRoleMapper.selectOne(Wrappers.<SysRole>lambdaQuery().eq(SysRole::getCode, roleCode).eq(SysRole::getSysId, sysId));
        Long roleId = sysRole.getId();
        List<SysUserRole> sysUserRoles = sysUserRoleService.list(Wrappers.<SysUserRole>lambdaQuery().eq(SysUserRole::getRoleId, roleId).eq(SysUserRole::getSysId, sysId));
        if (CollUtil.isEmpty(sysUserRoles)) {
            return R.ok();
        }
        List<Long> uIds = sysUserRoles.stream().map(SysUserRole::getWorkUserId).collect(Collectors.toList());
        return R.ok(uIds);
    }

    @Override
    public R<List<SysMenuDto.MenuVo>> getUserMenus(SysMenuDto.WorkMenuList workMenu) {
        return R.ok(R.feignCheckData(sysMenuService.workMenuList("", workMenu)));
    }

}
