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

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy;
import com.deepoove.poi.xwpf.NiceXWPFDocument;
import digital.laboratory.platform.common.core.util.R;
import digital.laboratory.platform.common.oss.service.OssFile;
import digital.laboratory.platform.inspection.constant.BusinessType;
import digital.laboratory.platform.inspection.constant.TestRecordFileUrl;
import digital.laboratory.platform.inspection.dto.HairSewageDataDto;
import digital.laboratory.platform.inspection.dto.NPSCaseTestDataDto;
import digital.laboratory.platform.inspection.entity.TestRecordInstrument;
import digital.laboratory.platform.inspection.entity.TestRecordReagent;
import digital.laboratory.platform.inspection.service.*;
import digital.laboratory.platform.inspetion.api.entity.EntrustInfo;
import digital.laboratory.platform.inspetion.api.entity.SampleInfo;
import digital.laboratory.platform.inspetion.api.entity.TargetObject;
import digital.laboratory.platform.inspetion.api.entity.TestRecord;
import digital.laboratory.platform.sys.enums.entrust.EntrustBiologyType;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author ChenJiangBao
 * @version 1.0
 * @description: 生成检验记录相关接口实现类
 * @date 2025/3/18 14:50
 */
@Service
public class InspectRecordServiceImpl implements InspectRecordService {

    @Resource
    private OssFile ossFile;

    @Resource
    private EntrustInfoService entrustInfoService;

    @Resource
    private TestRecordService testRecordService;

    @Resource
    private TestRecordInstrumentService testRecordInstrumentService;

    @Resource
    private TestRecordSampleDataService testRecordSampleDataService;

    @Resource
    private TestRecordReagentService testRecordReagentService;

    @Resource
    private SampleInfoService sampleInfoService;

    /**
     * 生成检验记录-贵阳禁毒
     *
     * @param entrustId 委托id
     * @param materialType 标注当前委托的检材是尿液还是毛发
     * @return
     * @throws Exception
     */
    @Override
    public R inspectionRecord(String entrustId, String materialType) throws Exception {
        EntrustInfo entrustInfo = entrustInfoService.getById(entrustId);
        if (entrustInfo == null) {
            return R.ok(false, "委托信息不存在！");
        }
        String type = entrustInfo.getBusinessType();
        if (type.equals(BusinessType.BOINT_CASE.getBusinessType())) {
//            Map<String, Object> map = this.invivoRecord(entrustInfo);
            return R.ok(this.createInVivoFile(entrustInfo, materialType), "生成成功!");
        }
        return R.ok(this.generateCommonDrugInpectRecord(entrustId));
    }

    /**
     * 生成生物样本检验记录数据map
     * @param entrustInfo 委托实体信息
     * @param materialType 检材类型
     * @return
     * @throws Exception
     */
    public Map<String, Object> invivoRecord(EntrustInfo entrustInfo, String materialType) throws Exception {
        // 获取检验记录信息
        TestRecord testRecord = testRecordService.getTestRecordByBusinessId(entrustInfo.getId());
        if (testRecord == null) {
            throw new Exception("未找到检验记录信息");
        }

        HashMap<String, Object> data = buildCommonInspectRecordDocMap(entrustInfo, testRecord, materialType);

        // 获取仪器设备数据
        List<String> deviceIdList = testRecord.getDeviceIdList();
        List<TestRecordInstrument> instruments = CollectionUtils.isEmpty(deviceIdList)
                ? Collections.emptyList()
                : testRecordInstrumentService.list(Wrappers.<TestRecordInstrument>lambdaQuery()
                .in(TestRecordInstrument::getId, deviceIdList));

        data.put("instrumentName", CollectionUtils.isEmpty(instruments)
                ? "未找到仪器设备数据!"
                : instruments.stream()
                .map(TestRecordInstrument::getInstrumentName)
                .collect(Collectors.joining("\n"))
        );

        // 获取样品检测数据
        List<HairSewageDataDto> hairDataDtos = (List<HairSewageDataDto>) testRecordSampleDataService
                .getSampleTestDataByBusiness(entrustInfo.getId());

        if (CollectionUtils.isEmpty(hairDataDtos)) {
            data.put("dataDtos", Collections.emptyList());
            data.put("sampleSize", 0);
            data.put("vo", testRecord);
            return data;
        }

        // 处理检测数据
        Map<String, List<HairSewageDataDto>> dataMap = hairDataDtos.stream()
                .collect(Collectors.groupingBy(HairSewageDataDto::getCompoundName));

        List<HairSewageDataDto> dataDtos = new ArrayList<>();

        dataMap.forEach((compoundName, list) -> {
            // 添加空白数据
            HairSewageDataDto blankDto = new HairSewageDataDto();
            blankDto.setSampleName("空白");
            blankDto.setCompoundName(compoundName);
            blankDto.setTmpTargetRtTime("/");
            blankDto.setTmpRtTimeError("/");
            blankDto.setRtTimeWithinError("否");
            blankDto.setTmpIonAbundanceRatio("/");
            blankDto.setWhetherCheckOut("否");
            dataDtos.add(blankDto);

            // 处理样品数据
            list.removeIf(item -> {
                if (StringUtils.equals(item.getSampleType(), "STD")) {
                    item.setSampleName("空白添加");
                    item.setWhetherCheckOut("是");
                    dataDtos.add(item);
                    return true;
                } else if (StringUtils.equals(item.getSampleType(), "QC")) {
                    return true;
                }
                return false;
            });

            // 排序
            list.sort(this.getSortBySampleNo("inVivo"));

            // 重新命名样品
            if (list.size() == 1) {
                list.get(0).setSampleName("检材样品");
            } else {
                for (int i = 0; i < list.size(); i++) {
                    list.get(i).setSampleName((i + 1) + "号检材样品");
                }
            }

            dataDtos.addAll(list);
        });

        data.put("dataDtos", dataDtos);
        data.put("sampleSize", dataDtos.size());
        return data;
    }

