package cn.iocoder.yudao.module.staff.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.common.BaseConstants;
import cn.iocoder.yudao.common.CommonErrorCodeConstants;
import cn.iocoder.yudao.common.InviterTypeEnum;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
import cn.iocoder.yudao.module.custom.entity.BasePromotion;
import cn.iocoder.yudao.module.custom.entity.CustomerMain;
import cn.iocoder.yudao.module.custom.service.BasePromotionService;
import cn.iocoder.yudao.module.custom.service.CustomerMainService;
import cn.iocoder.yudao.module.custom.service.UserCarService;
import cn.iocoder.yudao.module.label.entity.BusiLabel;
import cn.iocoder.yudao.module.label.entity.Label;
import cn.iocoder.yudao.module.label.service.BusiLabelService;
import cn.iocoder.yudao.module.label.service.LabelService;
import cn.iocoder.yudao.module.staff.entity.CompanyStaff;
import cn.iocoder.yudao.module.staff.mapper.CompanyStaffMapper;
import cn.iocoder.yudao.module.staff.service.CompanyStaffChangeService;
import cn.iocoder.yudao.module.staff.service.CompanyStaffService;
import cn.iocoder.yudao.module.staff.service.UniqueCodeService;
import cn.iocoder.yudao.module.staff.vo.CompanyStaffReqVO;
import cn.iocoder.yudao.module.staff.vo.CompanyStaffRespVO;
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
import cn.iocoder.yudao.module.system.api.permission.RoleApi;
import cn.iocoder.yudao.module.system.api.permission.dto.RoleReqDTO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import cn.iocoder.yudao.module.system.api.user.dto.UserDTO;
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.Period;
import java.time.ZoneId;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.config.CommonStr.USER_TYPE_STAFF;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;

/**
 * 企业管理-员工信息表 服务实现类
 *
 * @author 小李
 * @date 16:29 2024/8/6
 **/
@Service
public class CompanyStaffServiceImpl extends ServiceImpl<CompanyStaffMapper, CompanyStaff> implements CompanyStaffService {

    @Resource
    private CompanyStaffMapper staffMapper;

    @Resource
    private AdminUserApi adminUserApi;

    @Resource
    private LabelService labelService;

    @Resource
    private BusiLabelService busiLabelService;

    @Resource
    private UniqueCodeService uniqueCodeService;

    @Resource
    private DeptApi deptApi;

    @Resource
    private CompanyStaffChangeService staffChangeService;

    @Resource
    private RoleApi roleApi;

    @Resource
    private PermissionApi permissionApi;
    @Autowired
    private BasePromotionService basePromotionService;
    @Resource
    private CustomerMainService customerMainService;
    @Resource
    private UserCarService userCarService;

    /**
     * 获得企业管理-员工信息表分页
     *
     * @param pageReqVO 分页查询
     * @param page      分页对象
     * @author 小李
     * @date 17:11 2024/8/6
     **/
    @Override
    public IPage<CompanyStaff> queryListPage(CompanyStaffReqVO pageReqVO, Page<CompanyStaff> page) {
        return staffMapper.selectListPage(pageReqVO, page);
    }

