package com.qmth.sop.business.service.impl;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.fill.FillConfig;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qmth.boot.api.exception.ApiException;
import com.qmth.sop.business.bean.dto.*;
import com.qmth.sop.business.bean.params.ServiceScopeParam;
import com.qmth.sop.business.bean.params.TBCrmDetailParam;
import com.qmth.sop.business.bean.params.TBCrmParam;
import com.qmth.sop.business.bean.result.*;
import com.qmth.sop.business.entity.*;
import com.qmth.sop.business.mapper.TBCrmMapper;
import com.qmth.sop.business.service.*;
import com.qmth.sop.business.sync.FxxkApiUtils;
import com.qmth.sop.business.sync.been.fxxk.FxxkCrm;
import com.qmth.sop.business.util.SmsSendUtil;
import com.qmth.sop.common.contant.SpringContextHolder;
import com.qmth.sop.common.contant.SystemConstant;
import com.qmth.sop.common.enums.*;
import com.qmth.sop.common.util.FileUtil;
import com.qmth.sop.common.util.ResultUtil;
import com.qmth.sop.common.util.ServletUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * <p>
 * 派单信息表 服务实现类
 * </p>
 *
 * @author wangliang
 * @since 2023-08-01
 */
@Service
public class TBCrmServiceImpl extends ServiceImpl<TBCrmMapper, TBCrm> implements TBCrmService {

    private final static Logger log = LoggerFactory.getLogger(TBCrmServiceImpl.class);

    @Resource
    private TBServiceService tbServiceService;

    @Resource
    private TBUserArchivesAllocationService tbUserArchivesAllocationService;

    @Resource
    private SysRoleService sysRoleService;

    @Resource
    private TBUserArchivesService tbUserArchivesService;

    @Resource
    private TBServiceRegionDetailService tbServiceRegionDetailService;

    @Resource
    private SysUserService sysUserService;

    @Resource
    private FxxkApiUtils fxxkApiUtils;

    @Resource
    private SysCustomService sysCustomService;

    @Resource
    private TBProductService tbProductService;

    @Resource
    private SequenceService sequenceService;

    @Resource
    private TBCrmDetailService tbCrmDetailService;

    @Resource
    private SysMessageService sysMessageService;

    @Resource
    private SysUserRoleService sysUserRoleService;

    @Override
    public IPage<CrmServiceResult> findServiceScopePage(Long serviceUnitId, String city, ProductTypeEnum productType,
            String customName, Boolean bindStatus, Integer pageNumber, Integer pageSize) {
        if (city != null && city.length() > 0) {
            city = SystemConstant.translateSpecificSign(city);
        }
        if (customName != null && customName.length() > 0) {
            customName = SystemConstant.translateSpecificSign(customName);
        }
        SysUser requestUser = (SysUser) ServletUtil.getRequestUser();
        DataPermissionDto dpr = sysUserService.buildUserDataPermission(requestUser.getId());
        return this.baseMapper.findServiceScopePage(new Page<>(pageNumber, pageSize), serviceUnitId, city, productType,
                customName, bindStatus, dpr);
    }

    @Override
    public IPage<CrmServiceResult> findUnbindOrderPage(Long crmUserId, ProductTypeEnum productType, String customName,
            String crmNo, Long startTime, Long endTime, Integer pageNumber, Integer pageSize) {
        if (customName != null && customName.length() > 0) {
            customName = SystemConstant.translateSpecificSign(customName);
        }
        if (crmNo != null && crmNo.length() > 0) {
            crmNo = SystemConstant.translateSpecificSign(crmNo);
        }
        return this.baseMapper.findUnbindOrderPage(new Page<>(pageNumber, pageSize), crmUserId, productType, customName,
                crmNo, startTime, endTime);
    }

    @Override
    public List<CrmServiceResult> findCrmOrderByServiceIdList(List<Long> serviceIdList) {
        return this.baseMapper.findOrderListByServiceIds(serviceIdList);
    }