    /**
     * 根据委托信息和样本类型生成生物样本检验记录文件。
     *
     * @param entrustInfo 委托信息对象
     * @param materialType 样本类型（如"毛发"、"尿液"等）
     * @return 生成的生物样本检验记录文件的路径
     * @throws Exception 如果文件生成过程中出现错误，抛出异常
     */
    public String createInVivoFile(EntrustInfo entrustInfo, String materialType) throws Exception {
        // 获取文件数据map
        Map<String, Object> data = this.invivoRecord(entrustInfo, materialType);
        // 调用模板生成检验记录
        String templatePath = "";
        if (materialType.equals("毛发")) {
            templatePath = "/template" + "/贵阳生物样本毛发检验记录模板.docx";
        } else {
            templatePath = "/template" + "/贵阳生物样本尿液检验记录模板.docx";
        }
        LoopRowTableRenderPolicy policy = new LoopRowTableRenderPolicy();
        Configure config = Configure.builder().
                bind("dataDtos", policy).build();
        return buildDocFileAndUploadToOss(entrustInfo.getId(), templatePath, config, data);
    }

    /**
     * 根据委托信息、模板路径、配置和数据构建文档文件并上传到OSS
     *
     * @param entrustId 委托id
     * @param templatePath 模板文件路径
     * @param config 配置信息
     * @param data 要填充到模板中的数据
     * @return 上传后的文件路径
     * @throws Exception 可能抛出的异常
     */
    private String buildDocFileAndUploadToOss(String entrustId, String templatePath, Configure config, Map<String, Object> data) throws Exception {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ossFile.fileGet(templatePath, bos);
        byte[] templateArray = bos.toByteArray();
        ByteArrayInputStream bis = new ByteArrayInputStream(templateArray);
        bos.close();

        XWPFTemplate template = XWPFTemplate.compile(bis, config).render(data);
        NiceXWPFDocument document = template.getXWPFDocument();

//        XWPFTable table = document.getTables().get(3);
//        int sampleSize = (int) data.get("sampleSize");
//        //合并单元格
//
//        TableTools.mergeCellsVertically(table, 1, 1, sampleSize);

        bis.close();
        ByteArrayOutputStream fosWord = new ByteArrayOutputStream();
        template.write(fosWord);
        template.close();
        ByteArrayInputStream fisWord = new ByteArrayInputStream(fosWord.toByteArray());
        fosWord.close();
        document.close();
        String path = TestRecordFileUrl.TEST_RECORD_CATALOGUE.getFileUrl() + "/" + entrustId + "/" + "贵阳生物样本检验记录.docx";
        ossFile.fileSave(path, fisWord);
        return path;
    }