    /**
     * 新增企业管理-员工管理信息
     *
     * @param staffRespVO 员工对象
     * @author 小李
     * @date 13:57 2024/8/7
     **/
    @Override
    @DSTransactional
    public Long saveStaff(CompanyStaffRespVO staffRespVO) {
        // 获取当前登录用户的详细信息
        AdminUserRespDTO loginUser = getLoginUser();

        // 验证登录账号是否重复
        AdminUserRespDTO checkUserName = adminUserApi.getUserByUsername(staffRespVO.getLoginAccount());
        if (ObjectUtil.isNotEmpty(checkUserName)) {
            throw exception(CommonErrorCodeConstants.LOGIN_ACCOUNT_EXIST);
        }

        /* 创建UserDTO用于给sys_user插入数据 */
        UserDTO userDTO = new UserDTO();
        userDTO.setUsername(staffRespVO.getLoginAccount());
        userDTO.setPassword(staffRespVO.getPassword());
        userDTO.setNickname(staffRespVO.getName());
        userDTO.setDeptId(loginUser.getDeptId());
        userDTO.setMobile(staffRespVO.getTel());
        userDTO.setSex(staffRespVO.getSex());
        userDTO.setUserType(USER_TYPE_STAFF);
        // 获取sys_users中刚插入记录ID给准备添加的员工
        Long userId = adminUserApi.createUser(userDTO);
        staffRespVO.setUserId(userId);
        // 设置新增员工部门
        staffRespVO.setDeptId(loginUser.getDeptId());

        /* 插入标签库 */
        if (ObjectUtil.isNotEmpty(staffRespVO.getLabelsArray())) {
            // 1 获取标签库中的存在的数据
            List<Label> labels = getLabelsByLabelName(staffRespVO.getLabelsArray());
            // 2 如果有标签库中没有的数据，就添加到标签库
            if (labels.size() < staffRespVO.getLabelsArray().size()) {
                // 2.1 所有存在的标签的LabelName
                List<String> names = labels.stream().map(Label::getLabelName).collect(Collectors.toList());
                // 2.2 获取新的标签的名称
                List<String> newNames = staffRespVO.getLabelsArray().stream().filter(item -> !names.contains(item)).collect(Collectors.toList());
                // 2.3 构建新标签对象批量插入
                List<Label> newLabels = createLabels(newNames);
                labelService.saveBatch(newLabels);
            }
        }

        /* 插入员工库 */
        // 1 获取当前登录用户的企业信息给添加的员工
        DeptRespDTO loginDept = getLoginDept(loginUser.getDeptId());
        staffRespVO.setCorpId(loginDept.getCorpId());
        // 2 生成唯一推广码
        String uniqueCode = uniqueCodeService.createUniqueCode();
        if (!ObjectUtil.isNotEmpty(uniqueCode)) {
            throw exception(CommonErrorCodeConstants.UNIQUE_CODE_CREATE_REPEAT);
        }
        staffRespVO.setUniqueCode(uniqueCode);
        // 3 保存员工信息到数据库
        this.save(staffRespVO);

        // 新增角色权限
        Set<Long> roleIds = staffRespVO.getRoleIds().stream().collect(Collectors.toSet());
        // 非空判断
        if (CollectionUtil.isNotEmpty(roleIds)) {
            permissionApi.assignUserRoleByRoleIds(userId, roleIds);
        }

        /* 插入标签到业务标签表 */
        if (ObjectUtil.isNotEmpty(staffRespVO.getLabelsArray())) {
            // 1 获取所有标签信息
            List<Label> labels = getLabelsByLabelName(staffRespVO.getLabelsArray());
            // 构建业务标签对象批量插入
            List<BusiLabel> busiLabels = createBusiLabels(labels, staffRespVO.getId());
            busiLabelService.saveBatch(busiLabels);
        }
        return userId;
    }

    /**
     * 更新员工信息
     *
     * @param staffRespVO 员工对象
     * @author 小李
     * @date 11:06 2024/8/8
     **/
    @Override
    @DSTransactional
    public void updateStaff(CompanyStaffRespVO staffRespVO) {
        /* 修改标签 */
        if (ObjectUtil.isNotEmpty(staffRespVO.getLabelsArray())) {
            /* 检查是否需要新增标签 */
            // 1 获取标签
            List<Label> labels = getLabelsByLabelName(staffRespVO.getLabelsArray());
            List<String> oldLabelNames = labels.stream().map(item -> item.getLabelName()).collect(Collectors.toList());
            List<String> newLabelNames = staffRespVO.getLabelsArray().stream().filter(item -> !oldLabelNames.contains(item)).collect(Collectors.toList());
            // 2 如果有，就新增标签
            if (ObjectUtil.isNotEmpty(newLabelNames)) {
                // 2.1 获取需要新增的标签名新增标签
                List<Label> newLabels = createLabels(newLabelNames);
                labelService.saveBatch(newLabels);
            }

            /* 检查是否需要更新业务标签 */
            // 1 获取业务标签表中旧数据
            List<BusiLabel> busiLabels = getBusiLabelsByMainId(staffRespVO.getId());
            // 2 检查是否需要更新数据, 如果有就更新
            List<BusiLabel> oldBusiLabels = busiLabels.stream().filter(item -> !staffRespVO.getLabelsArray().contains(item.getLabelName())).collect(Collectors.toList());
            List<String> oldBusiLabelNames = oldBusiLabels.stream().map(item -> item.getLabelName()).collect(Collectors.toList());
            List<String> busiLabelNames = busiLabels.stream().map(item -> item.getLabelName()).collect(Collectors.toList());
            List<String> newBusiLabelNames = staffRespVO.getLabelsArray().stream().filter(item -> !busiLabelNames.contains(item)).collect(Collectors.toList());
            // 2.1 是否要需要删除
            if (ObjectUtil.isNotEmpty(oldBusiLabelNames)) {
                // 删除需要更新的旧数据
                busiLabelService.removeBatchByIds(oldBusiLabels);
            }
            // 2.2 是否需要新增业务标签
            if (ObjectUtil.isNotEmpty(newBusiLabelNames)) {
                // 构建新数据，新增
                List<Label> newLabel = getLabelsByLabelName(newBusiLabelNames);
                List<BusiLabel> newBusiLabel = createBusiLabels(newLabel, staffRespVO.getId());
                busiLabelService.saveBatch(newBusiLabel);
            }
        }

        /* 修改角色 */
        if (ObjectUtil.isNotEmpty(staffRespVO.getRoleIds())) {
            Set<Long> roleIdes = staffRespVO.getRoleIds().stream().collect(Collectors.toSet());
            permissionApi.assignUserRoleByRoleIds(staffRespVO.getUserId(), roleIdes);
        } else {
            permissionApi.assignUserRoleByRoleIds(staffRespVO.getUserId(), null);
        }

        /* 修改员工表 */
        baseMapper.updateById(staffRespVO);
    }