    @Transactional
    @Override
    public void bindCrmWithService(ServiceScopeParam serviceScopeParam) {
        SysUser requestUser = (SysUser) ServletUtil.getRequestUser();
        Long crmId = serviceScopeParam.getCrmId();
        Long serviceUnitId = serviceScopeParam.getServiceUnitId();
        RegionManagerSelectedTypeEnum leadType = serviceScopeParam.getLeadType();
        Long leadId = serviceScopeParam.getLeadId();
        if (RegionManagerSelectedTypeEnum.BY_PERSON.equals(leadType) && (leadId == null || leadId == 0)) {
            throw ExceptionResultEnum.ERROR.exception("指定大区经理模式必须选择大区经理");
        }

        if (crmId == null || crmId == 0) {
            throw ExceptionResultEnum.ERROR.exception("未选择要划定的派单");
        }

        TBService tbService = tbServiceService.getById(serviceUnitId);
        if (Objects.isNull(tbService) || !tbService.getEnable()) {
            throw ExceptionResultEnum.ERROR.exception("服务单元不存在");
        } else {
            ServiceStatusEnum status = tbService.getStatus();
            if (ServiceStatusEnum.FINISH.equals(status) || ServiceStatusEnum.CANCEL.equals(status)) {
                throw ExceptionResultEnum.ERROR.exception("不能划定进已完结或已作废的服务单元");
            }
        }
        TBCrm tbCrm = this.getById(crmId);
        if (Objects.isNull(tbCrm)) {
            throw ExceptionResultEnum.ERROR.exception("未找到派单");
        }
        if (CrmStatusEnum.PUBLISH.equals(tbCrm.getStatus())) {
            throw ExceptionResultEnum.ERROR.exception("该派单已经发布,不能重新划定");
        }
        Long customId = tbCrm.getCustomId();
        CrmBindResult crmBindResult = this.findCrmBindInfo(serviceUnitId, customId);
        RegionManagerSelectedTypeEnum defaultLeadType = crmBindResult.getLeadType();
        Long defaultLeadId = crmBindResult.getLeadId();
        boolean canEdit = crmBindResult.isCanEdit();
        String customName = crmBindResult.getCustomName();
        List<Long> similarCrmIdList = crmBindResult.getSimilarCrmIdList();

        // 删除旧的分配
        tbUserArchivesAllocationService.remove(new QueryWrapper<TBUserArchivesAllocation>().lambda()
                .eq(TBUserArchivesAllocation::getCrmNo, tbCrm.getCrmNo()));
        // 删除派单详情
        tbCrmDetailService.remove(new QueryWrapper<TBCrmDetail>().lambda().eq(TBCrmDetail::getCrmNo, tbCrm.getCrmNo()));

        if (canEdit) {
            // 可以更改
            switch (leadType) {
            case BY_AREA:
                // 按地区匹配
                ServiceRegionDetailResult serviceRegionDetailResult = tbServiceRegionDetailService.findRegionDetail(
                        serviceUnitId, tbCrm.getCrmNo());
                if (Objects.isNull(serviceRegionDetailResult)) {
                    throw ExceptionResultEnum.ERROR.exception("该地区还没有分配大区经理，请先分配");
                }
                UpdateWrapper<TBCrm> areaUpdateWrapper = new UpdateWrapper<>();
                areaUpdateWrapper.lambda().set(TBCrm::getServiceId, serviceUnitId).set(TBCrm::getLeadType, leadType)
                        .set(TBCrm::getUpdateId, requestUser.getId())
                        .set(TBCrm::getUpdateTime, System.currentTimeMillis())
                        .set(TBCrm::getLeadId, serviceRegionDetailResult.getLeadId())
                        .set(TBCrm::getRegionId, serviceRegionDetailResult.getServiceRegionId())
                        .set(TBCrm::getRegionDetailId, serviceRegionDetailResult.getServiceRegionDetailId());
                if (Objects.isNull(defaultLeadType) || leadType.equals(defaultLeadType)) {
                    // 更新自己信息(没有默认信息或默认信息与更改信息一致)
                    areaUpdateWrapper.lambda().eq(TBCrm::getId, crmId);
                } else if (CollectionUtils.isNotEmpty(similarCrmIdList)) {
                    // 更新同类派单信息
                    areaUpdateWrapper.lambda().in(TBCrm::getId, similarCrmIdList);
                }
                this.update(areaUpdateWrapper);
                break;
            case BY_PERSON:
                // 直接指定大区经理
                UpdateWrapper<TBCrm> personUpdateWrapper = new UpdateWrapper<>();
                personUpdateWrapper.lambda().set(TBCrm::getServiceId, serviceUnitId).set(TBCrm::getLeadType, leadType)
                        .set(TBCrm::getLeadId, leadId).set(TBCrm::getRegionId, null).set(TBCrm::getRegionDetailId, null)
                        .set(TBCrm::getUpdateId, requestUser.getId())
                        .set(TBCrm::getUpdateTime, System.currentTimeMillis());
                if ((Objects.isNull(defaultLeadType) || !SystemConstant.longNotNull(defaultLeadId)) || (
                        leadType.equals(defaultLeadType) && leadId.equals(defaultLeadId))) {
                    // 更新自己信息(没有默认信息或默认信息与更改信息一致)
                    personUpdateWrapper.lambda().eq(TBCrm::getId, crmId);
                } else if (CollectionUtils.isNotEmpty(similarCrmIdList)) {
                    // 更新同类派单信息
                    personUpdateWrapper.lambda().in(TBCrm::getId, similarCrmIdList);
                }
                this.update(personUpdateWrapper);
                break;
            }
        } else {
            // 不可更改
            if (!Objects.equals(leadType, defaultLeadType) || !Objects.equals(leadId, defaultLeadId)) {
                throw ExceptionResultEnum.ERROR.exception(String.format("客户[%s]在该服务单元下已经有发布的sop,无法更改绑定信息", customName));
            }
            tbCrm.setServiceId(serviceUnitId);
            tbCrm.setLeadType(defaultLeadType);
            tbCrm.setLeadId(defaultLeadId);
            this.updateById(tbCrm);
        }

        //研究生创建一条sop
        if (tbService.getType().equals(ProductTypeEnum.CLOUD_MARK)) {
            TBCrmDetailParam param = new TBCrmDetailParam();
            param.setCrmNo(tbCrm.getCrmNo());
            tbCrmDetailService.sopAutoCloudMarkCreate(param);
        }
    }

    @Transactional
    @Override
    public void unbindCrmWithService(Long crmId) {
        SysUser requestUser = (SysUser) ServletUtil.getRequestUser();
        TBCrm tbCrm = this.getById(crmId);
        if (Objects.isNull(tbCrm)) {
            throw ExceptionResultEnum.ERROR.exception("未找到派单信息");
        }
        if (CrmStatusEnum.PUBLISH.equals(tbCrm.getStatus())) {
            throw ExceptionResultEnum.ERROR.exception(String.format("已发布的派单[%s]不能移出，请先撤销发布", tbCrm.getCrmNo()));
        }
        Long serviceUnitId = tbCrm.getServiceId();

        TBService tbService = tbServiceService.getById(serviceUnitId);
        if (Objects.nonNull(tbService)) {
            ServiceStatusEnum status = tbService.getStatus();
            if (ServiceStatusEnum.FINISH.equals(status)) {
                throw ExceptionResultEnum.ERROR.exception("服务单元的当前状态更新为" + ServiceStatusEnum.FINISH.name() + "不允许移除");
            }
        }
        // 删除旧的分配
        tbUserArchivesAllocationService.remove(new QueryWrapper<TBUserArchivesAllocation>().lambda()
                .eq(TBUserArchivesAllocation::getCrmNo, tbCrm.getCrmNo()));
        // 删除派单详情
        tbCrmDetailService.remove(new QueryWrapper<TBCrmDetail>().lambda().eq(TBCrmDetail::getCrmNo, tbCrm.getCrmNo()));

        UpdateWrapper<TBCrm> updateWrapper = new UpdateWrapper<>();
        updateWrapper.lambda().eq(TBCrm::getId, crmId).set(TBCrm::getServiceId, null).set(TBCrm::getLeadType, null)
                .set(TBCrm::getLeadId, null).set(TBCrm::getRegionId, null).set(TBCrm::getRegionDetailId, null)
                .set(TBCrm::getUpdateId, requestUser.getId()).set(TBCrm::getUpdateTime, System.currentTimeMillis());
        this.update(updateWrapper);
    }

