package digital.laboratory.platform.entrustment.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.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import digital.laboratory.platform.common.core.exception.CheckedException;
import digital.laboratory.platform.entrustment.convert.DrugLiteConvert;
import digital.laboratory.platform.entrustment.convert.EntrustMaterialCheckoutResultConvert;
import digital.laboratory.platform.entrustment.dto.CheckoutResultExcelDTO;
import digital.laboratory.platform.entrustment.dto.EntrustMaterialCheckoutResultDTO;
import digital.laboratory.platform.entrustment.entity.EntrustMaterialCheckoutResult;
import digital.laboratory.platform.entrustment.entity.Entrustment;
import digital.laboratory.platform.entrustment.entity.EntrustmentIdentificationMaterial;
import digital.laboratory.platform.entrustment.enums.EntrustmentStatusConstants;
import digital.laboratory.platform.entrustment.mapper.EntrustMaterialCheckoutResultMapper;
import digital.laboratory.platform.entrustment.query.EntrustMaterialCheckoutResultQuery;
import digital.laboratory.platform.entrustment.service.CommonFeignService;
import digital.laboratory.platform.entrustment.service.EntrustMaterialCheckoutResultService;
import digital.laboratory.platform.entrustment.service.EntrustmentIdentificationMaterialService;
import digital.laboratory.platform.entrustment.service.EntrustmentService;
import digital.laboratory.platform.entrustment.vo.EntrustMaterialCheckoutResultVO;
import digital.laboratory.platform.sys.entity.Area;
import digital.laboratory.platform.sys.entity.DrugLite;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
* @author ChenJiangBao
* @description 针对表【b_entrust_material_checkout_result(委托检材--检出定性定量结果信息)】的数据库操作Service实现
* @createDate 2024-12-30 16:16:25
*/
@Service
public class EntrustMaterialCheckoutResultServiceImpl extends ServiceImpl<EntrustMaterialCheckoutResultMapper, EntrustMaterialCheckoutResult>
    implements EntrustMaterialCheckoutResultService {

    @Resource
    private EntrustmentService entrustmentService;

    @Resource
    private EntrustmentIdentificationMaterialService entrustmentIdentificationMaterialService;

    @Resource
    private CommonFeignService commonFeignService;


    /**
     * 构建委托材料检验结果字符串
     *
     * @param entrustId 委托ID
     * @return 委托材料检验结果字符串
     */
    @Override
    public String buildEntrustMaterialCheckoutResultStr(String entrustId) {
        // 先获取检材列表
        List<EntrustmentIdentificationMaterial> materialList = entrustmentIdentificationMaterialService.list(Wrappers.<EntrustmentIdentificationMaterial>lambdaQuery().eq(EntrustmentIdentificationMaterial::getEntrustmentId, entrustId));
        Map<String, EntrustmentIdentificationMaterial> materialMap = materialList.stream().collect(Collectors.toMap(EntrustmentIdentificationMaterial::getId, Function.identity()));
        // 获取检测结果
        List<EntrustMaterialCheckoutResult> checkoutResultList = super.list(Wrappers.<EntrustMaterialCheckoutResult>lambdaQuery().eq(EntrustMaterialCheckoutResult::getEntrustId, entrustId));
        if (CollUtil.isEmpty(checkoutResultList)) {
            return "";
        }

        List<String> resultStrList = new ArrayList<>();
        // 对检测结果进行分组
        Map<String, List<EntrustMaterialCheckoutResult>> checkoutResultGroupMap = checkoutResultList.stream().collect(Collectors.groupingBy(item -> DrugLiteConvert.joiningDrugListNameToStr(item.getQualitativeResult(), "、")));

        checkoutResultGroupMap.forEach((key, value) -> {
            StringBuilder builder = new StringBuilder();
            builder
                    .append(value.stream().map(item -> materialMap.get(item.getId()).getOrderNo1()).collect(Collectors.joining("、")))
                    .append(" : [")
                    .append(key)
                    .append("]");
            resultStrList.add(builder.toString());
        });
        return resultStrList.stream().sorted().collect(Collectors.joining("\n"));
    }

    /**
     * 根据DTO保存检材得检出结果信息
     * @param dto 检出DTO
     * @return
     */
    @Override
    public boolean save(EntrustMaterialCheckoutResultDTO dto) {
        Entrustment entrustment = entrustmentService.getById(dto.getEntrustId());
        if (entrustment == null) {
            throw new CheckedException("委托信息不存在！");
        }
        if (!entrustment.getStatus().equals(EntrustmentStatusConstants.ENTRUSTMENT_STATUS_ACCEPTED.getStatus())) {
            throw new CheckedException("该委托信息并未被机构受理！");
        }
        List<String> ids = dto.getIds();
        List<EntrustmentIdentificationMaterial> materialList = entrustmentIdentificationMaterialService.list(Wrappers.<EntrustmentIdentificationMaterial>lambdaQuery().in(EntrustmentIdentificationMaterial::getId, ids));
        Map<String, EntrustmentIdentificationMaterial> identificationMaterialMap = materialList.stream().collect(Collectors.toMap(EntrustmentIdentificationMaterial::getId, Function.identity()));
        boolean success = false;
        for (String id : ids) {
            EntrustmentIdentificationMaterial identificationMaterial = identificationMaterialMap.get(id);
            if (identificationMaterial == null) {
                throw new CheckedException(String.format("id为 [%s] 得检材信息不存在！", id));
            }
            EntrustMaterialCheckoutResult entrustMaterialCheckoutResult = EntrustMaterialCheckoutResultConvert.dtoToEntity(dto);
            entrustMaterialCheckoutResult.setId(id);
            entrustMaterialCheckoutResult.setEntrustId(identificationMaterial.getEntrustmentId());
            if (super.count(Wrappers.<EntrustMaterialCheckoutResult>lambdaQuery().eq(EntrustMaterialCheckoutResult::getId, id)) <= 0) {
//            throw new CheckedException(String.format("检材编号 [%s] 的检材检出结果已经存在！", identificationMaterial.getImNo()));
                success = super.save(entrustMaterialCheckoutResult);
            } else {
                success = super.updateById(entrustMaterialCheckoutResult);
            }
        }
        return success;
    }

    /**
     * 根据委托id导出检出结果excel文件
     *
     * @param entrustIds 委托id列表，包含需要导出的委托的id
     * @param response HttpServletResponse对象，用于将生成的excel文件写入响应中
     */
    @Override
    public void exportExcel(List<String> entrustIds, HttpServletResponse response) throws IOException {
        // 获取委托信息
        List<Entrustment> entrustmentList = entrustmentService.list(
                Wrappers.<Entrustment>lambdaQuery().in(Entrustment::getId, entrustIds)
        );
        // 获取检材信息， 排序的原因是需要数据是有序的
        List<EntrustmentIdentificationMaterial> materialList = entrustmentIdentificationMaterialService.list(
                Wrappers.<EntrustmentIdentificationMaterial>lambdaQuery()
                        .in(EntrustmentIdentificationMaterial::getEntrustmentId, entrustIds)
                        .orderByAsc(EntrustmentIdentificationMaterial::getEntrustmentId)
                        .orderByAsc(EntrustmentIdentificationMaterial::getOrderNo)
        );
        // 获取检材id,后续根据检材id获取对应的检出结果
        List<String> materialIds = materialList.stream().map(EntrustmentIdentificationMaterial::getId).collect(Collectors.toList());
        // 根据委托id进行分组，防止导出的excel数据被打乱
        Map<String, List<EntrustmentIdentificationMaterial>> materialGroupByEntrustMap = materialList.stream().collect(Collectors.groupingBy(EntrustmentIdentificationMaterial::getEntrustmentId));

        // 获取检出结果
        List<EntrustMaterialCheckoutResult> checkoutResultList = super.list(Wrappers.<EntrustMaterialCheckoutResult>lambdaQuery().in(EntrustMaterialCheckoutResult::getId, materialIds));
        Map<String, EntrustMaterialCheckoutResult> checkoutResultMap = checkoutResultList.stream().collect(Collectors.toMap(EntrustMaterialCheckoutResult::getId, Function.identity()));
        List<CheckoutResultExcelDTO> checkoutResultExcelDTOS = fetchCheckoutResultExcelDTOList(entrustmentList, materialGroupByEntrustMap, checkoutResultMap);

        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        String fileName = URLEncoder.encode("检材检出结果excel表", "UTF-8").replaceAll("\\+", "%20");
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");

        // 不使用默认样式
        // 空的头部样式
//        WriteCellStyle headWriteCellStyle = new WriteCellStyle();
//        headWriteCellStyle.setFillForegroundColor(IndexedColors.WHITE.getIndex());
//
//        // 空的内容样式
//        WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
//
//        // 不使用任何样式
//        HorizontalCellStyleStrategy horizontalCellStyleStrategy =
//                new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
//
//        FastExcel.write(response.getOutputStream(), CheckoutResultExcelDTO.class)
//                .sheet(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")))
//                .registerWriteHandler(horizontalCellStyleStrategy)
//                .doWrite(checkoutResultExcelDTOS);
        // 使用poi导出
        buildExcel(response, checkoutResultExcelDTOS);
    }

    /**
     * vo 分页对象
     * @param query
     * @return
     */
    @Override
    public IPage<EntrustMaterialCheckoutResultVO> voiPage(EntrustMaterialCheckoutResultQuery query) {
        IPage<EntrustMaterialCheckoutResult> page = this.page(
                new Page<>(query.getCurrent(), query.getSize()),
                Wrappers.<EntrustMaterialCheckoutResult>lambdaQuery()
                        .eq(StrUtil.isNotBlank(query.getEntrustId()), EntrustMaterialCheckoutResult::getEntrustId, query.getEntrustId())
                        .orderByDesc(EntrustMaterialCheckoutResult::getUpdateTime)
        );
        IPage<EntrustMaterialCheckoutResultVO> voPage = new Page<>();
        BeanUtils.copyProperties(page, voPage, "records");
        List<EntrustMaterialCheckoutResult> records = page.getRecords();
        if (CollUtil.isNotEmpty(records)) {
            List<EntrustmentIdentificationMaterial> entrustmentIdentificationMaterials = entrustmentIdentificationMaterialService.list(Wrappers.<EntrustmentIdentificationMaterial>lambdaQuery().in(EntrustmentIdentificationMaterial::getId, records.stream().map(EntrustMaterialCheckoutResult::getId).collect(Collectors.toSet())));
            Map<String, EntrustmentIdentificationMaterial> materialMap = entrustmentIdentificationMaterials.stream().collect(Collectors.toMap(EntrustmentIdentificationMaterial::getId, Function.identity()));
            List<EntrustMaterialCheckoutResultVO> entrustMaterialCheckoutResultVOS = records.stream().map(record -> {
                EntrustMaterialCheckoutResultVO entrustMaterialCheckoutResultVO = EntrustMaterialCheckoutResultConvert.entityToVO(record);
                entrustMaterialCheckoutResultVO.setName(materialMap.get(record.getId()).getName());
                return entrustMaterialCheckoutResultVO;
            }).collect(Collectors.toList());
            voPage.setRecords(entrustMaterialCheckoutResultVOS);
        }
        return voPage;
    }

    /**
     * 构建并输出Excel文件。
     *
     * @param response              HttpServletResponse对象，用于输出Excel文件
     * @param checkoutResultExcelDTOS 需要写入Excel的数据列表
     * @throws IOException 如果在写入Excel文件时发生IO异常
     *
     * 此方法用于将给定的checkoutResultExcelDTOS列表数据写入Excel文件，并输出到HttpServletResponse对象中。
     * 方法首先创建一个新的XSSFWorkbook对象作为Excel工作簿，并为其创建一个名为当前日期时间的工作表。
     * 接着，根据buildExcelHead方法返回的表头信息构建Excel的表头行。
     * 然后，遍历checkoutResultExcelDTOS列表，将每个元素的数据写入Excel的对应行中。
     * 最后，使用response.getOutputStream()获取输出流，将构建好的Excel工作簿写入该输出流中，实现文件的下载。
     */
    private void buildExcel(HttpServletResponse response, List<CheckoutResultExcelDTO> checkoutResultExcelDTOS) throws IOException {
        Workbook workbook = new XSSFWorkbook();
        Sheet sheet = workbook.createSheet(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
        // 表头导入
        List<String> head = buildExcelHead();
        // 算上表头的行数
        int rowCount = checkoutResultExcelDTOS.size() + 1;
        for (int i = 0; i < rowCount; i++) {
            if (i == 0) {
                Row row = sheet.createRow(i);
                for (int j = 0; j < head.size(); j++) {
                    row.createCell(j).setCellValue(head.get(j));
                }
            } else {
                // 开始写入具体数据内容
                Row row = sheet.createRow(i);
                CheckoutResultExcelDTO excelDTO = checkoutResultExcelDTOS.get(i - 1);
                row.createCell(0).setCellValue(excelDTO.getOrder());
                row.createCell(1).setCellValue(excelDTO.getDeliverTime());
                row.createCell(2).setCellValue(excelDTO.getClientOrgName());
                row.createCell(3).setCellValue(excelDTO.getProvinceCollectPlace());
                row.createCell(4).setCellValue(excelDTO.getCityCollectPlace());
                row.createCell(5).setCellValue(excelDTO.getAcceptNo());
                row.createCell(6).setCellValue(excelDTO.getOrderNo());
                row.createCell(7).setCellValue(excelDTO.getTypeName());
                row.createCell(8).setCellValue(excelDTO.getColor());
                row.createCell(9).setCellValue(excelDTO.getFormName());
                row.createCell(10).setCellValue(excelDTO.getQualitativeResult());
                row.createCell(11).setCellValue(excelDTO.getQuantitativeResult());
                row.createCell(12).setCellValue(excelDTO.getOtherResult());
                row.createCell(13).setCellValue(excelDTO.getRemark());
            }
        }
        // 输出到流
        workbook.write(response.getOutputStream());
    }

    private List<String> buildExcelHead() {
        List<String> headList = new ArrayList<>();
        headList.add("序号");
        headList.add("送检日期");
        headList.add("送检单位");
        headList.add("检材采集地");
        headList.add("检材采集地");
        headList.add("受理编号");
        headList.add("检材编号");
        headList.add("检材类型");
        headList.add("检材颜色");

        headList.add("检材形态");
        headList.add("定性结果");
        headList.add("定量结果");
        headList.add("其他鉴定结果");
        headList.add("备注");
        return headList;
    }

    /**
     * 根据委托列表、材料映射和检验结果映射生成导出Excel的DTO列表
     *
     * @param entrustList        委托列表
     * @param materialMap        材料映射，键为委托ID，值为对应的材料列表
     * @param checkoutResultMap  检验结果映射，键为委托ID，值为对应的检验结果
     * @return 导出Excel的DTO列表，但此方法实际返回空列表，因为最后返回的是Collections.emptyList()
     */
    private List<CheckoutResultExcelDTO> fetchCheckoutResultExcelDTOList(List<Entrustment> entrustList, Map<String, List<EntrustmentIdentificationMaterial>> materialMap, Map<String, EntrustMaterialCheckoutResult> checkoutResultMap) {
        List<CheckoutResultExcelDTO> checkoutResultExcelDTOList = new ArrayList<>();
        Integer index = 1;
        for (Entrustment entrust : entrustList) {
            List<EntrustmentIdentificationMaterial> materialList = materialMap.get(entrust.getId());
            List<Area> areas = commonFeignService.remoteGetProvinceCityInfo(entrust.getClientOrgId());
            String provinceCollectPlace = areas.get(0).getName();
            String cityCollectPlace = "";
            if (areas.size() > 1) {
                cityCollectPlace = areas.get(1).getName();
            }
            String typeName = getTypeName(entrust);
            if (CollUtil.isNotEmpty(materialList)) {
                for (EntrustmentIdentificationMaterial material : materialList) {
                    CheckoutResultExcelDTO excelDTO = new CheckoutResultExcelDTO();
                    excelDTO.setOrder(index++);
                    excelDTO.setDeliverTime(entrust.getDeliverTime().format(DateTimeFormatter.ofPattern("yyyy/M/d")));
                    excelDTO.setClientOrgName(entrust.getClientOrgName());
                    excelDTO.setProvinceCollectPlace(provinceCollectPlace);
                    excelDTO.setCityCollectPlace(cityCollectPlace);
                    excelDTO.setAcceptNo(entrust.getAcceptNo());
                    excelDTO.setOrderNo(material.getOrderNo());
                    excelDTO.setTypeName(typeName);
                    excelDTO.setColor(material.getColor());
                    excelDTO.setFormName(material.getFormName());
                    EntrustMaterialCheckoutResult entrustMaterialCheckoutResult = checkoutResultMap.get(material.getId());
                    excelDTO.setQualitativeResult(
                            DrugLiteConvert.convertDirtyLiteByJSON(entrustMaterialCheckoutResult.getQualitativeResult())
                                    .stream().map(DrugLite::getName).collect(Collectors.joining("、"))
                    );
                    excelDTO.setQuantitativeResult(entrustMaterialCheckoutResult.getQuantitativeResult());
                    excelDTO.setOtherResult(entrustMaterialCheckoutResult.getOtherResult());
                    excelDTO.setRemark(entrustMaterialCheckoutResult.getCheckoutRemark());
                    checkoutResultExcelDTOList.add(excelDTO);
                }
            }
        }
        return checkoutResultExcelDTOList;
    }

    /**
     * 根据委托类型获取委托检材类型的名称
     *
     * @param entrust 委托对象
     * @return 委托类型的名称
     */
    private String getTypeName(Entrustment entrust) {
        String typeName = "";
        if (entrust.getEntrustmentType().equals(0)) {
            typeName = "缴获物";
        } else if (entrust.getEntrustmentType().equals(1)) {
            typeName = "生物样本";
        } else {
            typeName = "其他";
        }
        return typeName;
    }
}