    /**
     * 删除员工
     *
     * @param id 员工ID
     * @author 小李
     * @date 10:42 2024/8/8
     **/
    @Override
    @DSTransactional
    public void deleteStaff(String id) {
        /* 获取删除记录详细信息 */
        CompanyStaff staff = baseMapper.selectById(id);

        /* 验证是否已交接工作 */
        Boolean flag = staffChangeService.checkChangeStatus(staff.getUserId());
        if (!flag) {
            throw exception(CommonErrorCodeConstants.STAFF_NOT_CHANGE);
        }

        /* 删除sys_users记录 */
        adminUserApi.deleteUser(staff.getUserId());

        /* 删除业务标签表记录 */
        busiLabelService.remove(new LambdaQueryWrapper<BusiLabel>().eq(BusiLabel::getMainId, staff.getId()));


        /* 删除员工表记录 */
        baseMapper.deleteById(id);
    }


    /**
     * 删除员工信息通过用户id
     *
     * @param userId 用户ID
     * @author zcy
     * @date 16:32 2024/8/6
     **/
    @Override
    @DSTransactional
    public void deleteStaffByUserId(Long userId) {
        /* 获取删除记录详细信息 */
        LambdaQueryWrapper<CompanyStaff> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(CompanyStaff::getUserId, userId).last("limit 1");
        CompanyStaff staff = baseMapper.selectOne(queryWrapper);
        /* 验证是否已交接工作 */
        Boolean flag = staffChangeService.checkChangeStatus(staff.getUserId());
        if (!flag) {
            throw exception(CommonErrorCodeConstants.STAFF_NOT_CHANGE);
        }
        /* 删除sys_users记录 */
        adminUserApi.deleteUser(staff.getUserId());
        /* 删除业务标签表记录 */
        busiLabelService.remove(new LambdaQueryWrapper<BusiLabel>().eq(BusiLabel::getMainId, staff.getId()));
        /* 删除员工表记录 */
        baseMapper.deleteById(staff.getId());
    }

    /**
     * 查询员工
     *
     * @param id
     * @author 小李
     * @date 17:59 2024/8/6
     **/
    @Override
    public CompanyStaffRespVO getCompanyStaffById(String id) {
        // 获取修改员工的详细信息
        CompanyStaff staff = baseMapper.selectById(id);
        // 构建员工响应对象
        CompanyStaffRespVO staffRespVO = BeanUtil.toBean(staff, CompanyStaffRespVO.class);
        // 查询业务标签表中属于员工的标签
        List<BusiLabel> busiLabels = busiLabelService.list(new LambdaQueryWrapper<BusiLabel>().eq(BusiLabel::getMainId, staff.getId()));
        // 取出标签名给员工响应对象
        if (ObjectUtil.isNotEmpty(busiLabels)) {
            List<String> labelsArray = busiLabels.stream().map(item -> item.getLabelName()).collect(Collectors.toList());
            staffRespVO.setLabelsArray(labelsArray);
        }
        // 查角色ID给响应对象
        List<Long> roleIds = permissionApi.getRoleIdsByUserId(staff.getUserId());
        staffRespVO.setRoleIds(roleIds);
        return staffRespVO;
    }

    /**
     * 获取当前功能的标签
     *
     * @author 小李
     * @date 14:59 2024/8/7
     **/
    @Override
    public List<Label> getLabels() {
        return labelService.list(new LambdaQueryWrapper<Label>().and(item -> {
            item.eq(Label::getSystemCode, BaseConstants.COMPANY_STAFF)
                    .eq(Label::getType, BaseConstants.FUNC_COMPANY);
        }));
    }

