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

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.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.feign.RemoteTemplate2htmlService;
import digital.laboratory.platform.common.mybatis.security.service.DLPUser;
import digital.laboratory.platform.imr.dto.OutWarehouseApplyDTO;
import digital.laboratory.platform.imr.dto.VerificationCode;
import digital.laboratory.platform.imr.entity.*;
import digital.laboratory.platform.imr.enums.OutApplyStatus;
import digital.laboratory.platform.imr.enums.OutboundType;
import digital.laboratory.platform.imr.feign.TransferFeignService;
import digital.laboratory.platform.imr.feign.dto.UpdateHolderDTO;
import digital.laboratory.platform.imr.mapper.*;
import digital.laboratory.platform.imr.service.CommonFeignService;
import digital.laboratory.platform.imr.service.SampleOutWarehouseApplyService;
import digital.laboratory.platform.imr.vo.BaseSampleDetail;
import digital.laboratory.platform.imr.vo.CellVO;
import digital.laboratory.platform.imr.vo.OutSampleVO;
import digital.laboratory.platform.imr.vo.SampleOutWarehouseApplyVO;
import digital.laboratory.platform.sys.entity.CellAndStoreSupplyData;
import digital.laboratory.platform.sys.feign.RemoteCabinetService;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

/**
 * 样本出库申请实体类服务实现类
 *
 * @author Zhang Xiaolong created at  2023-03-17
 * @describe 样本出库申请实体类 服务实现类
 */
@Service
@RequiredArgsConstructor
public class SampleOutWarehouseApplyServiceImpl extends ServiceImpl<SampleOutWarehouseApplyMapper, SampleOutWarehouseApply> implements SampleOutWarehouseApplyService {

    private final TransferFeignService transferFeignService;


    private final RemoteTemplate2htmlService remoteTemplate2htmlService;

    private final CommonFeignService commonFeignService;

    private final SampleOutWarehouseApplyMapper sawMapper;

    private final OutWarehouseDetailedMapper owdMapper;

    private final SampleStorageMapper storageMapper;

    private final SampleInboundAndOutboundTableMapper tableMapper;

    private final SampleOutboundRecordMapper outboundRecordMapper;

    /**
     * 创建出库申请(检验人员使用)
     *
     * @param dto
     * @param dlpUser
     * @return
     */
    @Override
    @Transactional
    public SampleOutWarehouseApplyVO createOutApply(OutWarehouseApplyDTO dto, DLPUser dlpUser) {
        //
        SampleOutWarehouseApply sampleOutWarehouseApply = new SampleOutWarehouseApply();
        sampleOutWarehouseApply.setReturnDate(dto.getReturnDate());//归还时间
        sampleOutWarehouseApply.setPurpose(dto.getPurpose());
        sampleOutWarehouseApply.setAppraiserId(dto.getAppraiserId());
        sampleOutWarehouseApply.setId(IdWorker.get32UUID().toUpperCase());
        sampleOutWarehouseApply.setApplicantId(dlpUser.getId());//申请人
        sampleOutWarehouseApply.setApplicationDate(LocalDateTime.now());//申请时间
        sampleOutWarehouseApply.setStatus(OutApplyStatus.TO_BE_CONFIRMED);//申请状态
        //添加数据
        sawMapper.insert(sampleOutWarehouseApply);
        //创建出库申请明细
        List<String> sampleIds = dto.getSampleIds();
        for (String sampleId : sampleIds) {
            OutWarehouseDetailed outWarehouseDetailed = new OutWarehouseDetailed();
            outWarehouseDetailed.setId(IdWorker.get32UUID().toUpperCase());
            outWarehouseDetailed.setSampleId(sampleId);
            outWarehouseDetailed.setApplyId(sampleOutWarehouseApply.getId());
            owdMapper.insert(outWarehouseDetailed);
            //创建完成以后先修改sample状态为"领用出库申请中"
            transferFeignService.updateStatus(sampleId, 9);
        }
        return sawMapper.getOutApplyDetail(sampleOutWarehouseApply.getId());
    }


