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

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import digital.laboratory.platform.inspection.constant.SampleInjectorConstant;
import digital.laboratory.platform.inspection.enums.StdSolutionNum;
import digital.laboratory.platform.inspection.dto.ResetSampleInjectorDTO;
import digital.laboratory.platform.inspection.dto.SampleInjectorExcelDTO;
import digital.laboratory.platform.inspection.entity.SampleInjector;
import digital.laboratory.platform.inspetion.api.entity.TestRecord;
import digital.laboratory.platform.inspection.mapper.SampleInjectorMapper;
import digital.laboratory.platform.inspection.service.SampleInjectorService;
import digital.laboratory.platform.inspection.service.TestRecordService;
import digital.laboratory.platform.inspection.vo.ResultConcentrationVO;
import digital.laboratory.platform.inspection.vo.TestRecordSolutionVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

/**
 * 进样器信息接口实现类
 */
@Slf4j
@Service
public class SampleInjectorServiceImpl extends ServiceImpl<SampleInjectorMapper, SampleInjector> implements SampleInjectorService {

    @Resource
    private TestRecordService testRecordService;

    /**
     * 保存进样信息
     * @param sampleInjector
     * @return SampleInjector
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public SampleInjector saveSampleInjector(SampleInjector sampleInjector) {
        // 处理方盘，判断编号有没有重复放置
        if (sampleInjector.getType() == 2) {
            Set<String> sampleNoSet = new HashSet<>();
            int primalSize = 0;
            // 合并方盘数组
            JSONArray injectorTypeArray = getJsonArrayByInjectorType(sampleInjector);
            for (int i = 0; i < injectorTypeArray.size(); i++) {
                JSONObject jsonObject = injectorTypeArray.getJSONObject(i);
                String sampleNo = jsonObject.getString("sampleNo");
                if (StrUtil.isNotBlank(sampleNo)) {
                    sampleNoSet.add(sampleNo);
                    if (sampleNoSet.size() > primalSize) {
                        primalSize = sampleNoSet.size();
                    } else {
                        throw new RuntimeException(String.format("溶液编号为 %s 的溶液重复放置，请取出来重新放置！", sampleNo));
                    }
                }
            }
        }
        // 开始处理保存的数据
        boolean notBlank = StrUtil.isNotBlank(sampleInjector.getId());
        sampleInjector.setResetFlag(true);// 保存设置的重置的标识
        if (notBlank) {
            SampleInjector oldInfo = this.getById(sampleInjector.getId());
            if (oldInfo == null) {
                throw new RuntimeException(String.format("id为 %s 的进样信息不存在！", sampleInjector.getId()));
            }
            this.updateById(sampleInjector);
        } else {
            sampleInjector.setId(IdWorker.get32UUID());
            this.save(sampleInjector);
        }
        // 判断是否在上机，并更新状态值
        if (judgeOnTheMachine(sampleInjector)){
            testRecordService.update(Wrappers.<TestRecord>lambdaUpdate()
                    .eq(TestRecord::getId, sampleInjector.getTestId())
                    .set(TestRecord::getStatus, 1));
        }
        return sampleInjector;
    }

    /**
     * 检材列表查询
     * @param testId
     * @param key
     * @return
     */
    @Override
    public List<TestRecordSolutionVO> queryMaterialList(String testId, String key) {
        TestRecord testInfo = testRecordService.getById(testId);
        if (testInfo == null) {
            throw new RuntimeException(String.format("查询不到检验任务id为 %s 的信息！", testId));
        }
        // 查询是否已经进样
        SampleInjector one = this.getOne(Wrappers.<SampleInjector>lambdaQuery().eq(SampleInjector::getTestId, testId));

        // 根据进样器类型返回json数组
        JSONArray jsonArray = getJsonArrayByInjectorType(one);
        JSONArray finalJsonArray = jsonArray;
        Map<String, List<TestRecordSolutionVO>> testRecordSolutionVOSGroupByType = baseMapper.queryMaterialList(Wrappers.<TestRecordSolutionVO>query()
                        .eq("T.test_id", testId)
                        .orderByDesc("T.number")
                        .and(StrUtil.isNotBlank(key),
                                wrapper -> wrapper.like("T.number", key)))
                .parallelStream().peek(item -> {
                    // 根据其编号判断其溶液类型
                    String number = item.getNumber();
                    if (number.contains(StdSolutionNum.BLANK_SOLUTION.getPrefix())) {

                        item.setTypeName(SampleInjectorConstant.BLANK_NAME);
                    } else if (number.contains(StdSolutionNum.SIMPLE_SOLUTION.getPrefix())) {

                        item.setTypeName(SampleInjectorConstant.STANDARD_NAME);
                    } else if (number.contains(StdSolutionNum.MIXED_SOLUTION.getPrefix())) {
                        item.setTypeName(SampleInjectorConstant.MIX_STANDARD_NAME);
                    } else {
                        item.setTypeName(SampleInjectorConstant.NORMAL_NAME);
                    }
                    // 处理已经进样的数据
                    if (finalJsonArray != null) {
                        for (int i = 0; i < finalJsonArray.size(); i++) {
                            JSONObject jsonObject = finalJsonArray.getJSONObject(i);
                            String sampleNo = jsonObject.getString("sampleNo");
                            if (StrUtil.isNotBlank(sampleNo) && item.getNumber().equals(sampleNo)) {
                                item.setInjectorCount(jsonObject.getInteger("injectorCount"));
                                item.setFrameNumber(jsonObject.getString("frameNumber"));
                                String injectorLocation = one.getType() == 1 ? jsonObject.getString("title") : jsonObject.getString("name");
                                item.setInjectorLocation(injectorLocation);
                            }
                        }
                    }
                }).collect(Collectors.toList()).stream().collect(Collectors.groupingBy(TestRecordSolutionVO::getTypeName));

        // 根据分类封装结果
        List<TestRecordSolutionVO> testRecordSolutionVOS = new ArrayList<>();
        if (testRecordSolutionVOSGroupByType.containsKey(SampleInjectorConstant.NORMAL_NAME)) {
            testRecordSolutionVOS.addAll(testRecordSolutionVOSGroupByType.get(SampleInjectorConstant.NORMAL_NAME));
        }
        if (testRecordSolutionVOSGroupByType.containsKey(SampleInjectorConstant.BLANK_NAME)) {
            testRecordSolutionVOS.addAll(testRecordSolutionVOSGroupByType.get(SampleInjectorConstant.BLANK_NAME));
        }
        if (testRecordSolutionVOSGroupByType.containsKey(SampleInjectorConstant.MIX_STANDARD_NAME)) {
            testRecordSolutionVOS.addAll(testRecordSolutionVOSGroupByType.get(SampleInjectorConstant.MIX_STANDARD_NAME));
        }
        if (testRecordSolutionVOSGroupByType.containsKey(SampleInjectorConstant.STANDARD_NAME)) {
            testRecordSolutionVOS.addAll(testRecordSolutionVOSGroupByType.get(SampleInjectorConstant.STANDARD_NAME));
        }
        return testRecordSolutionVOS;
    }

