package com.njcn.gather.device.service.impl;

import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.afterturn.easypoi.excel.entity.result.ExcelImportResult;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.njcn.common.pojo.enums.common.DataStateEnum;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.common.pojo.poi.PullDown;
import com.njcn.common.utils.EncryptionUtil;
import com.njcn.gather.device.mapper.PqStandardDevMapper;
import com.njcn.gather.device.pojo.param.PqStandardDevParam;
import com.njcn.gather.device.pojo.po.PqStandardDev;
import com.njcn.gather.device.pojo.po.PqStandardDevGain;
import com.njcn.gather.device.pojo.vo.PqStandardDevExcel;
import com.njcn.gather.device.pojo.vo.PqStandardDevVO;
import com.njcn.gather.device.pojo.vo.PreDetection;
import com.njcn.gather.device.service.IPqStandardDevGainService;
import com.njcn.gather.device.service.IPqStandardDevService;
import com.njcn.gather.plan.mapper.AdPlanStandardDevMapper;
import com.njcn.gather.plan.service.IAdPlanStandardDevService;
import com.njcn.gather.pojo.enums.DetectionResponseEnum;
import com.njcn.gather.system.dictionary.pojo.po.DictData;
import com.njcn.gather.system.dictionary.pojo.po.DictType;
import com.njcn.gather.system.dictionary.service.IDictDataService;
import com.njcn.gather.system.dictionary.service.IDictTypeService;
import com.njcn.gather.type.service.IDevTypeService;
import com.njcn.web.factory.PageFactory;
import com.njcn.web.utils.ExcelUtil;
import com.njcn.web.utils.PoiUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author 曹泽辉
 * @date 2025-07-02
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class PqStandardDevServiceImpl extends ServiceImpl<PqStandardDevMapper, PqStandardDev> implements IPqStandardDevService {
    private final IDevTypeService devTypeService;
    private final IDictDataService dictDataService;
    private final IDictTypeService dictTypeService;
    private final IPqStandardDevGainService pqStandardDevGainService;

    @Override
    public Page<PqStandardDev> listPqStandardDevs(PqStandardDevParam.QueryParam queryParam) {
        QueryWrapper<PqStandardDev> wrapper = new QueryWrapper<>();
        wrapper.like(StrUtil.isNotBlank(queryParam.getName()), "name", queryParam.getName())
                .eq(StrUtil.isNotBlank(queryParam.getManufacturer()), "manufacturer", queryParam.getManufacturer())
                .eq(StrUtil.isNotBlank(queryParam.getDevType()), "dev_type", queryParam.getDevType())
                .eq("state", DataStateEnum.ENABLE.getCode())
                .orderByDesc("create_time");
        return this.page(new Page<>(PageFactory.getPageNum(queryParam), PageFactory.getPageSize(queryParam)), wrapper);
    }

    @Override
    public PqStandardDevVO getPqStandardDevById(String id) {
        PqStandardDev standardDev = this.getById(id);
        PqStandardDevVO pqStandardDevVO = BeanUtil.copyProperties(standardDev, PqStandardDevVO.class);
        if (standardDev.getEncryptionFlag() == 1) {
            pqStandardDevVO.setSeries(EncryptionUtil.decoderString(1, standardDev.getSeries()));
            pqStandardDevVO.setDevKey(EncryptionUtil.decoderString(1, standardDev.getDevKey()));
        }

        List<PqStandardDevGain> gainList = pqStandardDevGainService.list(id);
        pqStandardDevVO.setGainList(gainList);
        return pqStandardDevVO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean addPqStandardDev(PqStandardDevParam param) {
        this.checkRepeat(param, false);
        PqStandardDev pqStandardDev = BeanUtil.copyProperties(param, PqStandardDev.class);
        pqStandardDev.setState(DataStateEnum.ENABLE.getCode());
        this.checkEncryption(pqStandardDev);
        return this.save(pqStandardDev);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updatePqStandardDev(PqStandardDevParam.UpdateParam param) {
        this.checkRepeat(param, true);
        PqStandardDev pqStandardDev = BeanUtil.copyProperties(param, PqStandardDev.class);
        this.checkEncryption(pqStandardDev);
        return this.updateById(pqStandardDev);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deletePqStandardDevs(List<String> ids) {
        LambdaUpdateWrapper<PqStandardDev> wrapper = new LambdaUpdateWrapper();
        wrapper.set(PqStandardDev::getState, DataStateEnum.DELETED.getCode())
                .in(PqStandardDev::getId, ids);
        return this.update(wrapper);
    }

    @Override
    public void export(PqStandardDevParam.QueryParam queryParam) {
        QueryWrapper<PqStandardDev> wrapper = new QueryWrapper<>();
        wrapper.like(StrUtil.isNotBlank(queryParam.getName()), "name", queryParam.getName())
                .eq(StrUtil.isNotBlank(queryParam.getManufacturer()), "manufacturer", queryParam.getManufacturer())
                .eq(StrUtil.isNotBlank(queryParam.getDevType()), "dev_type", queryParam.getDevType())
                .eq("state", DataStateEnum.ENABLE.getCode());
        List<PqStandardDev> pqStandardDevs = this.list(wrapper);
        this.visualizeStandardDev(pqStandardDevs);
        List<PqStandardDevExcel> pqStandardDevExcels = BeanUtil.copyToList(pqStandardDevs, PqStandardDevExcel.class);
        ExcelUtil.exportExcelPullDown(new ExportParams(), "标准设备导出数据.xlsx", 1, this.getStandardDevPullDownList(), PqStandardDevExcel.class, pqStandardDevExcels);
    }


    @Override
    public void downloadTemplate() {
        ExcelUtil.exportExcelPullDown(new ExportParams(), "标准设备导入模板.xlsx", 1, this.getStandardDevPullDownList(), PqStandardDevExcel.class, Collections.emptyList());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void importData(MultipartFile file, HttpServletResponse response) {
        ImportParams params = new ImportParams();
        params.setStartSheetIndex(0);
        params.setSheetNum(1);
        params.setNeedVerify(true);
        params.setHeadRows(1);

        List<PqStandardDevExcel> contrastDevExcelList = null;
        try {
            ExcelImportResult<PqStandardDevExcel> excelImportResult = ExcelImportUtil.importExcelMore(file.getInputStream(), PqStandardDevExcel.class, params);
            if (excelImportResult.isVerifyFail()) {
                // 此处前端要做特殊处理，具体可以参考技术监督的数据导入
                Workbook failWorkbook = excelImportResult.getFailWorkbook();
                PoiUtil.exportFileByWorkbook(failWorkbook, "非法被检设备数据.xlsx", response);
            } else {
                contrastDevExcelList = excelImportResult.getList();
            }
        } catch (Exception e) {
            throw new BusinessException(DetectionResponseEnum.IMPORT_DATA_FAIL);
        }
        this.importData(contrastDevExcelList);
    }

//    @Override
//    public List<PqStandardDev> listByPlanId(String planId) {
//        return adPlanStandardDevMapper.listByPlanId(Collections.singletonList(planId));
//    }

    @Override
    public List<PreDetection> listStandardDevPreDetection(List<String> ids) {
        if (CollectionUtil.isNotEmpty(ids)) {
            return this.baseMapper.listStandardDevPreDetection(ids);
        }
        return Collections.emptyList();
    }

    /**
     * 导入数据
     *
     * @param contrastDevExcelList
     * @return
     */
    private void importData(List<PqStandardDevExcel> contrastDevExcelList) {
        if (CollectionUtil.isNotEmpty(contrastDevExcelList)) {
            List<PqStandardDev> contrastDevs = BeanUtil.copyToList(contrastDevExcelList, PqStandardDev.class);
            this.reverseVisualizeStandardDev(contrastDevs);
            this.saveBatch(contrastDevs);
        }
    }

    /**
     * 获取标准设备导出文件下拉列表
     *
     * @return
     */
    private List<PullDown> getStandardDevPullDownList() {
        List<PullDown> pullDownList = new ArrayList<>();

        PullDown pullDown = new PullDown();

        // 设备类型
        pullDown.setFirstCol(1);
        pullDown.setLastCol(1);
        pullDown.setStrings(devTypeService.listAll().stream().map(devType -> devType.getName()).collect(Collectors.toList()));
        pullDownList.add(pullDown);


        List<DictData> dictDataList = null;
        // 设备厂家
        DictType dictType = dictTypeService.getByCode("Dev_Manufacturers");
        if (ObjectUtil.isNotNull(dictType)) {
            dictDataList = dictDataService.getDictDataByTypeId(dictType.getId());

            pullDown = new PullDown();
            pullDown.setFirstCol(2);
            pullDown.setLastCol(2);
            pullDown.setStrings(dictDataList.stream().map(DictData::getName).collect(Collectors.toList()));
            pullDownList.add(pullDown);
        }

        // 通讯协议
        dictType = dictTypeService.getByCode("Protocol");
        if (ObjectUtil.isNotNull(dictType)) {
            dictDataList = dictDataService.getDictDataByTypeId(dictType.getId());

            pullDown = new PullDown();
            pullDown.setFirstCol(4);
            pullDown.setLastCol(4);
            pullDown.setStrings(dictDataList.stream().map(DictData::getName).collect(Collectors.toList()));
            pullDownList.add(pullDown);
        }

        // 是否加密
        pullDown = new PullDown();
        pullDown.setFirstCol(7);
        pullDown.setLastCol(7);
        pullDown.setStrings(Arrays.asList("否", "是"));
        pullDownList.add(pullDown);

        return pullDownList;
    }

    /**
     * 可视化标准设备
     *
     * @param pqStandardDevs
     */
    private void visualizeStandardDev(List<PqStandardDev> pqStandardDevs) {
        pqStandardDevs.forEach(pqStandardDev -> {
            pqStandardDev.setDevType(devTypeService.getById(pqStandardDev.getDevType()).getName());
            pqStandardDev.setManufacturer(dictDataService.getDictDataById(pqStandardDev.getManufacturer()).getName());
            pqStandardDev.setProtocol(dictDataService.getDictDataById(pqStandardDev.getProtocol()).getName());
            if (pqStandardDev.getEncryptionFlag() == 1) {
                pqStandardDev.setSeries(EncryptionUtil.decoderString(1, pqStandardDev.getSeries()));
                pqStandardDev.setDevKey(EncryptionUtil.decoderString(1, pqStandardDev.getDevKey()));
            }
        });
    }

    /**
     * 逆向可视化标准设备
     *
     * @param pqStandardDevs
     */
    private void reverseVisualizeStandardDev(List<PqStandardDev> pqStandardDevs) {
        pqStandardDevs.forEach(pqStandardDev -> {
            PqStandardDevParam pqStandardDevParam = BeanUtil.copyProperties(pqStandardDev, PqStandardDevParam.class);
            this.checkRepeat(pqStandardDevParam, false);
            this.checkEncryption(pqStandardDev);
            pqStandardDev.setDevType(devTypeService.getByName(pqStandardDev.getDevType()).getId());
            pqStandardDev.setManufacturer(dictDataService.getDictDataByName(pqStandardDev.getManufacturer()).getId());
            pqStandardDev.setProtocol(dictDataService.getDictDataByName(pqStandardDev.getProtocol()).getId());
            pqStandardDev.setState(DataStateEnum.ENABLE.getCode());
        });
    }

    /**
     * 检查是否重复
     *
     * @param pqStandardDevParam
     * @param isExcludeSelf
     */
    private void checkRepeat(PqStandardDevParam pqStandardDevParam, boolean isExcludeSelf) {
        QueryWrapper<PqStandardDev> wrapper = new QueryWrapper<>();
        wrapper.eq("pq_standard_dev.Name", pqStandardDevParam.getName())
                .eq("pq_standard_dev.State", DataStateEnum.ENABLE.getCode());
        if (isExcludeSelf) {
            if (pqStandardDevParam instanceof PqStandardDevParam.UpdateParam) {
                wrapper.ne("pq_standard_dev.Id", ((PqStandardDevParam.UpdateParam) pqStandardDevParam).getId());
            }
        }
        int count = this.count(wrapper);
        if (count > 0) {
            throw new BusinessException(DetectionResponseEnum.PQ_STANDARD_DEV_REPEAT);
        }
    }

    /**
     * 检查加密情况
     *
     * @param pqStandardDev
     */
    private void checkEncryption(PqStandardDev pqStandardDev) {
        if (pqStandardDev.getEncryptionFlag() == 1) {
            if (StrUtil.isNotBlank(pqStandardDev.getSeries()) && StrUtil.isNotBlank(pqStandardDev.getDevKey())) {
                pqStandardDev.setSeries(EncryptionUtil.encodeString(1, pqStandardDev.getSeries()));
                pqStandardDev.setDevKey(EncryptionUtil.encodeString(1, pqStandardDev.getDevKey()));
            } else {
                throw new BusinessException(DetectionResponseEnum.SERIES_AND_DEVKEY_NOT_BLANK);
            }
        }
    }

    @Override
    public List<PqStandardDev> canBindingList() {
        /*List<String> excludeStandardDevIds = new ArrayList<>();
        // 获取所有已绑定的标准设备
        List<AdPlanStandardDev> boundList = adPlanStandardDevService.list();
        if (CollectionUtil.isNotEmpty(boundList)) {
            // 获取对应检测计划
            List<String> planIds = boundList.stream().map(AdPlanStandardDev::getPlanId).collect(Collectors.toList());
            IAdPlanService adPlanService = SpringUtil.getBean(IAdPlanService.class);
            List<AdPlan> planList = adPlanService.lambdaQuery().in(AdPlan::getId, planIds).eq(AdPlan::getState, DataStateEnum.ENABLE.getCode()).list();
            // 区分主计划和子计划
            List<AdPlan> mainPlanList = planList.stream().filter(plan -> plan.getFatherPlanId().equals(CommonEnum.FATHER_ID.getValue())).collect(Collectors.toList());
            List<AdPlan> subPlanList = planList.stream().filter(plan -> !plan.getFatherPlanId().equals(CommonEnum.FATHER_ID.getValue())).collect(Collectors.toList());
            List<String> excludePlanIds = new ArrayList<>();

            // 主计划未完成直接排除
            if (CollectionUtil.isNotEmpty(mainPlanList)) {
                List<String> excludeMainPlanIds = mainPlanList.stream().filter(plan -> !plan.getTestState().equals(CheckStateEnum.CHECKED.getValue())).map(AdPlan::getId).collect(Collectors.toList());
                if (CollectionUtil.isNotEmpty(excludeMainPlanIds)) {
                    excludePlanIds.addAll(excludeMainPlanIds);
                }

            }
            // 子计划需要判断其主计划, 如果主计划未完成则排除
            if (CollectionUtil.isNotEmpty(subPlanList)) {
                List<String> fatherPlanIds = subPlanList.stream().map(AdPlan::getFatherPlanId).distinct().collect(Collectors.toList());
                List<AdPlan> fatherPlanList = adPlanService.listByIds(fatherPlanIds);
                List<String> excludeFatherPlanIds = fatherPlanList.stream()
                        .filter(plan -> !plan.getTestState().equals(CheckStateEnum.CHECKED.getValue()))
                        .map(AdPlan::getId).collect(Collectors.toList());
                List<String> excludeSubPlanIds = subPlanList.stream()
                        .filter(plan -> excludeFatherPlanIds.contains(plan.getFatherPlanId()))
                        .map(AdPlan::getId).collect(Collectors.toList());
                excludePlanIds.addAll(excludeSubPlanIds);
            }
            if (CollectionUtil.isNotEmpty(excludePlanIds)) {
                List<AdPlanStandardDev> excludeBoundList = boundList.stream()
                        .filter(bound -> excludePlanIds.contains(bound.getPlanId()))
                        .collect(Collectors.toList());
                excludeStandardDevIds = excludeBoundList.stream().map(AdPlanStandardDev::getStandardDevId).collect(Collectors.toList());
            }
        }*/
        List<PqStandardDev> list = this.lambdaQuery()
                .eq(PqStandardDev::getState, DataStateEnum.ENABLE.getCode())
                .list();
        /*for (PqStandardDev pqStandardDev : list) {
            pqStandardDev.setDisabled(excludeStandardDevIds.contains(pqStandardDev.getId()));
        }*/
        return list;
    }
}