    /**
     * 创建出库申请方法2
     *
     * @param dto
     * @param dlpUser
     * @return
     */
    @Override
    public SampleOutWarehouseApplyVO createOutRepositoryApply(OutWarehouseApplyDTO dto, DLPUser dlpUser) {
        //
        SampleOutWarehouseApply sampleOutWarehouseApply = new SampleOutWarehouseApply();
        sampleOutWarehouseApply.setReturnDate(dto.getReturnDate());
        sampleOutWarehouseApply.setPurpose(dto.getPurpose());
        sampleOutWarehouseApply.setAppraiserId(dto.getAppraiserId());
        sampleOutWarehouseApply.setId(IdWorker.get32UUID().toUpperCase());
        sampleOutWarehouseApply.setApplicantId(dlpUser.getId());//申请人
        sampleOutWarehouseApply.setApplicationDate(LocalDateTime.now());//申请时间
        sampleOutWarehouseApply.setStatus(OutApplyStatus.TO_BE_CONFIRMED);//申请状态
        /**
         * 创建"取检材"码(这个码不应该重复)
         * 两种办法
         * 1.生成码先查询数据库重复就重新生成
         * 2.将已经使用出库的码设置为空
         */
        //String sampleCode = this.getSampleCode();
        //sampleOutWarehouseApply.setSampleCodeNumber(sampleCode);
        this.save(sampleOutWarehouseApply);
        //创建出库申请明细
        List<String> sampleIds = dto.getSampleIds();
        for (String sampleId : sampleIds) {
            OutWarehouseDetailed outWarehouseDetailed = new OutWarehouseDetailed();
            outWarehouseDetailed.setId(IdWorker.get32UUID().toUpperCase());
            outWarehouseDetailed.setSampleId(sampleId);
            outWarehouseDetailed.setApplyId(sampleOutWarehouseApply.getId());
            owdMapper.insert(outWarehouseDetailed);
        }
        return sawMapper.getSampleOutRepositoryInfo(sampleOutWarehouseApply.getId());
    }

    /**
     * 领用出库确认
     * 1.生成取检码(不能重复)
     * 2.修改状态
     * 3.添加检材管理员
     *
     * @param id
     * @return
     */
    @Override
    @Transactional
    public SampleOutWarehouseApplyVO reconfirmApply(String id, String userId) {

        SampleOutWarehouseApplyVO vo = sawMapper.getOutApplyDetail(id);
        if (vo == null) {
            throw new RuntimeException("当前数据不存在");
        }

        if (vo.getStatus() != OutApplyStatus.TO_BE_CONFIRMED) {
            throw new RuntimeException("当前状态不是待审核状态");
        }

        //生成取检码(不能重复)
        String sampleCode = this.getSampleCode();
        //修改数据
        UpdateWrapper<SampleOutWarehouseApply> wrapper = new UpdateWrapper<>();
        wrapper.eq("id", vo.getId());
        wrapper.set("sample_code_number", sampleCode);
        wrapper.set("status", OutApplyStatus.SUCCESS);//修改当前状态
        wrapper.set("im_administrators_id", userId);
        this.update(wrapper);
        SampleOutWarehouseApplyVO applyVO = sawMapper.getOutApplyDetail(id);
        applyVO.setAmount((long) applyVO.getSampleList().size());//数量
        applyVO.setStatusCode(applyVO.getStatus().getStatus());//状态码
        return applyVO;
    }