    /**
     * 导出进样信息excel表
     * @param id
     * @param response
     */
    @Override
    public void exportSampleInjectorInfoToExcel(String id, HttpServletResponse response){
        // 先查询进样信息，取出进样位置信息
        SampleInjector sampleInjector = this.getById(id);
        if (sampleInjector == null) {
            throw new RuntimeException("进样信息查询失败！");
        }
        Map<String, String> resultConcentrationVOMap = baseMapper.queryResultConcentrationList(Wrappers.<ResultConcentrationVO>query().eq("T.test_id", sampleInjector.getTestId())).stream().collect(Collectors.toMap(ResultConcentrationVO::getNumber, ResultConcentrationVO::getResultConcentration));
        List<SampleInjectorExcelDTO> list = getSampleInjectorExportDataByDisk(sampleInjector, resultConcentrationVOMap);
//        ServletOutputStream outputStream = null;
        try {

            this.setExcelResponseProp(response, "进样表-" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy年MM月dd日-HH时mm分ss秒")));
//            outputStream = response.getOutputStream();
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            EasyExcel.write(byteArrayOutputStream)
                    .head(SampleInjectorExcelDTO.class)
                    .excelType(ExcelTypeEnum.CSV)
                    .sheet("进样表")
                    .doWrite(list);
            response.getOutputStream().write(byteArrayOutputStream.toByteArray());
            response.getOutputStream().flush();

        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    /**
     * 重置进样信息
     * @param dto
     * @return
     */
    @Override
    public SampleInjector reset(ResetSampleInjectorDTO dto) {
        /*// 转json成map处理
        if (dto.getType() == 2) { // 如果是方盘先合并数组，在处理
            dto.setInjectorInfo(
                    mergeSquareInjectorInfo(
                            dto.getInjectorInfo1(),
                            dto.getInjectorInfo2(),
                            dto.getInjectorInfo3(),
                            dto.getInjectorInfo4()
                    )
            );
        }
        List<Map<String, Object>> injectorInfo = JSONArray.toJavaObject(dto.getInjectorInfo(), List.class);
        // 使用并行流处理
        injectorInfo.parallelStream().forEach( item -> {
            String sampleNo = item.get("sampleNo").toString();
            if (StrUtil.isNotBlank(sampleNo)) {
                item.put("sampleNo", "");
                item.put("injectorCount", "");
                if (item.containsKey("active")) {
                    item.put("active", false);
                } else {
                    item.put("selected", false);
                }
            }

        });*/

        this.update(Wrappers.<SampleInjector>lambdaUpdate().eq(SampleInjector::getId, dto.getId())
                .set(SampleInjector::getInjectorInfo, null)
                .set(SampleInjector::getType, 0)
                .set(SampleInjector::isResetFlag, false)
                .set(SampleInjector::getBlankStartPosition, null)
                .set(SampleInjector::getInsertNumber, null));
        return this.getById(dto.getId());
    }

    /**
     * 根据实验id获取，数据为空则初始部分数据返回
     * @param testId
     * @return
     */
    @Override
    public SampleInjector getByTestId(String testId) {
        SampleInjector sampleInjector = this.getOne(Wrappers.<SampleInjector>lambdaQuery().eq(SampleInjector::getTestId, testId));
        if (sampleInjector == null) {
            sampleInjector = new SampleInjector();
            sampleInjector.setTestId(testId);
            // 查询不到设置保存标记为false
            sampleInjector.setResetFlag(false);

        } /*else if (sampleInjector.getType() == 2){
            // 方盘，取出json 处理
            JSONArray jsonArray = JSON.parseArray(sampleInjector.getInjectorInfo());
            for (int i = 0; i < jsonArray.size(); i++) {
                JSONObject jsonObject = jsonArray.getJSONObject(i);
                String frameNumber = jsonObject.getString("frameNumber");
                if (frameNumber.equals("P-1")) {
                    sampleInjector.getInjectorInfo1().add(jsonObject);
                } else if (frameNumber.equals("P-2")) {
                    sampleInjector.getInjectorInfo2().add(jsonObject);
                } else if (frameNumber.equals("P-3")) {
                    sampleInjector.getInjectorInfo3().add(jsonObject);
                } else if (frameNumber.equals("P-4")) {
                    sampleInjector.getInjectorInfo4().add(jsonObject);
                } else {

                }
            }
        }
        // 第一次访问或者可以重置的都初始方盘数据
        if (sampleInjector.getId() == null || !sampleInjector.isResetFlag()) {
            // 这里不管是圆盘还是方盘都返回方盘初始化的数组，前端根据情况选择参数
            sampleInjector.setInjectorInfo1(SampleInjectorConstant.P_1);
            sampleInjector.setInjectorInfo2(SampleInjectorConstant.P_2);
            sampleInjector.setInjectorInfo3(SampleInjectorConstant.P_3);
            sampleInjector.setInjectorInfo4(SampleInjectorConstant.P_4);
        }*/
        return sampleInjector;
    }

    /**
     * 判断当前进样器是否保存了进样位置信息
     * @param testId
     * @return
     */
    @Override
    public boolean whetherSave(String testId) {
        SampleInjector sampleInjector = this.getOne(Wrappers.<SampleInjector>lambdaQuery()
                .eq(SampleInjector::getTestId, testId)
                .eq(SampleInjector::isResetFlag, true)
                .isNotNull(SampleInjector::getInjectorInfo));
        // 判断不为null, 则执行下面代码
        if (sampleInjector != null) {
            return judgeOnTheMachine(sampleInjector);
        }
        return false;
    }

    /**
     * 判断是否正在上机
     * @param sampleInjector
     * @return
     */
    private boolean judgeOnTheMachine(SampleInjector sampleInjector) {
        JSONArray injectorJsonArray = getJsonArrayByInjectorType(sampleInjector);
        for (int i = 0; i < injectorJsonArray.size(); i++) {
            JSONObject jsonObject = injectorJsonArray.getJSONObject(i);
            String sampleNo = jsonObject.getString("sampleNo");
            boolean active = jsonObject.getBoolean("active");
            if (StrUtil.isNotBlank(sampleNo) && active) {
                return true;
            }
        }
        return false;
    }

    /**
     * 设置响应结果
     *
     * @param response    响应结果对象
     * @param rawFileName 文件名
     * @throws UnsupportedEncodingException 不支持编码异常
     */
    private void setExcelResponseProp(HttpServletResponse response, String rawFileName) throws UnsupportedEncodingException {
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");// "application/vnd.ms-excel"  "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
        response.setCharacterEncoding("utf-8");
        String fileName = URLEncoder.encode(rawFileName, "UTF-8");
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".csv");
    }

    /**
     * 组装圆盘进样信息导出的数据
     *
     * @param sampleInjector
     * @param resultConcentrationVOMap
     */
    private List<SampleInjectorExcelDTO> getSampleInjectorExportDataByDisk(SampleInjector sampleInjector, Map<String, String> resultConcentrationVOMap) {
        // new 一个返回的结果列表
        List<SampleInjectorExcelDTO> list = new ArrayList<>();
        // 根据进样器类型返回json数组
        JSONArray jsonArray = getJsonArrayByInjectorType(sampleInjector);

        int index = 1; // 导出表时的index值
        Integer blankCount = 1; // 空白穿插时的统计
        for (int i = 0; i < jsonArray.size(); i++) {
            JSONObject jsonObject = jsonArray.getJSONObject(i);
            String sampleNo = jsonObject.getString("sampleNo"); // 默认取溶液编号
            if (StrUtil.isNotBlank(sampleNo)) {
                if (sampleInjector.getBlankStartPosition() != null
                        && index == sampleInjector.getBlankStartPosition()
                        && !sampleNo.contains(StdSolutionNum.BLANK_SOLUTION.getPrefix())) {
                    // 判断是空白起始插入位置，空白开始插入
                    list.addAll(blankInsert(sampleInjector.getInsertNumber(), blankCount));
                }
                // 溶液编号不为空，证明这个进样盘号有检材
                SampleInjectorExcelDTO excelDTO = new SampleInjectorExcelDTO();
                excelDTO.setFileName(sampleNo);
                excelDTO.setId(index == 1 ? "ID" : "ID" + (index));
                if (sampleInjector.getType() == 1) {
                    excelDTO.setSampleLocation(jsonObject.get("title").toString());
                } else {
                    excelDTO.setSampleLocation(jsonObject.getString("frameNumber") + "-" + jsonObject.getString("name"));
                }
                excelDTO.setIndex(index);

                if (CollUtil.isNotEmpty(resultConcentrationVOMap)) {
                    excelDTO.setFileText(resultConcentrationVOMap.get(sampleNo));
                }
                if (sampleNo.contains(StdSolutionNum.BLANK_SOLUTION.getPrefix())) {
                    excelDTO.setType(SampleInjectorConstant.BLANK);
                } else if (sampleNo.contains(StdSolutionNum.SIMPLE_SOLUTION.getPrefix())) {
                    excelDTO.setType(SampleInjectorConstant.STANDARD);
                }
                list.add(excelDTO);

                /*if (sampleInjector.getBlankStartPosition() != null && index == sampleInjector.getBlankStartPosition()) {
                    // 判断是空白起始插入位置，空白结束插入
                    list.addAll(blankInsert(sampleInjector.getInsertNumber(), false));
                }*/
                index++;
            }
        }
        return list;
    }

    /**
     * 空白穿插
     * @param insertNumber
     * @param blankCount 是否是开始穿插， true 是， false 不是
     */

    private List<SampleInjectorExcelDTO> blankInsert(Integer insertNumber, Integer blankCount) {
        List<SampleInjectorExcelDTO> blankInsertList = new ArrayList<>();
        for (int i = 1; i <= insertNumber; i++) {
            SampleInjectorExcelDTO blankExcelDTO = new SampleInjectorExcelDTO();

            blankExcelDTO.setFileName(StdSolutionNum.BLANK_SOLUTION.getPrefix() + i);

            blankExcelDTO.setType(SampleInjectorConstant.BLANK);
            blankInsertList.add(blankExcelDTO);
            blankCount++;
        }
        return blankInsertList;
    }

    /**
     * 处理方盘的位置信息，把四个数组合并成一个
     * @param injectorInfo1 p-1
     * @param injectorInfo2 p-2
     * @param injectorInfo3 p-3
     * @param injectorInfo4 p-4
     * @return 返回json数组
     */
    private JSONArray mergeSquareInjectorInfo(JSONArray injectorInfo1,
                                              JSONArray injectorInfo2,
                                              JSONArray injectorInfo3,
                                              JSONArray injectorInfo4) {

        if (CollUtil.isEmpty(injectorInfo1)
                || CollUtil.isEmpty(injectorInfo2)
                || CollUtil.isEmpty(injectorInfo3)
                || CollUtil.isEmpty(injectorInfo4)) {
            throw new RuntimeException("方盘参数不全，请检查参数是否合法！");
        }
        // 合并四个数组到一个数组
        JSONArray resultArray = new JSONArray();
        resultArray.addAll(injectorInfo1);
        resultArray.addAll(injectorInfo2);
        resultArray.addAll(injectorInfo3);
        resultArray.addAll(injectorInfo4);
        return resultArray;

    }


    /**
     * 根据进样器类型返回json数组
     * @param one
     * @return
     */
    private static JSONArray getJsonArrayByInjectorType(SampleInjector one) {
        // 处理已经进样的数据
        JSONArray jsonArray = new JSONArray();
        if (one != null && StrUtil.isNotBlank(one.getInjectorInfo())) {
            // todo 返回列表时 相关检材的进样盘号也需要查询处理
            if (one.getType() == 1) {
                jsonArray = JSON.parseArray(one.getInjectorInfo());
            } else if (one.getType() == 2){
                JSONArray totalArray = JSON.parseArray(one.getInjectorInfo());
                for (int i = 0; i < totalArray.size(); i++) {
                    jsonArray.addAll(totalArray.getJSONArray(i));
                }
            }
        }
        return jsonArray;
    }

}
