package digital.laboratory.platform.imr.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import digital.laboratory.platform.common.core.constant.CommonConstants;
import digital.laboratory.platform.common.core.exception.ValidateCodeException;
import digital.laboratory.platform.common.feign.RemoteTemplate2htmlService;
import digital.laboratory.platform.common.mybatis.security.service.DLPUser;
import digital.laboratory.platform.imr.convert.DrugMaterialInfoConvert;
import digital.laboratory.platform.imr.dto.DrugMaterialInfoDTO;
import digital.laboratory.platform.imr.dto.InRepositoryDTO;
import digital.laboratory.platform.imr.entity.*;
import digital.laboratory.platform.imr.enums.DrugMaterialStatus;
import digital.laboratory.platform.imr.mapper.DrugMaterialInfoMapper;
import digital.laboratory.platform.imr.mapper.SampleStorageMapper;
import digital.laboratory.platform.imr.query.DrugDepotsQuery;
import digital.laboratory.platform.imr.service.*;
import digital.laboratory.platform.imr.vo.DrugMaterialInfoVO;
import digital.laboratory.platform.imr.vo.OutSampleVO;
import digital.laboratory.platform.sys.entity.Drug;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author ChenJiangBao
 * @description 针对表【b_drug_material_info(毒品库中的毒品检材信息)】的数据库操作Service实现
 * @createDate 2024-11-07 12:04:42
 */