    /**
     * 查询单个出库申请信息
     *
     * @param id 出库申请信息+对应出库样本信息
     * @return
     */
    @Override
    public SampleOutWarehouseApplyVO getOutWarehouseDetailedById(String id) {
        SampleOutWarehouseApplyVO vo = sawMapper.getOutApplyDetail(id);
        if (vo == null) {
            throw new RuntimeException(String.format("当前数据不存在"));
        }
        vo.setStatusCode(vo.getStatus().getStatus());//状态码

        Long applyId = owdMapper.selectCount(Wrappers.<OutWarehouseDetailed>query()
                .eq("apply_id", vo.getId())
        );
        List<OutSampleVO> sampleList = vo.getSampleList();
        Collections.sort(sampleList, new Comparator<OutSampleVO>() {
            @Override
            public int compare(OutSampleVO o1, OutSampleVO o2) {
                String[] split1 = o1.getSampleNo().split("-");
                String[] split2 = o2.getSampleNo().split("-");
                // 先比较年份
                int yearComparison = split2[0].compareTo(split1[0]);
                if (yearComparison != 0) {
                    return yearComparison;
                }
                // 再比较序号
                int num1 = Integer.parseInt(split2[1]);
                int num2 = Integer.parseInt(split1[1]);
                if (num1 - num2 != 0) {
                    return num1 - num2;
                }
                // 再比较尾号
                int num3 = Integer.parseInt(split1[2]);
                int num4 = Integer.parseInt(split2[2]);
                return num3 - num4;
            }
        });

        vo.setAmount(applyId);
        if (vo != null) {
            return vo;
        } else {
            return null;
        }
    }

    /**
     * 分页1
     *
     * @param qw
     * @return
     */
    @Override
    public IPage<SampleOutWarehouseApply> getSampleOutWarehouseApplyVOList(QueryWrapper<SampleOutWarehouseApply> qw) {
        IPage<SampleOutWarehouseApply> thisList = sawMapper.getSampleOutWarehouseApplyVOList(qw);

        return thisList;
    }

    /**
     * 分页
     *
     * @param page
     * @param qw
     * @return
     */
    @Override
    public IPage<SampleOutWarehouseApplyVO> getSampleOutWarehouseApplyVOPage(Page<SampleOutWarehouseApply> page, QueryWrapper<SampleOutWarehouseApply> qw) {
        IPage<SampleOutWarehouseApplyVO> thisPage = sawMapper.getSampleOutWarehouseApplyVOPage(page, qw);
        List<SampleOutWarehouseApplyVO> records = thisPage.getRecords();
        for (SampleOutWarehouseApplyVO sampleVO : records) {

            Long applyId = owdMapper.selectCount(Wrappers.<OutWarehouseDetailed>query()
                    .eq("apply_id", sampleVO.getId())
            );
            sampleVO.setAmount(applyId);//数量
            sampleVO.setStatusCode(sampleVO.getStatus().getStatus());//状态码
        }

        return thisPage;
    }