    /**
     * 根据标签名集合获取标签
     *
     * @param labelNames 标签名集合
     * @author 小李
     * @date 11:48 2024/8/8
     **/
    private List<Label> getLabelsByLabelName(List<String> labelNames) {
        return labelService.list(new LambdaQueryWrapper<Label>().and(item -> {
            item.in(Label::getLabelName, labelNames).eq(Label::getSystemCode, BaseConstants.COMPANY_STAFF);
        }));
    }

    /**
     * 根据员工ID获取业务标签
     *
     * @param mainId 员工ID
     * @author 小李
     * @date 11:48 2024/8/8
     **/
    private List<BusiLabel> getBusiLabelsByMainId(String mainId) {
        return busiLabelService.list(new LambdaQueryWrapper<BusiLabel>().eq(BusiLabel::getMainId, mainId));
    }

    /**
     * 根据标签名信息创建标签
     *
     * @param names 标签名集合
     * @author 小李
     * @date 11:52 2024/8/8
     **/
    private List<Label> createLabels(List<String> names) {
        return names.stream().map(item -> {
            Label label = new Label();
            label.setLabelName(item);
            label.setType(BaseConstants.FUNC_COMPANY);
            label.setLabelDesc(item);
            label.setLabelType(BaseConstants.LABEL_TYPE);
            label.setSystemCode(BaseConstants.COMPANY_STAFF);
            return label;
        }).collect(Collectors.toList());
    }

    /**
     * 根据标签信息创建业务标签
     *
     * @param labels 标签名集合
     * @param id     员工ID
     * @author 小李
     * @date 11:52 2024/8/8
     **/
    private List<BusiLabel> createBusiLabels(List<Label> labels, String id) {
        return labels.stream().map(item -> {
            BusiLabel label = new BusiLabel();
            label.setMainTable(BaseConstants.COMPANY_STAFF);
            label.setMainId(id);
            label.setLabelId(item.getId());
            label.setLabelType(item.getLabelType());
            label.setLabelName(item.getLabelName());
            label.setLabelContent(item.getLabelName());
            label.setSystemCode(BaseConstants.COMPANY_STAFF);
            return label;
        }).collect(Collectors.toList());
    }

    /**
     * 获取当前登录用户部门下所有员工信息
     *
     * @author 小李
     * @date 15:54 2024/8/8
     **/
    @Override
    public List<CompanyStaff> getStaffList(String query) {
        if (ObjectUtil.isNotEmpty(query)) {
            return baseMapper.selectList(new LambdaQueryWrapper<CompanyStaff>()
                    .like(CompanyStaff::getName, query)
                    .or()
                    .like(CompanyStaff::getTel, query)
            );
        }
        return baseMapper.selectList(new QueryWrapper<>()).stream().limit(20).collect(Collectors.toList());
    }

    /**
     * 重置员工登录密码
     *
     * @param staffRespVO 员工对象
     * @author 小李
     * @date 16:21 2024/8/9
     **/
    public void resetPassword(CompanyStaffRespVO staffRespVO) {
        // 修改员工密码
        adminUserApi.resetPassword(staffRespVO.getUserId(), staffRespVO.getPassword());
    }

    /**
     * 验证工号是否重复
     *
     * @param workNo 输入的工号
     * @author 小李
     * @date 14:03 2024/8/9
     **/
    public Boolean checkWorkNo(String workNo) {
        return staffMapper.selectCount(new LambdaQueryWrapper<CompanyStaff>().and(item -> {
            item.eq(CompanyStaff::getWorkNo, workNo);
        })) == 0;
    }

    /**
     * 获取当前登录用户的详细信息
     *
     * @author 小李
     * @date 17:27 2024/8/9
     **/
    private AdminUserRespDTO getLoginUser() {
        Long userId = SecurityFrameworkUtils.getLoginUserId();
        return adminUserApi.getUser(userId);
    }

    /**
     * 获取当前登录用户的部门详细信息
     *
     * @param deptId 部门ID
     * @author 小李
     * @date 17:28 2024/8/9
     **/
    private DeptRespDTO getLoginDept(Long deptId) {
        return deptApi.getDept(deptId);
    }