    @Override
    public CrmSubTotalResult findCrmSubTotalData(Long serviceUnitId, String city, ProductTypeEnum productType,
            String customName, Boolean bindStatus) {
        SysUser requestUser = (SysUser) ServletUtil.getRequestUser();
        DataPermissionDto dpr = sysUserService.buildUserDataPermission(requestUser.getId());
        List<CrmServiceResult> crmServiceResultList = this.baseMapper.findServiceScopeList(serviceUnitId, city,
                productType, customName, bindStatus, dpr);
        int totalCrmCount = crmServiceResultList.size();
        int bindCrmCount = 0;
        int unbindCrmCount = 0;
        if (totalCrmCount > 0) {
            bindCrmCount = (int) crmServiceResultList.stream().filter(e -> e.getServiceUnitId() != null).count();
            unbindCrmCount = (int) crmServiceResultList.stream().filter(e -> e.getServiceUnitId() == null).count();
        }
        CrmSubTotalResult crmSubTotalResult = new CrmSubTotalResult();
        crmSubTotalResult.setTotalCrmCount(totalCrmCount);
        crmSubTotalResult.setBindCrmCount(bindCrmCount);
        crmSubTotalResult.setUnbindCrmCount(unbindCrmCount);
        return crmSubTotalResult;
    }

    @Override
    public TBCrm findByCrmNo(String crmNo) {
        List<TBCrm> tbCrmList = this.list(
                new QueryWrapper<TBCrm>().lambda().eq(TBCrm::getCrmNo, crmNo).eq(TBCrm::getEnable, true));
        if (CollectionUtils.isEmpty(tbCrmList)) {
            throw ExceptionResultEnum.ERROR.exception("派单不存在[" + crmNo + "]");
        }
        if (tbCrmList.size() > 1) {
            throw ExceptionResultEnum.ERROR.exception("有重复派单号[" + crmNo + "]");
        }
        return tbCrmList.get(0);
    }

    @Override
    public IPage<TBCrmResult> query(IPage<Map> iPage, Long serviceId, Boolean isBind, Long leadId, Long crmUserId,
            ProductTypeEnum type, String custom, String crmNo, Long startTime, Long endTime) {
        SysUser requestUser = (SysUser) ServletUtil.getRequestUser();
        if (custom != null && custom.length() > 0) {
            custom = SystemConstant.translateSpecificSign(custom);
        }
        if (crmNo != null && crmNo.length() > 0) {
            crmNo = SystemConstant.translateSpecificSign(crmNo);
        }
        DataPermissionDto dpr = sysUserService.buildUserDataPermission(requestUser.getId());
        return this.baseMapper.query(iPage, serviceId, isBind, leadId, crmUserId,
                Objects.nonNull(type) ? type.name() : null, custom, crmNo, startTime, endTime, dpr);
    }

    @Override
    public int count(Long serviceId, Boolean isBind, Long leadId, Long crmUserId, ProductTypeEnum type, String custom,
            String crmNo, Long startTime, Long endTime) {
        SysUser requestUser = (SysUser) ServletUtil.getRequestUser();
        if (custom != null && custom.length() > 0) {
            custom = SystemConstant.translateSpecificSign(custom);
        }
        if (crmNo != null && crmNo.length() > 0) {
            crmNo = SystemConstant.translateSpecificSign(crmNo);
        }
        DataPermissionDto dpr = sysUserService.buildUserDataPermission(requestUser.getId());
        return this.baseMapper.count(serviceId, isBind, leadId, crmUserId, Objects.nonNull(type) ? type.name() : null,
                custom, crmNo, startTime, endTime, dpr);
    }

    /**
     * 新增修改派单信息表
     *
     * @param tBCrm tBCrm
     */
    @Override
    @Transactional
    public Boolean saveTBCrm(TBCrm tBCrm) {
        try {
            Long oldServiceUnitId = null;
            tBCrm.setSync(false);
            tBCrm.setEnable(true);
            SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
            if (Objects.isNull(tBCrm.getId())) {// 新增
                tBCrm.setStatus(CrmStatusEnum.UN_PUBLISH);
                tBCrm.insertInfo(sysUser.getId());
            } else { // 修改
                TBCrm dbTBCrm = this.getById(tBCrm.getId());
                if (Objects.isNull(dbTBCrm)) {
                    throw ExceptionResultEnum.ERROR.exception("未找到派单信息");
                }
                oldServiceUnitId = dbTBCrm.getServiceId();
                tBCrm.updateInfo(sysUser.getId());
            }
            boolean result = saveOrUpdate(tBCrm);
            Long serviceId = tBCrm.getServiceId();
            if (result) {
                this.crmReBindServiceUnit(tBCrm.getCrmNo(), oldServiceUnitId, serviceId);
            }

            return result;
        } catch (Exception e) {
            if (e instanceof DuplicateKeyException) {
                String errorColumn = e.getCause().toString();
                String columnStr = errorColumn.substring(errorColumn.lastIndexOf("key") + 3).replaceAll("'", "");
                throw ExceptionResultEnum.SQL_ERROR.exception(
                        "[" + FieldUniqueEnum.convertToTitle(columnStr) + "]数据不允许重复插入");
            } else if (e instanceof ApiException) {
                ResultUtil.error((ApiException) e, ((ApiException) e).getCode(), e.getMessage());
            } else {
                ResultUtil.error(e.getMessage());
            }
        }
        return null;
    }