    /**
     * 取检码确认出库
     *
     * @param dto
     * @return
     */
    @Override
    @GlobalTransactional
    public SampleOutWarehouseApplyVO confirmOutbound(VerificationCode dto, DLPUser dlpUser) {

        //这里校验需要code和样本信息的校验

        //判断当前取件码是否可用
        SampleOutWarehouseApply selectOne = sawMapper.selectOne(new QueryWrapper<SampleOutWarehouseApply>().eq("sample_code_number", dto.getCode()));
        if (selectOne == null) {
            throw new RuntimeException("未查询到该数据");
        } else if (selectOne.getStatus() != OutApplyStatus.SUCCESS) {
            throw new RuntimeException("该取检码不可用,请重试");
        }

        //根据取检码获取领用申请样本明细id然后比对校验
        List<OutWarehouseDetailed> detaileds = owdMapper.selectList(Wrappers.<OutWarehouseDetailed>query()
                .eq("apply_id", selectOne.getId())
        );

        if (detaileds.size() != dto.getSampleIds().size()) {
            throw new RuntimeException("数据不对应,请重试");
        }

        boolean result = true;//设定判断参数
        for (OutWarehouseDetailed detailed : detaileds) {
            if (!dto.getSampleIds().contains(detailed.getSampleId())) {//如果不包含该元素;则数据存在错误
                result = false;
                break;
            }
        }
        if (!result) {
            throw new RuntimeException("取检码对应的样本数据不匹配,请重新输入");
        }

        //0.修改当前领用出库申请状态为已使用
        selectOne.setStatus(OutApplyStatus.ALREADY_USED);
        selectOne.setSampleCodeNumber(selectOne.getSampleCodeNumber() + "(已使用)");
        sawMapper.updateById(selectOne);
        //代码出现到这一步说明样本验证成功
        /*
            0.修改当前领用出库申请状态为已使用
            1.修改仓库样本状态为出库
            2.修改流转样本状态为出库
            3.修改样本出入库登记表
            4.添加样本流转记录
            5.添加样本出库记录(应该是修改出入库登记表,填充部分数据)
            6.修改样本状态为领用出库中
            7.修改当前sample持有人为领取人

         */
        for (OutWarehouseDetailed detailed : detaileds) {

            //样本名称
            SampleStorage storage = storageMapper.selectOne(new QueryWrapper<SampleStorage>().eq("sample_id", detailed.getSampleId()));

            BaseSampleDetail sampleDetailInfo = storageMapper.getSampleDetailInfo(detailed.getSampleId());


            //1.修改仓库样本状态为出库
            UpdateWrapper<SampleStorage> wrapper = new UpdateWrapper<>();
            wrapper.eq("sample_id", detailed.getSampleId());
            wrapper.set("status", 3);//；已经出库
            wrapper.set("return_time", selectOne.getReturnDate());//归还时间
            wrapper.set("recipient_id", selectOne.getApplicantId());//领取人
            wrapper.set("is_return", 1);//需要归还
            wrapper.set("out_repository_date", LocalDateTime.now());//出库时间
            wrapper.set("purpose", selectOne.getPurpose());// 用途
            storageMapper.update(null, wrapper);
//        2.修改流转样本状态为出库(或者检验中)
            transferFeignService.updateStatus(detailed.getSampleId(), 6);
//        3.修改样本出入库登记表--出库是修改操作先查询出sample对应的出入库登记表
            //程序文件中说明:检验时才填写出入库登记表
            UpdateWrapper<SampleInboundAndOutboundTable> updateWrapper = new UpdateWrapper<>();
            updateWrapper.eq("sample_id", detailed.getSampleId());
            updateWrapper.isNull("appraiser_id");//鉴定人为空的数据
            updateWrapper.set("appraiser_id", selectOne.getAppraiserId());//鉴定人
            updateWrapper.set("out_warehouse_date", LocalDateTime.now());//出库时间
            updateWrapper.set("remarks", selectOne.getPurpose());//备注
            updateWrapper.set("return_time", selectOne.getReturnDate());//返回时间
            updateWrapper.set("outbound_supervisor", dto.getSupervisor());//监督人
            tableMapper.update(null, updateWrapper);


//        4.添加样本流转记录(远程调用)
            transferFeignService.addTransferByOut(detailed.getSampleId(), selectOne.getApplicantId());
//        5.添加样本出库记录
            SampleOutboundRecord sampleOutboundRecord = new SampleOutboundRecord();
            sampleOutboundRecord.setId(IdWorker.get32UUID().toUpperCase());
            sampleOutboundRecord.setSampleId(detailed.getSampleId());
            sampleOutboundRecord.setOutboundType(OutboundType.USE);//领用出库
            sampleOutboundRecord.setRecipientId(selectOne.getApplicantId());//领取人
            sampleOutboundRecord.setName(storage.getName());//样本名称--方便搜索
            sampleOutboundRecord.setRecipientId(selectOne.getApplicantId());//领取人id
            sampleOutboundRecord.setReason(selectOne.getPurpose());//把出库原因设置为备注
            sampleOutboundRecord.setOutboundDate(LocalDateTime.now());//出库时间
            outboundRecordMapper.insert(sampleOutboundRecord);


            //  6.修改样本状态出库
            transferFeignService.updateStatus(detailed.getSampleId(), 1);
//        7.修改样本当前持有人为领取人
            transferFeignService.updateSampleHolder(new UpdateHolderDTO(detailed.getSampleId(), selectOne.getApplicantId()));


            //8.柜子出库
            commonFeignService.remoteCabinetOutbound(storage.getStorageCellId(), storage.getSampleId());
        }
        return this.getOutWarehouseDetailedById(selectOne.getId());
    }