    public Comparator getSortBySampleNo(String type) {

        if (type.equals("inVivo")) {
            Comparator<HairSewageDataDto> comparator = Comparator.comparing(
                    HairSewageDataDto::getSampleNo,
                    (a, b) -> {
                        String[] partsA = a.split("-");
                        String[] partsB = b.split("-");

                        // 逐部分比较数值
                        for (int i = 0; i < 3; i++) {
                            int numA = Integer.parseInt(partsA[i]);
                            int numB = Integer.parseInt(partsB[i]);
                            if (numA != numB) return Integer.compare(numA, numB);
                        }
                        return 0; // 所有部分相同
                    }
            );
            return comparator;
        } else {
            Comparator<NPSCaseTestDataDto> comparator = Comparator.comparing(
                    NPSCaseTestDataDto::getSampleNo,
                    (a, b) -> {
                        String[] partsA = a.split("-");
                        String[] partsB = b.split("-");

                        // 逐部分比较数值
                        for (int i = 0; i < 3; i++) {
                            int numA = Integer.parseInt(partsA[i]);
                            int numB = Integer.parseInt(partsB[i]);
                            if (numA != numB) return Integer.compare(numA, numB);
                        }
                        return 0; // 所有部分相同
                    }
            );
            return comparator;
        }
    }

    /**
     * 生成常规毒品的检验记录（贵阳禁毒）
     */
    public String generateCommonDrugInpectRecord(String entrustId) throws Exception {
        Map<String, Object> docMap = buildCommonDrugDocMap(entrustId);
        String templatePath = "/template" + "/贵阳禁毒常规毒品检验记录模板.docx";
        LoopRowTableRenderPolicy policy = new LoopRowTableRenderPolicy();
        Configure config = Configure.builder().
                bind("dataDtos", policy).build();
        return buildDocFileAndUploadToOss(entrustId, templatePath, config, docMap);
    }

    /**
     * 构建常规毒品的检验记录数据
     *
     * @return
     */
    private Map<String, Object> buildCommonDrugDocMap(String entrustId) {
        EntrustInfo entrustInfo = entrustInfoService.getById(entrustId);

        // 这里因为检验记录实体并未关联业务id, 只能先查询实验数据，在通过实验数据获取到检验记录信息
        TestRecord testRecord = testRecordService.getTestRecordByBusinessId(entrustInfo.getId());

        // 封装数据到map中
        Map<String, Object> docMap = buildCommonInspectRecordDocMap(entrustInfo, testRecord, "");

        return docMap;
    }


    /**
     * 构建通用的检验记录文档映射
     *
     * @param entrustInfo  委托信息对象
     * @param materialType
     * @return 包含委托信息和检验记录信息的HashMap对象
     */
    private HashMap<String, Object> buildCommonInspectRecordDocMap(EntrustInfo entrustInfo, TestRecord testRecord, String materialType) {
        HashMap<String, Object> data = new HashMap<>();

        data.put("entrustDepartment", entrustInfo.getEntrustDepartment());
        data.put("deliver1Name", entrustInfo.getDeliver1Name());
        data.put("deliver2Name", entrustInfo.getDeliver2Name());
        // 受理日期
        data.put("acceptYear", entrustInfo.getAcceptDate().getYear());
        data.put("acceptMonth", entrustInfo.getAcceptDate().getMonthValue());
        data.put("acceptDay", entrustInfo.getAcceptDate().getDayOfMonth());

        // 检验日期
        data.put("inspectYear", testRecord.getTestStartDate().getYear());
        data.put("inspectMonth", testRecord.getTestStartDate().getMonthValue());
        data.put("inspectDay", testRecord.getTestStartDate().getDayOfMonth());

        // 检材性状描述和检验要求成分
        List<SampleInfo> sampleInfoList = getSampleInfosByMaterialType(entrustInfo, materialType);
        String materialCharacterDesc = buildMaterialCharacterDesc(sampleInfoList);
        String targetObjectStr = sampleInfoList
                .stream()
                .flatMap(
                        sampleInfo -> {
                            String toJSONString = JSONArray.toJSONString(sampleInfo.getTargetObject());
                            List<TargetObject> drugLiteList = JSONArray.parseArray(toJSONString, TargetObject.class);
                            return drugLiteList.stream();
                        })
                .collect(Collectors.toList()) // 得到转换好的筛查物对象
                .stream()
                .map(TargetObject::getName)
                .collect(Collectors.toSet()) // 区筛查物对象名称并去重
                .stream()
                .collect(Collectors.joining("、")); // 拼接筛查物名称
        data.put("materialCharacterDesc", materialCharacterDesc);
        data.put("materialIngredient", targetObjectStr);

        // 设置使用的标准物质和试剂
        List<TestRecordReagent> references = testRecordReagentService.list(Wrappers.<TestRecordReagent>lambdaQuery()
                .in(TestRecordReagent::getId, testRecord.getReagentConsumablesList())
                .eq(TestRecordReagent::getCategory, "标准物质"));

        if (references == null || references.size() == 0) {
            data.put("referenceMaterialName", "未找到试剂耗材数据!");
        } else {

            String referenceMaterialName = references.stream()
                    .map(reagent -> "\u2611" + " " + reagent.getReagentConsumableName())
                    .collect(Collectors.joining("\n"));

            data.put("referenceMaterialName", referenceMaterialName);
        }

        List<TestRecordReagent> reagents = testRecordReagentService.list(Wrappers.<TestRecordReagent>lambdaQuery()
                .in(TestRecordReagent::getId, testRecord.getReagentConsumablesList())
                .eq(TestRecordReagent::getCategory, "试剂"));

        if (reagents == null || reagents.size() == 0) {
            data.put("reagentConsumableName", "未找到试剂耗材数据!");
        } else {
            String reagentConsumableName = reagents.stream()
                    .map(TestRecordReagent::getReagentConsumableName)
                    .collect(Collectors.joining("、"));
            data.put("reagentConsumableName", reagentConsumableName);
        }
        return data;
    }