    @Transactional
    @Override
    public Long saveTBCrm(TBCrmParam tbCrmParam) {
        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
        try {
            Long id = tbCrmParam.getId();
            String crmName = tbCrmParam.getCrmName();
            String crmNo = tbCrmParam.getCrmNo();
            Long beginTime = tbCrmParam.getBeginTime();
            Long crmUserId = tbCrmParam.getCrmUserId();
            Long customId = tbCrmParam.getCustomId();
            Long examStartTime = tbCrmParam.getExamStartTime();
            Long examEndTime = tbCrmParam.getExamEndTime();
            Long productId = tbCrmParam.getProductId();

            if (Objects.nonNull(examStartTime) && Objects.nonNull(examEndTime) && examStartTime > 0 && examEndTime > 0
                    && examEndTime < examStartTime) {
                throw ExceptionResultEnum.ERROR.exception("考试结束时间不能小于考试开始时间");
            }
            TBCrm checkCrmNo = this.getOne(
                    new QueryWrapper<TBCrm>().lambda().eq(TBCrm::getCrmNo, crmNo).last(SystemConstant.LIMIT1));
            if (Objects.nonNull(checkCrmNo) && !Objects.equals(checkCrmNo.getId(), id)) {
                throw ExceptionResultEnum.ERROR.exception("派单号不能重复");
            }
            if (!sysUserRoleService.userContainsRoles(crmUserId, RoleTypeEnum.ACCOUNT_MANAGER)) {
                throw ExceptionResultEnum.ERROR.exception("未找到客户经理信息");
            }
            if (Objects.isNull(sysCustomService.getById(customId))) {
                throw ExceptionResultEnum.ERROR.exception("未找到客户信息");
            }
            if (Objects.isNull(tbProductService.getById(productId))) {
                throw ExceptionResultEnum.ERROR.exception("未找到产品信息");
            }

            if (Objects.isNull(id)) {
                // id不存在新增
                TBCrm tbCrm = new TBCrm();
                tbCrm.setName(crmName);
                tbCrm.setCrmNo(crmNo);
                tbCrm.setBeginTime(beginTime);
                tbCrm.setCrmUserId(crmUserId);
                tbCrm.setCustomId(customId);
                if (Objects.nonNull(examStartTime) && examStartTime > 0) {
                    tbCrm.setExamStartTime(examStartTime);
                }
                if (Objects.nonNull(examEndTime) && examEndTime > 0) {
                    tbCrm.setExamEndTime(examEndTime);
                }
                tbCrm.setProductId(productId);

                tbCrm.setSync(false);
                tbCrm.setEnable(true);
                tbCrm.setStatus(CrmStatusEnum.UN_PUBLISH);
                tbCrm.insertInfo(sysUser.getId());
                this.save(tbCrm);
                id = tbCrm.getId();
            } else {
                // id存在更新
                TBCrm dbTBCrm = this.getById(id);
                if (Objects.isNull(dbTBCrm)) {
                    throw ExceptionResultEnum.ERROR.exception("未找到派单信息");
                }
                dbTBCrm.setName(crmName);
                dbTBCrm.setCrmNo(crmNo);
                dbTBCrm.setBeginTime(beginTime);
                dbTBCrm.setCrmUserId(crmUserId);
                dbTBCrm.setCustomId(customId);
                if (Objects.nonNull(examStartTime) && examStartTime > 0) {
                    dbTBCrm.setExamStartTime(examStartTime);
                }
                if (Objects.nonNull(examEndTime) && examEndTime > 0) {
                    dbTBCrm.setExamEndTime(examEndTime);
                }
                dbTBCrm.setProductId(productId);
                dbTBCrm.updateInfo(sysUser.getId());
                this.updateById(dbTBCrm);
            }
            return id;
        } catch (Exception e) {
            if (e instanceof DuplicateKeyException) {
                String errorColumn = e.getCause().toString();
                String columnStr = errorColumn.substring(errorColumn.lastIndexOf("key") + 3).replaceAll("'", "");
                throw ExceptionResultEnum.SQL_ERROR.exception(
                        "[" + FieldUniqueEnum.convertToTitle(columnStr) + "]数据不允许重复插入");
            } else if (e instanceof ApiException) {
                ResultUtil.error((ApiException) e, ((ApiException) e).getCode(), e.getMessage());
            } else {
                ResultUtil.error(e.getMessage());
            }
            return null;
        }
    }

    /**
     * 删除派单信息表
     *
     * @param id id
     */
    @Override
    @Transactional
    public Boolean delete(Long id) {
        return this.removeById(id);
    }

    @Override
    @Transactional
    public boolean batchDisable(long[] crmIds) {
        //批量修改
        List<Long> crmIdList = Arrays.stream(crmIds).boxed().collect(Collectors.toList());
        List<TBCrm> tbCrmList = this.listByIds(crmIdList);
        for (TBCrm tbCrm : tbCrmList) {
            Long serviceId = tbCrm.getServiceId();
            if (serviceId != null && serviceId > 0) {
                throw ExceptionResultEnum.ERROR.exception(String.format("已经划分的派单[%s]不能作废", tbCrm.getCrmNo()));
            }
            tbCrm.setEnable(false);
        }

        return this.updateBatchById(tbCrmList);
    }