    /**
     * 通过code取检码获取出库申请信息(出库申请+明细)
     *
     * @param code
     * @return
     */

    @Override
    public SampleOutWarehouseApplyVO getInfoByCode(String code) {
        //判断当前取件码是否可用
        SampleOutWarehouseApply selectOne = sawMapper.selectOne(new QueryWrapper<SampleOutWarehouseApply>().eq("sample_code_number", code));
        if (selectOne == null) {
            throw new RuntimeException("未查询到该数据");
        } else if (selectOne.getStatus() != OutApplyStatus.SUCCESS) {
            throw new RuntimeException("该取检码不可用");
        }
        SampleOutWarehouseApplyVO applyVO = sawMapper.getOutApplyDetail(selectOne.getId());
        applyVO.setAmount((long) applyVO.getSampleList().size());//数量
        applyVO.setStatusCode(applyVO.getStatus().getStatus());//状态码

        List<OutSampleVO> sampleList = applyVO.getSampleList();

        //分组
        Map<String, List<OutSampleVO>> map = sampleList.stream().collect(Collectors.groupingBy(OutSampleVO::getStorageLocation));

        List<CellVO> cellVOList = new ArrayList<>();
        for (String str : map.keySet()) {
            List<OutSampleVO> sampleVOS = map.get(str);
            QueryWrapper<SampleStorage> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("sample_id", sampleVOS.get(0).getSampleId());
            SampleStorage sampleStorage = storageMapper.selectOne(queryWrapper);
            String cellId = sampleVOS.get(0).getCellId();
            Integer number = null;
            CellVO cellVO1 = baseMapper.getCellLocation(cellId);
            if (cellVO1.getCabinetType() != null && cellVO1.getCabinetType() == 3) {
                String[] split = cellVO1.getLocation().split("-");
                number = Integer.parseInt(split[0]);
            }
            CellVO cellVO = new CellVO(sampleVOS.get(0).getCellId(), str, sampleStorage.getBuildingLv(), sampleStorage.getRoomNo(), sampleStorage.getCabinetName(), sampleStorage.getBoxId(), sampleVOS, number, cellVO1.getLocation(), cellVO1.getCabinetType());
            cellVOList.add(cellVO);
        }

        applyVO.setCellVOS(cellVOList);
        return applyVO;
    }


/*
打印取检条码
 */

    @Override
    public String buildEventImLabelContent(String code) {

        QueryWrapper<SampleOutWarehouseApply> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("sample_code_number", code);

        SampleOutWarehouseApply apply = sawMapper.selectOne(queryWrapper);

        if (apply == null) {
            throw new RuntimeException("未查询到相关取检码信息");
        }

        Map<String, Object> data = new HashMap<>();
        data.put("code", code);

        String templateFileName = "取检码标签模板.vm";//标签模板
        return remoteTemplate2htmlService.getHtml(templateFileName, data);
    }





/*
-------------------------------------------------------------------------------------------------------------
 */


    /**
     * 指定长度随机数
     *
     * @param length
     * @return
     */
    public static String getRandomString(int length) {
        String str = "0123456789";
        Random random = new Random();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < length; i++) {
            int number = random.nextInt(10);
            sb.append(str.charAt(number));
        }
        return sb.toString();
    }


    /**
     * 判断取检码重复
     */
    public String getSampleCode() {
        String randomString = getRandomString(6);
        QueryWrapper<SampleOutWarehouseApply> wrapper = new QueryWrapper<>();
        wrapper.eq("sample_code_number", randomString);
        SampleOutWarehouseApply one = sawMapper.selectOne(wrapper);
        if (one == null) {
            return randomString;
        } else {
            getSampleCode();
        }
        return null;
    }
}