    /**
     * 根据委托信息和检材类型获取检材信息列表
     *
     * @param entrustInfo 委托信息对象
     * @param materialType 检材类型, 毛发或尿液
     * @return 检材信息列表
     */
    private List<SampleInfo> getSampleInfosByMaterialType(EntrustInfo entrustInfo, String materialType) {
        List<SampleInfo> sampleInfoList = sampleInfoService.list(Wrappers.<SampleInfo>lambdaQuery().eq(SampleInfo::getBusinessId, entrustInfo.getId()));
        // 对检材进行筛选，如果materialType 不为空则进行操作
        if (StrUtil.isNotBlank(materialType)) {
            if (materialType.equals("毛发")) {
                sampleInfoList = sampleInfoList.stream()
                        .filter(o -> EntrustBiologyType.HAIR.equals(EntrustBiologyType.isExist(o.getForm())))
                        .collect(Collectors.toList());

            } else {
                // 尿液
                sampleInfoList = sampleInfoList.stream()
                        .filter(o -> EntrustBiologyType.URINE.equals(EntrustBiologyType.isExist(o.getForm())))
                        .collect(Collectors.toList());
            }
        }
        return sampleInfoList;
    }


    /**
     * 构建检材性状描述
     *
     * @param sampleInfoList 样本信息列表
     * @return 物料特性描述，如果无法构建则返回null
     */
    private String buildMaterialCharacterDesc(List<SampleInfo> sampleInfoList) {
        if (CollUtil.isEmpty(sampleInfoList)) {
            return StrUtil.EMPTY;
        }

        // 仅有一个样本时，直接返回
        if (sampleInfoList.size() == 1) {
            return "检材为" + sampleInfoList.get(0).getForm() + "。";
        }

        // 按 form 分组
        Map<String, List<SampleInfo>> sampleMapGroupByForm = sampleInfoList.stream()
                .collect(Collectors.groupingBy(SampleInfo::getForm));

        // 所有样本 form 相同
        if (sampleMapGroupByForm.size() == 1) {
            String form = sampleMapGroupByForm.keySet().iterator().next(); // 取唯一的 key
            String orderNoStr = sampleInfoList.stream()
                    .map(sample -> sample.getOrderNo() + "号")
                    .collect(Collectors.joining("、"));
            return orderNoStr + "检材均为" + form + "。";
        }

        // 存在多个 form 的情况
        List<String> descriptions = new ArrayList<>();
        sampleMapGroupByForm.forEach((form, value) -> {
            String orderNoStr = value.stream()
                    .map(sample -> sample.getOrderNo() + "号")
                    .collect(Collectors.joining("、"));
            descriptions.add(orderNoStr + "检材均为" + form);
        });

        return String.join("；", descriptions) + "。"; // 用分号连接不同类型的描述
    }

}