    @Override
    public CrmProjectResult findCrmProjectInfo(String sopNo, Long crmDetailId, String crmNo) {
        CrmProjectResult result = new CrmProjectResult();
        TBCrmDetail tbCrmDetail = null;
        if (crmDetailId != null && crmDetailId > 0) {
            tbCrmDetail = tbCrmDetailService.getById(crmDetailId);
        } else if (sopNo != null && sopNo.length() > 0) {
            tbCrmDetail = tbCrmDetailService.getOne(
                    new QueryWrapper<TBCrmDetail>().lambda().eq(TBCrmDetail::getSopNo, sopNo)
                            .last(SystemConstant.LIMIT1));
        } else if (crmNo != null && crmNo.length() > 0) {
            // 传派单号 -> 派单详情的调配信息直接返回派单信息
            List<CrmProjectResult> crmProjectResultList = this.baseMapper.findCrmProjectByCrmNo(crmNo);
            if (CollectionUtils.isNotEmpty(crmProjectResultList)) {
                return crmProjectResultList.get(0);
            }
        }
        if (Objects.isNull(tbCrmDetail)) {
            log.error(String.format("未找到对应的派单详情, sopNo[%s], crmDetailId[%s]", sopNo, crmDetailId));
            throw ExceptionResultEnum.ERROR.exception("未找到对应的派单详情");
        }
        crmNo = tbCrmDetail.getCrmNo();
        crmDetailId = tbCrmDetail.getId();
        List<CrmProjectResult> crmProjectResultList = this.baseMapper.findCrmProjectByCrmNo(crmNo);
        if (CollectionUtils.isNotEmpty(crmProjectResultList)) {
            result = crmProjectResultList.get(0);
        }
        List<TBUserArchivesAllocation> datasource = tbUserArchivesAllocationService.list(
                new QueryWrapper<TBUserArchivesAllocation>().lambda()
                        .eq(TBUserArchivesAllocation::getCrmDetailId, crmDetailId));
        List<UserArchivesDto> regionCoordinatorList = this.buildUserArchivesDtoList(datasource,
                SopRoleTypeEnum.REGION_COORDINATOR);
        List<UserArchivesDto> projectManagerList = this.buildUserArchivesDtoList(datasource,
                SopRoleTypeEnum.PROJECT_MANAGER);
        List<UserArchivesDto> engineerList = this.buildUserArchivesDtoList(datasource, SopRoleTypeEnum.ENGINEER);
        result.setRegionCoordinatorList(regionCoordinatorList);
        result.setProjectManagerList(projectManagerList);
        result.setEngineerList(engineerList);
        result.setCourseName(tbCrmDetail.getCourseName());
        return result;
    }

    @Override
    public SopCrmInfo findSopCrmInfoBySop(String sopNo) {
        List<SopCrmInfo> sopCrmInfoList = this.baseMapper.findSopCrmInfoBySopNo(sopNo);
        if (CollectionUtils.isEmpty(sopCrmInfoList)) {
            //            throw ExceptionResultEnum.ERROR.exception(String.format("未找到sop单号为[%s]的派单信息", sopNo));
            return null;
        } else if (sopCrmInfoList.size() > 1) {
            throw ExceptionResultEnum.ERROR.exception(String.format("sop单号为[%s]的派单信息异常", sopNo));
        }
        return sopCrmInfoList.get(0);
    }

    @Override
    public List<TBCrm> findOccupiedCrm(Long userId, Long userArchivesId) {
        return this.baseMapper.findOccupiedCrm(userId, userArchivesId);
    }

    @Override
    public List<TBCrm> findAllCrm(Long userId, Long userArchivesId) {
        return this.baseMapper.findAllCrm(userId, userArchivesId);
    }

    @Transactional
    @Override
    public void updateCrmStatus(String crmNo, CrmStatusEnum status) {
        SysUser requestUser = (SysUser) ServletUtil.getRequestUser();
        TBCrm tbCrm = this.findByCrmNo(crmNo);
        switch (status) {
        case UN_PUBLISH: // 撤回
            tbCrm.setStatus(CrmStatusEnum.UN_PUBLISH);
            break;
        case PUBLISH: // 发布
            tbCrm.setStatus(CrmStatusEnum.PUBLISH);
            tbCrm.setPublishUserId(requestUser.getId());
            tbCrm.setPublishTime(System.currentTimeMillis());
            break;
        case FINISH: // 完结
            tbCrm.setStatus(CrmStatusEnum.FINISH);
            //            // crm完结 释放工程师资源
            //            List<CrmArchivesAllocationResult> crmArchivesAllocationResultList = tbUserArchivesAllocationService.findAllocationByCrmNo(
            //                    crmNo);
            //            List<Long> engineerArchivesIdList = crmArchivesAllocationResultList.stream().filter(e -> !RoleTypeEnum.REGION_COORDINATOR.equals(e.getType()))
            //                    .map(CrmArchivesAllocationResult::getArchivesId).distinct().collect(Collectors.toList());

            //            if (CollectionUtils.isNotEmpty(engineerArchivesIdList)) {
            //                UpdateWrapper<TBUserArchives> updateWrapper = new UpdateWrapper<>();
            //                updateWrapper.lambda().set(TBUserArchives::getStatus, UserArchivesStatusEnum.FREE).in(TBUserArchives::getId, engineerArchivesIdList);
            //                tbUserArchivesService.update(updateWrapper);
            //            }
            break;
        }
        this.updateById(tbCrm);
    }

    @Override
    public List<TBCrm> findByTBCrmListByServiceUnitIdAndAddress(Long serviceUnitId, String province, String city) {
        return this.baseMapper.findByTBCrmListByServiceUnitIdAndAddress(serviceUnitId, province, city);
    }