    /**
     * 更新员工的工龄和司龄
     *
     * @author 小李
     * @date 11:41 2024/8/13
     **/
    @Override
    @DSTransactional
    public void updateStaffWorkAndJoinedYears() {
        LambdaQueryWrapper<CompanyStaff> queryWrapper = new LambdaQueryWrapper<>();
        Page<CompanyStaff> page = new Page<>(1, BaseConstants.BATCH_SIZE);
        while (true) {
            // 查询一页的数据
            Page<CompanyStaff> companyStaffPage = baseMapper.selectPage(page, queryWrapper);
            // 对每一页数据进行操作更新
            List<CompanyStaff> result = companyStaffPage.getRecords().stream().map(item -> {
                // 获取当前时间、工作时间、入职时间
                LocalDate currentDate = LocalDate.now();
                LocalDate workDate = item.getWorkDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
                LocalDate joinedDate = item.getJoinedDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate();

                // 计算工龄
                BigDecimal workYear = new BigDecimal(Period.between(workDate, currentDate).getYears());
                item.setWorkYear(workYear);

                // 计算司龄
                BigDecimal joinedYear = new BigDecimal(Period.between(joinedDate, currentDate).getYears());
                item.setJoinedYear(joinedYear);

                return item;
            }).collect(Collectors.toList());

            // 批量更新
            updateBatchById(result);

            // 没有下一页了就退出
            if (!companyStaffPage.hasNext()) {
                break;
            }

            // 取一页
            page.setCurrent(page.getCurrent() + 1);
        }
    }

    /**
     * 获取当前租户有些什么角色可以分配
     *
     * @author 小李
     * @date 14:53 2024/8/14
     **/
    @Override
    public List<RoleReqDTO> getRoleList() {
        return roleApi.getRoleList();
    }

    /**
     * 新增推广记录---忽略租户
     *
     * @param dataFrom         客户来源
     * @param uniqueCode       推广码
     * @param user             被推广用户
     * @param promotionChannel 推广渠道
     * @param registerTime     被推广用户注册的时间
     * @author vinjor-m
     * @date 12:26 2024/8/14
     **/
    @Override
    @TenantIgnore
    public Boolean createPromotion(String dataFrom, String uniqueCode, AdminUserDO user, String promotionChannel, Date registerTime) {
        /*1.先查是否是员工推广 */
        LambdaQueryWrapper<CompanyStaff> queryWrapper = new LambdaQueryWrapper<CompanyStaff>()
                .eq(CompanyStaff::getUniqueCode, uniqueCode);
        List<CompanyStaff> staffList = this.list(queryWrapper);
        if (!staffList.isEmpty()) {
            //员工推广
            CompanyStaff staff = staffList.get(0);
            //查该客户是否已被该租户的员工推广过
            final CustomerMain[] thisCus = {null};
            //取到该员工的租户，以该租户的角度去保存客户数据
            TenantUtils.execute(staff.getTenantId(), () -> {
                thisCus[0] = customerMainService.getCustomerByUserId(user.getId());
            });
            if (null == thisCus[0]) {
                // 插入推广记录
                BasePromotion basePromotion = new BasePromotion();
                basePromotion.setOldUserId(staff.getUserId());
                basePromotion.setOldUserName(staff.getName());
                basePromotion.setPromotionChannel(promotionChannel);
                basePromotion.setNewUserId(user.getId());
                basePromotion.setNewUserName(user.getNickname());
                basePromotion.setRegisterTime(registerTime);
                basePromotion.setUniqueCode(uniqueCode);
                basePromotionService.save(basePromotion);
                //将客户信息授权给员工所属的租户
                userCarService.empowerUserInfo(staff.getTenantId(), dataFrom, staff.getUserId(), user.getId(), InviterTypeEnum.STAFF.getCode());
            }
        } else {
            /*2.不是员工推广，再查是否是客户推广*/
        }
        return true;
    }

    /**
     * 给所有没有推广码的员工设置推广码
     *
     * @author vinjor-M
     * @date 17:23 2024/10/15
     **/
    @Override
    @TenantIgnore
    public void setStaffUnicode() {
        LambdaQueryWrapper<CompanyStaff> queryWrapper = new LambdaQueryWrapper<CompanyStaff>()
                .isNull(CompanyStaff::getUniqueCode);
        List<CompanyStaff> list = this.list(queryWrapper);
        if (!list.isEmpty()) {
            for (CompanyStaff staff : list) {
                try {
                    // 2 生成唯一推广码
                    String uniqueCode = uniqueCodeService.createUniqueCode();
                    if (!ObjectUtil.isNotEmpty(uniqueCode)) {
                        throw exception(CommonErrorCodeConstants.UNIQUE_CODE_CREATE_REPEAT);
                    }
                    staff.setUniqueCode(uniqueCode);
                } catch (Exception e) {
                    //记录日志，不能影响程序执行
                    log.error(e.getMessage());
                }
            }
            this.updateBatchById(list);
        }
    }
}