@Service
public class DrugMaterialInfoServiceImpl extends ServiceImpl<DrugMaterialInfoMapper, DrugMaterialInfo>
        implements DrugMaterialInfoService {

    @Resource
    private RemoteTemplate2htmlService remoteTemplate2htmlService;

    @Resource
    private SampleStorageMapper sampleStorageMapper;

    @Resource
    private SampleStorageService sampleStorageService;

    @Resource
    private SampleInboundAndOutboundTableService sampleInboundAndOutboundTableService;

    @Resource
    private SampleInboundRecordService sampleInboundRecordService;

    @Resource
    private DrugHandingOverApplyService drugHandingOverApplyService;

    @Resource
    private CommonFeignService commonFeignService;
    /**
     * 批量生成毒品检材编号
     * JC5201010020240001
     * JC 标识是检材 520100 地市行政编码 00 预留给派出所 2024 年份 0001 流水号
     *
     * @param drugCaseInfo 案件信息
     * @param number
     * @return
     */
    @Override
    public List<String> batchBuildDrugMaterialNO(DrugCaseInfo drugCaseInfo, Integer number) {
        DrugHandingOverApply handingOverApply = drugHandingOverApplyService.getById(drugCaseInfo.getApplyId());
        Calendar calendar = Calendar.getInstance();
        String prefixNO = CommonConstants.CODE_PREFIX_IDENTIFICATION_MATERIAL + handingOverApply.getHandingOverOrg() + "00" + calendar.get(Calendar.YEAR);
        List<DrugMaterialInfo> drugMaterialInfos = this.list(Wrappers.<DrugMaterialInfo>lambdaQuery()
                .likeRight(DrugMaterialInfo::getDrugNo, prefixNO)
                .orderByDesc(DrugMaterialInfo::getDrugNo));
        Integer startNO = 0;
        if (CollUtil.isNotEmpty(drugMaterialInfos)) {
            DrugMaterialInfo drugMaterialInfo = drugMaterialInfos.get(0);
            startNO = Integer.parseUnsignedInt(StrUtil.removePrefixIgnoreCase(drugMaterialInfo.getDrugNo(), prefixNO));
        }

        List<String> drugNoList = new ArrayList<>();
        for (int i = 0; i < number; i++) {
            drugNoList.add(prefixNO + String.format("%04d", ++startNO));
        }
        return drugNoList;
    }

    /**
     * 批量保存毒品检材信息
     *
     * @param drugMaterialInfos
     * @param drugCaseInfo
     * @return
     */
    @Override
    public boolean saveDrugMaterialBatch(List<DrugMaterialInfo> drugMaterialInfos, DrugCaseInfo drugCaseInfo) {
        int size = drugMaterialInfos.size();
        // 获取编号
        List<String> drugMaterialNOList = this.batchBuildDrugMaterialNO(drugCaseInfo, size);
        for (int i = 0; i < size; i++) {
            DrugMaterialInfo drugMaterialInfo = drugMaterialInfos.get(i);
            drugMaterialInfo.setDrugNo(drugMaterialNOList.get(i));
            drugMaterialInfo.setCaseId(drugCaseInfo.getId());
            drugMaterialInfo.setStatus(DrugMaterialStatus.ENTER_DRUG_INFO.getStatus());
        }
        return super.saveBatch(drugMaterialInfos);
    }

    /**
     * 复制检材
     * @param dto
     * @return
     */
    @Override
    public DrugMaterialInfoVO copy(DrugMaterialInfoDTO dto) {
        DrugMaterialInfo drugMaterialInfo = DrugMaterialInfoConvert.dtoToEntity(dto);
//        if (super.count(Wrappers.<DrugMaterialInfo>lambdaQuery().eq(DrugMaterialInfo::getCaseId, dto.getCaseId())) == 0) {
//            // 如果当前案件是第一次保存检材，则都保存当前检材和复制的检材
//            super.save(drugMaterialInfo);
//        }
//        drugMaterialInfo = DrugMaterialInfoConvert.dtoToEntity(dto);
        drugMaterialInfo.setId(null);
        super.save(drugMaterialInfo);
        List<DrugMaterialInfoVO> drugMaterialInfoVOS = baseMapper.getDrugMaterialVO(Wrappers.<DrugMaterialInfo>lambdaQuery()
                .eq(DrugMaterialInfo::getId, drugMaterialInfo.getId()));
        // 检查是否获取到 VO
        if (!drugMaterialInfoVOS.isEmpty()) {
            return drugMaterialInfoVOS.get(0);
        } else {
            // 处理未找到 VO 的情况
            throw new RuntimeException("Failed to retrieve the DrugMaterialInfoVO for the saved record.");
        }
    }

    /**
     * 分页查询毒品库中的毒品检材信息VO列表
     *
     * @param query
     * @return
     */
    @Override
    public IPage<DrugMaterialInfoVO> page(DrugDepotsQuery query) {
        IPage<DrugMaterialInfoVO> drugMaterialVOPage = baseMapper.getDrugMaterialVOPage(new Page<>(query.getCurrent(), query.getSize()), query);
        List<DrugMaterialInfoVO> records = drugMaterialVOPage.getRecords();
        records.parallelStream().forEach(record -> {
            record.setHandingOverOrgName(commonFeignService.remoteGetSysOrg(record.getHandingOverOrg()).getName());
            record.setStatusName(DrugMaterialStatus.fromStatus(record.getStatus()).getDesc());
        });
        return drugMaterialVOPage;
    }

    @Override
    public String buildDrugLabelHtml(String id) {
        List<DrugMaterialInfoVO> drugMaterialInfoVOList = baseMapper.getDrugMaterialVO(Wrappers.<DrugMaterialInfo>query().eq("dm.id", id));
        if (CollUtil.isEmpty(drugMaterialInfoVOList)) {
            throw new RuntimeException(String.format("没有找到 id 为 %s 的毒品检材", id));
        }
        DrugMaterialInfoVO drugMaterialInfoVO = drugMaterialInfoVOList.get(0);
        drugMaterialInfoVO.setHandingOverOrgName(commonFeignService.remoteGetSysOrg(drugMaterialInfoVO.getHandingOverOrg()).getName());
        Map<String, Object> data = new HashMap<>();
        data.put("drugInfo", drugMaterialInfoVO);

        String templateFileName = "毒品库毒品检材条码模板.vm";
        return remoteTemplate2htmlService.getHtml(templateFileName, data);
    }

    /***
     * 毒品检材入库
     * @param sample
     * @param dlpUser
     * @return
     */
    @Override
    @Transactional
    public List<OutSampleVO> drugMaterialPutInRepository(List<InRepositoryDTO> sample, DLPUser dlpUser) {
        // 一次性获取到所有的入库毒品检材信息,并根据编号转成map
        List<DrugMaterialInfoVO> drugMaterialInfoVOS = baseMapper
                .getDrugMaterialVO(Wrappers.<DrugMaterialInfo>query()
                        .in("drug_no",
                                sample.stream().map(InRepositoryDTO::getSampleNo).collect(Collectors.toList()))
                        .in("dm.status", DrugMaterialStatus.ENTER_DRUG_INFO.getStatus(), DrugMaterialStatus.OUTBOUND.getStatus())); // 状态为为未入库或已出库的
        if (CollUtil.isEmpty(drugMaterialInfoVOS)) {
            return Collections.emptyList();
        }
        Map<String, DrugMaterialInfoVO> drugMaterialInfoVOMap = drugMaterialInfoVOS.stream()
                .collect(Collectors.toMap(DrugMaterialInfoVO::getDrugNo, Function.identity()));
        // 待操作的毒品检材id列表
        List<String> waitOpDrugIds = drugMaterialInfoVOS.stream().map(DrugMaterialInfoVO::getId).collect(Collectors.toList());
        List<SampleStorage> sampleStorages = sampleStorageMapper.selectList(Wrappers.<SampleStorage>lambdaQuery()
                        .in(SampleStorage::getSampleId, waitOpDrugIds));
        Map<String, SampleStorage> sampleStorageMap = CollUtil.isEmpty(sampleStorages) ?
                new HashMap<>() : sampleStorages.stream().collect(Collectors.toMap(SampleStorage::getSampleId, Function.identity()));
        // 存储需要批量保存的实体信息
        List<SampleStorage> updateSampleStorageList = new ArrayList<>();
        List<SampleStorage> saveSampleStorageList = new ArrayList<>();
        List<SampleInboundAndOutboundTable> saveSampleInboundAndOutboundTableList = new ArrayList<>();
        List<SampleInboundRecord> saveSampleInboundRecordList = new ArrayList<>();
        ArrayList<OutSampleVO> sampleVOS = new ArrayList<>();
        for (InRepositoryDTO dto : sample) {
            //先通过样本编号查询出这个样本的基本信息(重点是获取到id)
            DrugMaterialInfoVO drugMaterialInfoVO = drugMaterialInfoVOMap.get(dto.getSampleNo());
            if (drugMaterialInfoVO == null) {
//                throw new ValidateCodeException(String.format("毒品检材编号为 %s 的毒品信息不存在请重试!", dto.getSampleNo()));
                continue;
            }
            // 查询当前毒品检材是否已经录入过
            SampleStorage sampleStorage = sampleStorageMap.get(drugMaterialInfoVO.getId());
            //已经录入到关联表;待存放状态
            if (sampleStorage != null) {
                if (sampleStorage.getStatus() == 1 || sampleStorage.getStatus() == 2) {
                    throw new RuntimeException(String.format("编号为" + dto.getSampleNo() + "的样本已录入"));
                } else if (sampleStorage.getStatus() == 3) {//已出库;现在是重新入库
                    //重新入库是更新(二次入库)
                    sampleStorage.setInRepositoryDate(LocalDateTime.now());
                    sampleStorage.setRecipientId(null);
                    sampleStorage.setDepositorId(dlpUser.getId());
                    sampleStorage.setStatus(1);
                    sampleStorage.setOutRepositoryDate(null);
//                    sampleStorageMapper.updateById(sampleStorage);
                    updateSampleStorageList.add(sampleStorage);
                }
            } else {
                //新录入仓库样本(添加操作)
                sampleStorage = getSampleStorage(dlpUser, dto, drugMaterialInfoVO);
                //数据存储
//                    sampleStorageMapper.insert(sampleStorage);
                saveSampleStorageList.add(sampleStorage);
            }

            //更新出入库登记表(这里因为只有入库,所以就创建部分数据;入库一定是添加数据)
            SampleInboundAndOutboundTable table = getSampleInboundAndOutboundTable(dlpUser, dto, drugMaterialInfoVO);
//                tableMapper.insert(table);
            saveSampleInboundAndOutboundTableList.add(table);
            //新出入库登记表

            //入库记录
            SampleInboundRecord record = getSampleInboundRecord(dlpUser, drugMaterialInfoVO);
//                recordMapper.insert(record);
            saveSampleInboundRecordList.add(record);

            OutSampleVO outSampleVO = getOutSampleVO(sampleStorage, drugMaterialInfoVO);
            sampleVOS.add(outSampleVO);
        }

        sampleStorageService.updateBatchById(updateSampleStorageList);
        sampleStorageService.saveBatch(saveSampleStorageList);
        sampleInboundAndOutboundTableService.saveBatch(saveSampleInboundAndOutboundTableList);
        sampleInboundRecordService.saveBatch(saveSampleInboundRecordList);
        // 修改当前修改过的毒品状态为待入库
        super.update(Wrappers.<DrugMaterialInfo>lambdaUpdate().in(DrugMaterialInfo::getId, waitOpDrugIds).set(DrugMaterialInfo::getStatus, DrugMaterialStatus.WAIT_INBOUND.getStatus()));
        return sampleVOS;
    }


    /**
     * 毒品检材封装
     * @param sampleStorage
     * @param drugId
     * @return
     */
    @Override
    public OutSampleVO drugMaterialToOutSampleVO(SampleStorage sampleStorage, String drugId) {
        List<DrugMaterialInfoVO> drugMaterialVOs = baseMapper.getDrugMaterialVO(Wrappers.<DrugMaterialInfo>query().eq("dm.id", drugId));
        if (CollUtil.isEmpty(drugMaterialVOs)) {
            return null;
        }
        return getOutSampleVO(sampleStorage, drugMaterialVOs.get(0));
    }

    /**
     * 删除毒品检材
     * @param ids
     * @return
     */
    @Override
    public boolean delete(List<String> ids) {
        if (CollUtil.isEmpty(ids)) {
            throw new ValidateCodeException("未选择删除的检材！");
        }
        List<DrugMaterialInfo> drugMaterialInfos = super.list(Wrappers.<DrugMaterialInfo>lambdaQuery().in(DrugMaterialInfo::getId, ids));
        for (DrugMaterialInfo drugMaterialInfo : drugMaterialInfos) {
            if (!drugMaterialInfo.getStatus().equals(DrugMaterialStatus.ENTER_DRUG_INFO.getStatus())
                    && !drugMaterialInfo.getStatus().equals(DrugMaterialStatus.WAIT_INBOUND.getStatus())) {
                throw new ValidateCodeException(String.format("检材编号为 %s 的检材不在可删除状态，不能删除！", drugMaterialInfo.getDrugNo()));
            }
        }
        return super.remove(Wrappers.<DrugMaterialInfo>lambdaQuery().in(DrugMaterialInfo::getId, ids));
    }

    /**
     * 根据样本存储信息和药物材料信息生成出库样本对象
     *
     * @param sampleStorage        样本存储信息
     * @param drugMaterialInfoVO   药物材料信息对象
     * @return 出库样本对象
     */
    private OutSampleVO getOutSampleVO(SampleStorage sampleStorage, DrugMaterialInfoVO drugMaterialInfoVO) {
        OutSampleVO outSampleVO = new OutSampleVO();
        outSampleVO.setId(sampleStorage.getId());
        outSampleVO.setSampleId(drugMaterialInfoVO.getId());
        outSampleVO.setName(drugMaterialInfoVO.getDrugName());
        outSampleVO.setCategory("常规毒品");
        outSampleVO.setSampleNo(drugMaterialInfoVO.getDrugNo());
        outSampleVO.setStorageMethod("常规");
        outSampleVO.setStorageLocation(drugMaterialInfoVO.getStorageLocation());
        outSampleVO.setSampleStatus(1);
        return outSampleVO;
    }

    /**
     * 根据提供的用户信息、入库DTO和药物材料信息对象获取样本存储对象
     *
     * @param dlpUser             用户信息对象
     * @param dto                 入库DTO
     * @param drugMaterialInfoVO  药物材料信息对象
     * @return 样本存储对象
     */
    private SampleStorage getSampleStorage(DLPUser dlpUser, InRepositoryDTO dto, DrugMaterialInfoVO drugMaterialInfoVO) {
        SampleStorage sampleStorage;
        sampleStorage = new SampleStorage();
        sampleStorage.setId(IdWorker.get32UUID().toUpperCase());
        sampleStorage.setStatus(1);//入库待存放
        sampleStorage.setDepositorId(dlpUser.getId());//当前持有人变成入库人
        sampleStorage.setInRepositoryDate(LocalDateTime.now());//入库时间

        //毒品和其他存储时间为长期(100年)
        sampleStorage.setStorageDate(LocalDateTime.now().plusYears(100));

        sampleStorage.setSampleId(drugMaterialInfoVO.getId()); //毒品检材id
        sampleStorage.setEarlyWarning(0);//创建默认未到"销毁"
        sampleStorage.setUnit(dto.getUnit());//单位
        sampleStorage.setQuality(dto.getQuality());//入库时的质量
        sampleStorage.setName(drugMaterialInfoVO.getDrugName());
        sampleStorage.setSampleType("A");//样本类型
        return sampleStorage;
    }

    /**
     * 根据提供的用户信息、入库DTO和药物材料信息对象获取样本入库出库表对象
     *
     * @param dlpUser             用户信息对象
     * @param dto                 入库DTO
     * @param drugMaterialInfoVO  药物材料信息对象
     * @return 样本入库出库表对象
     */
    private SampleInboundAndOutboundTable getSampleInboundAndOutboundTable(DLPUser dlpUser, InRepositoryDTO dto, DrugMaterialInfoVO drugMaterialInfoVO) {
        SampleInboundAndOutboundTable table = new SampleInboundAndOutboundTable();
        table.setId(IdWorker.get32UUID().toUpperCase());
        table.setImAdministrators(dlpUser.getId());
        table.setRecipient(dlpUser.getId());//持有人变入库人
        table.setRemarks("检材入库");
        table.setWarehousingDate(LocalDateTime.now());//入库时间
        table.setSampleId(drugMaterialInfoVO.getId());
        table.setInboundSupervisor(dto.getSupervisor());//入库监督人
        return table;
    }

    /**
     * 根据提供的用户信息和药物材料信息对象获取样本入库记录对象
     *
     * @param dlpUser             用户信息对象
     * @param drugMaterialInfoVO  药物材料信息对象
     * @return 样本入库记录对象
     */
    private SampleInboundRecord getSampleInboundRecord(DLPUser dlpUser, DrugMaterialInfoVO drugMaterialInfoVO) {
        SampleInboundRecord record = new SampleInboundRecord();
        record.setId(IdWorker.get32UUID().toUpperCase());
        record.setWarehousingPersonId(dlpUser.getId());//入库人
        record.setWarehousingDate(LocalDateTime.now());
        record.setImAdministrators(dlpUser.getId());
        record.setSampleId(drugMaterialInfoVO.getId());
        record.setName(drugMaterialInfoVO.getDrugName());
        record.setRemarks("检材入库");
        return record;
    }
}