    @Deprecated
    @Transactional
    @Override
    public void crmReBindServiceUnit(String crmNo, Long oldServiceUnitId, Long newServiceUnitId) {
        TBCrm tbCrm = this.findByCrmNo(crmNo);
        if (CrmStatusEnum.PUBLISH.equals(tbCrm.getStatus())) {
            throw ExceptionResultEnum.ERROR.exception(String.format("已发布的派单[%s]不能重新划分，请先撤销发布", tbCrm.getCrmNo()));
        }

        if (oldServiceUnitId != null && oldServiceUnitId > 0) {
            // 之前派单有划定在服务单元 -> 解绑派单所有资源信息
            List<TBUserArchivesAllocation> dbAllocationList = tbUserArchivesAllocationService.list(
                    new QueryWrapper<TBUserArchivesAllocation>().lambda().eq(TBUserArchivesAllocation::getCrmNo, crmNo)
                            .eq(TBUserArchivesAllocation::getServiceId, oldServiceUnitId));

            if (CollectionUtils.isNotEmpty(dbAllocationList)) {
                // 释放工程师资源
                List<Long> archivesIdList = dbAllocationList.stream().map(TBUserArchivesAllocation::getArchivesId)
                        .distinct().collect(Collectors.toList());
                if (CollectionUtils.isNotEmpty(archivesIdList)) {
                    UpdateWrapper<TBUserArchives> archivesUpdateWrapper = new UpdateWrapper<>();
                    archivesUpdateWrapper.lambda().in(TBUserArchives::getId, archivesIdList)
                            .set(TBUserArchives::getStatus, UserArchivesStatusEnum.FREE);
                    tbUserArchivesService.update(archivesUpdateWrapper);
                }
                // 删除之前的派单分配详情
                tbUserArchivesAllocationService.removeByIds(
                        dbAllocationList.stream().map(TBUserArchivesAllocation::getId).distinct()
                                .collect(Collectors.toList()));
            }
            UpdateWrapper<TBCrm> updateWrapper = new UpdateWrapper<>();
            updateWrapper.lambda().eq(TBCrm::getId, tbCrm.getId()).set(TBCrm::getServiceId, null)
                    .set(TBCrm::getLeadId, null).set(TBCrm::getRegionId, null).set(TBCrm::getRegionDetailId, null)
                    .set(TBCrm::getRegionCoordinatorId, null);
            this.update(updateWrapper);
        }
        if (newServiceUnitId != null && newServiceUnitId > 0) {
            // 为派单划定的新服务单元绑定大区信息
            UpdateWrapper<TBCrm> bindServiceWrapper = new UpdateWrapper<>();
            bindServiceWrapper.lambda().eq(TBCrm::getId, tbCrm.getId()).set(TBCrm::getServiceId, newServiceUnitId);
            this.update(bindServiceWrapper);

            ServiceRegionDetailResult regionDetail = tbServiceRegionDetailService.findRegionDetail(newServiceUnitId,
                    crmNo);
            if (Objects.nonNull(regionDetail)) {
                UpdateWrapper<TBCrm> matchWrapper = new UpdateWrapper<>();
                matchWrapper.lambda().eq(TBCrm::getId, tbCrm.getId()).set(TBCrm::getLeadId, regionDetail.getLeadId())
                        .set(TBCrm::getRegionId, regionDetail.getServiceRegionId())
                        .set(TBCrm::getRegionDetailId, regionDetail.getServiceRegionDetailId());
                this.update(matchWrapper);
            }
        }
    }

    @Transactional
    @Override
    public Map<String, Object> syncCrmFromFxxk(Map<String, Object> map) {
        SysUser requestUser = (SysUser) map.get(SystemConstant.USER);
        // 已有派单信息
        List<TBCrm> tbCrmList = this.list();
        List<String> crmNoList = tbCrmList.stream().map(TBCrm::getCrmNo).collect(Collectors.toList());

        List<String> errorMsgList = new ArrayList<>();
        List<FxxkCrm> fxxkCrmList = fxxkApiUtils.findCustomList();
        int totalCount = fxxkCrmList.size();
        // 成功同步的派单数
        int successCount = 0;
        // 已有的派单数（派单号相同不重新同步）
        int alreadyCount = 0;
        // 缺失客户信息的客户
        List<String> customMissList = new ArrayList<>();
        try {
            for (FxxkCrm fxxkCrm : fxxkCrmList) {
                String basicInfoMissingError = "";
                String crmNo = fxxkCrm.getCrmNo();
                if (crmNoList.contains(crmNo)) {
                    alreadyCount++;
                    continue;
                }
                String crmName = fxxkCrm.getCrmName();
                Long beginTime = fxxkCrm.getBeginTime();
                String managerName = fxxkCrm.getManagerName();
                String managerMobileNumber = fxxkCrm.getManagerMobileNumber();
                ProductTypeEnum customType = fxxkCrm.getCustomType();
                String customName = fxxkCrm.getCustomName();
                String customProvince = fxxkCrm.getCustomProvince();
                String customCity = fxxkCrm.getCustomCity();
                String customArea = fxxkCrm.getCustomArea();
                String customAddress = fxxkCrm.getCustomAddress();
                Long examStartTime = fxxkCrm.getExamStartTime();
                Long examEndTime = fxxkCrm.getExamEndTime();

                if (crmName == null || crmName.length() == 0) {
                    basicInfoMissingError = basicInfoMissingError + "缺少派单名称";
                }
                if (beginTime == null || beginTime == 0) {
                    basicInfoMissingError = basicInfoMissingError + "缺少派单开始时间";
                }
                if (managerName == null || managerName.length() == 0) {
                    basicInfoMissingError = basicInfoMissingError + "缺少客户经理";
                }
                if (managerMobileNumber == null || managerMobileNumber.length() == 0) {
                    basicInfoMissingError = basicInfoMissingError + "缺少客户经理电话";
                }
                if (customType == null) {
                    basicInfoMissingError = basicInfoMissingError + "缺少客户类型或客户类型不为['高校教务处'或'研究生招办']";
                }
                if (customName == null || customName.length() == 0) {
                    basicInfoMissingError = basicInfoMissingError + "缺少客户名称";
                }
                if (examStartTime == null || examStartTime == 0) {
                    basicInfoMissingError = basicInfoMissingError + "缺少考试开始时间";
                }
                if (examEndTime == null || examEndTime == 0) {
                    basicInfoMissingError = basicInfoMissingError + "缺少考试结束时间";
                }
                if (basicInfoMissingError.length() > 0) {
                    // fxxk 项目派单基础信息缺失异常 -> 补充fxxk系统的派单基础信息
                    basicInfoMissingError = String.format("纷享销客派单[%s]基础信息异常请补充: %s \n", crmNo, basicInfoMissingError);
                    errorMsgList.add(basicInfoMissingError);
                } else {
                    // 基础信息正常再继续解析需要和质控平台对接的信息
                    String matchInfoError = "";

                    // 客户经理校验
                    Long crmUserId = null;
                    SysUser sysUser = sysUserService.getOne(
                            new QueryWrapper<SysUser>().lambda().eq(SysUser::getMobileNumber, managerMobileNumber)
                                    .eq(SysUser::getRealName, managerName));
                    if (Objects.isNull(sysUser) || !sysUser.getEnable()) {
                        matchInfoError = matchInfoError + String.format("纷享销客中的客户经理[%s(%s)]在质控平台不存在或已禁用(姓名 + 手机号)\n",
                                managerName, managerMobileNumber);
                    } else {
                        List<RoleDto> roleDtoList = sysRoleService.listRolesByUserId(sysUser.getId());
                        if (roleDtoList.stream().noneMatch(e -> RoleTypeEnum.ACCOUNT_MANAGER.equals(e.getType()))) {
                            matchInfoError =
                                    matchInfoError + String.format("纷享销客中的客户经理[%s(%s)]在质控平台缺少[%s]角色)\n", managerName,
                                            managerMobileNumber, RoleTypeEnum.ACCOUNT_MANAGER);
                        } else {
                            crmUserId = sysUser.getId();
                        }
                    }

                    // 系统客户校验
                    Long customId = null;
                    List<SysCustom> sysCustomList = sysCustomService.list(
                            new QueryWrapper<SysCustom>().lambda().eq(SysCustom::getName, customName)
                                    .eq(SysCustom::getType, customType));

                    if (CollectionUtils.isEmpty(sysCustomList)) {
                        if (customProvince == null || customProvince.length() == 0 || customCity == null
                                || customCity.length() == 0) {
                            // 客户不存在且客户信息缺失
                            String customMissKey = customName + "-" + customType;
                            if (!customMissList.contains(customMissKey)) {
                                customMissList.add(customMissKey);
                            }
                        } else {
                            // 质控平台不存在该类型客户 -> 新增一个客户
                            SysCustom sysCustom = new SysCustom();
                            sysCustom.setName(customName);
                            sysCustom.setCode(sequenceService.createSerialNo(SequenceEnum.SYS_CUSTOM_CODE));
                            sysCustom.setType(customType);
                            sysCustom.setProvince(customProvince);
                            sysCustom.setCity(customCity);
                            if (customArea != null && customArea.length() > 0) {
                                sysCustom.setArea(customArea);
                            }
                            if (customAddress != null && customAddress.length() > 0) {
                                sysCustom.setAddress(customAddress);
                            }
                            sysCustom.insertInfo(requestUser.getId());
                            sysCustomService.save(sysCustom);
                            customId = sysCustom.getId();
                        }
                    } else if (sysCustomList.size() > 1) {
                        throw ExceptionResultEnum.ERROR.exception("客户数据异常 ：客户名称-客户类型 不唯一\n");
                    } else {
                        SysCustom sysCustom = sysCustomList.get(0);
                        customId = sysCustom.getId();
                    }

                    // 实施产品校验
                    Long productId = null;
                    List<TBProduct> tbProductList = tbProductService.list(
                            new QueryWrapper<TBProduct>().lambda().eq(TBProduct::getEnable, true));
                    if (CollectionUtils.isEmpty(tbProductList)) {
                        matchInfoError = matchInfoError + "质控平台缺少可用产品\n";
                    } else {
                        productId = tbProductList.get(0).getId();
                    }
                    if (matchInfoError.length() > 0) {
                        // 质控平台基础信息缺失异常 -> 补充质控平台系统的派单相关信息
                        matchInfoError = String.format("纷享销客派单[%s]中的相关信息在质控平台中缺失,请先补充质控平台系统的派单相关信息: %s \n", crmNo,
                                matchInfoError);
                        errorMsgList.add(matchInfoError);
                    } else {
                        // 新增
                        TBCrm tbCrm = new TBCrm();
                        tbCrm.setCrmNo(crmNo);
                        tbCrm.setName(crmName);
                        tbCrm.setBeginTime(beginTime);
                        tbCrm.setCrmUserId(crmUserId);
                        tbCrm.setCustomId(customId);
                        tbCrm.setExamStartTime(examStartTime);
                        tbCrm.setExamEndTime(examEndTime);
                        tbCrm.setProductId(productId);
                        tbCrm.setSync(true);
                        tbCrm.setEnable(true);
                        tbCrm.setStatus(CrmStatusEnum.UN_PUBLISH);
                        tbCrm.insertInfo(requestUser.getId());
                        tbCrm.updateInfo(requestUser.getId());
                        this.save(tbCrm);
                        successCount++;
                    }
                }
            }
            String errorMsg = "";
            if (errorMsgList.size() > 0) {
                errorMsg = String.join(";\n", errorMsgList);
            }
            if (CollectionUtils.isNotEmpty(customMissList)) {
                // 发送短信
                SmsSendUtil smsSendUtil = SpringContextHolder.getBean(SmsSendUtil.class);
                String configKey = SystemConstant.SMS_CUSTOM_INFO_MISSING_REMIND_CODE;
                String mobileNumber = "18903719928";
                SysUser signature = sysUserService.getOne(
                        new QueryWrapper<SysUser>().lambda().eq(SysUser::getMobileNumber, mobileNumber)
                                .last(SystemConstant.LIMIT1));
                if (Objects.nonNull(signature)) {
                    String userName = signature.getRealName();
                    String customInfo = String.join(";", customMissList);
                    Map<String, Object> templateParam = new HashMap<>();
                    templateParam.put("userName", userName);
                    templateParam.put("customInfo", customInfo);

                    String content = MessageEnum.NOTICE_OF_CUSTOM_MISSING_REMIND.getTemplate()
                            .replaceAll("\\$\\{userName\\}", userName).replaceAll("\\$\\{customInfo\\}", customInfo);
                    SysMessage sysMessage = new SysMessage(null, null, MessageTypeEnum.CUSTOM_INFO_MISSING, null, null,
                            null, content, null);

                    //发送消息
                    sysMessageService.saveSysMessage(Collections.singletonList(sysMessage));

                    //发送短信
                    smsSendUtil.sendSms(mobileNumber, configKey, templateParam);
                }
            }
            map.put(SystemConstant.SUCCESS, successCount);
            map.put(SystemConstant.RESULT_ERROR, totalCount - successCount - alreadyCount);
            map.put(SystemConstant.ALREADY_COUNT, alreadyCount);
            map.put(SystemConstant.ERROR_MSG, errorMsg);
            return map;
        } catch (Exception e) {
            throw ExceptionResultEnum.ERROR.exception(e.getMessage());
        }
    }

    @Override
    public CrmBindResult findCrmBindInfo(Long serviceUnitId, Long customId) {
        RegionManagerSelectedTypeEnum leadType = null;
        Long leadId = null;
        boolean canEdit = true;
        SysCustom sysCustom = sysCustomService.getById(customId);
        if (Objects.isNull(sysCustom)) {
            throw ExceptionResultEnum.ERROR.exception("未找到客户");
        }

        List<TBCrm> sameCrmList = this.list(new QueryWrapper<TBCrm>().lambda().eq(TBCrm::getCustomId, customId)
                .eq(TBCrm::getServiceId, serviceUnitId));
        if (CollectionUtils.isNotEmpty(sameCrmList)) {
            // 对于同一服务单元同客户下有发布sop的，不允许更换大区经理，上述两图中内容置灰，不可更换大区经理。一旦更换大区经理，该客户的所有派单均更换大区经理
            List<RegionManagerSelectedTypeEnum> leadTypeList = sameCrmList.stream().map(TBCrm::getLeadType).distinct()
                    .collect(Collectors.toList());
            if (leadTypeList.size() != 1) {
                throw ExceptionResultEnum.ERROR.exception("客户在服务单元下存在不同的大区经理指定方式");
            }
            leadType = leadTypeList.get(0);
            List<Long> leadIdList = sameCrmList.stream().map(TBCrm::getLeadId).distinct().collect(Collectors.toList());
            if (leadIdList.size() != 1) {
                throw ExceptionResultEnum.ERROR.exception("客户在服务单元下存在不同的大区经理");
            }
            leadId = leadIdList.get(0);
            if (sameCrmList.stream().anyMatch(e -> CrmStatusEnum.PUBLISH.equals(e.getStatus()))) {
                canEdit = false;
            }
        }
        CrmBindResult crmBindResult = new CrmBindResult();
        crmBindResult.setSimilarCrmIdList(
                sameCrmList.stream().map(TBCrm::getId).distinct().collect(Collectors.toList()));
        crmBindResult.setCanEdit(canEdit);
        if (leadId != null) {
            SysUser leader = sysUserService.getById(leadId);
            if (Objects.isNull(leader)) {
                throw ExceptionResultEnum.ERROR.exception("未找到大区经理用户");
            }
            crmBindResult.setLeadId(leadId);
            crmBindResult.setLeadName(leader.getRealName());
        }
        crmBindResult.setLeadType(leadType);
        crmBindResult.setCustomName(sysCustom.getName());
        return crmBindResult;
    }

    @Override
    public void export(Long serviceUnitId) {
        File fileTemp = null;
        try {
            fileTemp = SystemConstant.getFileTempVar(SystemConstant.XLSX_PREFIX);
            InputStream inputStream = this.getClass().getResourceAsStream("/" + SystemConstant.STATIC + "/" +
                    ImportTemplateEnum.TEMPLATE_CRM_CUSTOM_RECEIVER.getTemplateName());
            ExcelWriter writer = EasyExcel.write(fileTemp).withTemplate(inputStream).build();
            WriteSheet sheet = EasyExcel.writerSheet().build();
            FillConfig fillConfig = FillConfig.builder().forceNewRow(true).build();

            List<CrmCustomImportDto> dataList = baseMapper.listCrmCustomByServiceId(serviceUnitId);

            //过滤一个客户对应多个收件人的数据
            Function<CrmCustomImportDto,List<Object>> compositeKey = crmCustom -> Arrays.asList(crmCustom.getCrmNo(),crmCustom.getCustomName());
            Map<List<Object>, List<CrmCustomImportDto>> customGroupByList = dataList.stream().collect(Collectors.groupingBy(compositeKey, Collectors.toList()));
            List<CrmCustomImportDto> customDtoList = filterCrmCustom(customGroupByList);

            writer.fill(customDtoList, fillConfig, sheet);
            writer.finish();
            HttpServletResponse response = ServletUtil.getResponse();
            FileUtil.outputFile(response, fileTemp, ImportTemplateEnum.TEMPLATE_CRM_CUSTOM_RECEIVER.getFileName());
        } catch (IOException e) {
            throw ExceptionResultEnum.ERROR.exception(e.getMessage());
        } finally {
            if (Objects.nonNull(fileTemp)) {
                fileTemp.delete();
            }
        }
    }

    private List<CrmCustomImportDto> filterCrmCustom(Map<List<Object>, List<CrmCustomImportDto>> customGroupByList) {
        List<CrmCustomImportDto> customDtoList = new ArrayList<>();
        Set<Map.Entry<List<Object>, List<CrmCustomImportDto>>> entries = customGroupByList.entrySet();
        for (Map.Entry<List<Object>, List<CrmCustomImportDto>> entry : entries) {
            List<CrmCustomImportDto> crmCustomList = entry.getValue();
            if (CollectionUtils.isEmpty(crmCustomList)) {
                continue;
            }
            if (crmCustomList.size() == 1) {
                customDtoList.addAll(crmCustomList);
            } else if (crmCustomList.size() > 1) {
                CrmCustomImportDto dto = new CrmCustomImportDto();
                dto.setCrmNo(crmCustomList.get(0).getCrmNo());
                customDtoList.add(dto);
            }
        }
        return customDtoList;
    }

    /**
     * 根据sop用户身份类型构建用户档案信息
     *
     * @param datasource      数据源
     * @param sopRoleTypeEnum 身份
     * @return 用户档案信息
     */
    private List<UserArchivesDto> buildUserArchivesDtoList(List<TBUserArchivesAllocation> datasource,
            SopRoleTypeEnum sopRoleTypeEnum) {
        return datasource.stream().filter(e -> sopRoleTypeEnum.equals(e.getSopRoleType())).flatMap(e -> {
            UserArchivesDto dto = new UserArchivesDto();
            Long userArchivesId = e.getArchivesId();
            UserArchivesResult userArchivesResult = tbUserArchivesService.findUserArchivesByArchivesIdORUserId(
                    userArchivesId, null);
            dto.setUserArchivesId(userArchivesId);
            dto.setUserId(e.getUserId());
            dto.setName(tbUserArchivesService.getById(userArchivesId).getName());
            dto.setProvince(userArchivesResult.getProvince());
            dto.setCity(userArchivesResult.getCity());
            dto.setSupplierName(userArchivesResult.getSupplierName());
            return Stream.of(dto);
        }).collect(Collectors.toList());
    }
}
