package com.bonus.material.device.service.impl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.bonus.common.core.utils.DateUtils;
import com.bonus.common.core.utils.StringUtils;
import com.bonus.common.core.web.domain.AjaxResult;
import com.bonus.common.security.utils.SecurityUtils;
import com.bonus.material.devConfig.domain.EquipmentProperty;
import com.bonus.material.devConfig.mapper.EquipmentPropertyMapper;
import com.bonus.material.devConfig.service.EquipmentPropertyService;
import com.bonus.material.devchange.domain.MaDevFile;
import com.bonus.material.devchange.domain.MaDevInfo;
import com.bonus.material.devchange.domain.MapBean;
import com.bonus.material.devchange.mapper.MaDevInfoMapper;
import com.bonus.material.device.domain.EquipmentImportDTO;
import com.bonus.material.device.domain.MaDevQc;
import com.bonus.material.device.domain.vo.DevInfoPropertyVo;
import com.bonus.material.device.domain.vo.DevInfoVo;
import com.bonus.material.device.domain.vo.DevMergeVo;
import com.bonus.material.device.mapper.DevInfoMapper;
import com.bonus.material.device.mapper.DevMergeMapper;
import com.bonus.material.device.mapper.MaDevQcMapper;
import com.bonus.material.device.service.DevMergeService;
import com.bonus.material.device.service.MaDevQcService;
import com.bonus.material.utils.FolderZipUtil;
import com.bonus.system.api.RemoteFileService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolation;
import javax.validation.Validator;
import java.io.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipInputStream;

import static com.bonus.common.biz.constant.MaterialConstants.ADMIN_ID;
import static com.bonus.common.biz.constant.MaterialConstants.PROVINCE_COMPANY_DEPT_ID;

/**
 * 设备信息Service业务层处理
 *
 * @author syruan
 */
@Service
@Slf4j
public class DevMergeServiceImpl implements DevMergeService {

    @Resource
    private RemoteFileService remoteFileService;
    @Resource
    private DevMergeMapper devMergeMapper;
    @Resource
    private DevInfoMapper devInfoMapper;
    @Autowired
    private MaDevInfoMapper mapper;
    @Autowired
    private Validator validator;
    @Autowired
    private MaDevQcMapper qcMapper;

    @Resource
    private EquipmentPropertyMapper equipmentPropertyMapper;

    @Override
    public List<DevMergeVo> list(DevMergeVo devInfo) {
        Long deptId = SecurityUtils.getLoginUser().getSysUser().getDeptId();
        Long userId = SecurityUtils.getLoginUser().getUserid();
        // 管理员和省公司可查看所有数据
        if (userId != null && deptId != null
                && !userId.equals(ADMIN_ID)
                && !deptId.equals(PROVINCE_COMPANY_DEPT_ID)) {
            devInfo.setCompanyId(deptId);
        }
        return devMergeMapper.list(devInfo);
    }

    @Override
    public List<DevInfoVo> devList(DevInfoVo devInfo) {
        return devMergeMapper.devList(devInfo);
    }

    @Override
    public AjaxResult addOrder() {
        DevMergeVo o = new DevMergeVo();
        o.setCreateUser(SecurityUtils.getLoginUser().getSysUser().getNickName());
        o.setOrderNumber(generate());
        Integer i = devMergeMapper.addOrder(o);
        return AjaxResult.success("添加成功", o);
    }

    @Override
    public void insertOrderDevReal(String orderId, Long maId) {
        devMergeMapper.insertOrderDevReal(orderId, maId);
    }

    @Override
    public AjaxResult submitOrder(DevMergeVo o) {

        Integer num = devMergeMapper.selNum(o);
        if (num == 0) {
            return AjaxResult.warn("请先添加装备");
        }


        Integer i = devMergeMapper.submitOrder(o);
        if (i > 0) {
            devMergeMapper.updateDeviceStatus(o);
        }
        return AjaxResult.success("提交成功");
    }

    @Override
    public AjaxResult delOrder(DevMergeVo o) {
        int i = devMergeMapper.delOrder(o);
        return i > 0 ? AjaxResult.success("删除成功") : AjaxResult.error("删除失败");
    }

    @Override
    public AjaxResult checkDevice(DevMergeVo o) {
        String[] split = o.getDevIds().split(",");
        if (split.length > 0) {
            List<MapBean> list = new ArrayList<>();
            for (String s : split) {
                list.add(new MapBean(s, o.getStatus()));
            }
            if (o.getStatus().equals("1")) {
                devMergeMapper.updateChangeStatus(list);
            }

            int i = devMergeMapper.updateDevice(list, o.getStatus());
            //判断有没有审批完
            int b = devMergeMapper.getDevNoCheck(o.getId());
            if (b == 0) {
                o.setStatus("1");
                devMergeMapper.submitOrder(o);
            }
            return i > 0 ? AjaxResult.success("操作成功") : AjaxResult.error("操作失败");
        } else {
            return AjaxResult.error("操作失败");
        }
    }

    /**
     * 查询所有一级类型（顶级类型）
     *
     * @return 一级类型列表
     */
    @Override
    public AjaxResult findFirstLevelTypes() {
        try {
            List<Map<String, Object>> list = devMergeMapper.findFirstLevelTypes();
            if (CollectionUtils.isEmpty(list)) {
                return AjaxResult.error();
            } else {
                return AjaxResult.success(list);
            }
        } catch (Exception e) {
            return AjaxResult.error();
        }
    }

    /**
     * @return
     */
    @Override
    public AjaxResult getSecondToFifthLevelTypes(Long firstLevelId) {
        try {
            List<Map<String, Object>> list = devMergeMapper.getSecondToFifthLevelTypes(firstLevelId);
            if (CollectionUtils.isEmpty(list)) {
                return AjaxResult.error();
            } else {
                return AjaxResult.success(list);
            }
        } catch (Exception e) {
            return AjaxResult.error();
        }
    }

    /**
     * 根据一级类型ID查询其下的二、三级类型
     *
     * @param firstLevelId 一级类型ID
     * @return 二、三级类型列表
     */
    @Override
    public AjaxResult findSecondAndThirdLevelTypes(Long firstLevelId) {
        try {
            List<Map<String, Object>> list = devMergeMapper.findSecondAndThirdLevelTypes(firstLevelId);
            if (CollectionUtils.isEmpty(list)) {
                return AjaxResult.error();
            } else {
                return AjaxResult.success(list);
            }
        } catch (Exception e) {
            return AjaxResult.error();
        }
    }

    /**
     * 根据三级类型ID查询其下的四、五、六级类型
     *
     * @param thirdLevelId 三级类型ID
     * @return 四、五、六级类型列表
     */
    @Override
    public AjaxResult getFourthToSixthLevelTypes(Long thirdLevelId) {
        try {
            List<Map<String, Object>> list = devMergeMapper.getFourthToSixthLevelTypes(thirdLevelId);
            if (CollectionUtils.isEmpty(list)) {
                return AjaxResult.error();
            } else {
                return AjaxResult.success(list);
            }
        } catch (Exception e) {
            return AjaxResult.error();
        }
    }

    /**
     * @param maDevInfo
     * @return
     */
    @Override
    public AjaxResult interDevice(MaDevInfo maDevInfo) {
        try {
            Long thisLoginUserDeptId = SecurityUtils.getLoginUser().getSysUser().getDeptId();
            String province = SecurityUtils.getLoginUser().getSysUser().getDept().getProvince();
            maDevInfo.setPropertyUnitId(Math.toIntExact(thisLoginUserDeptId));
            maDevInfo.setCode(getString());
            maDevInfo.setProvince(province);
            Integer i = devMergeMapper.interDevice(maDevInfo);
            if (i > 0) {
                devInfoMapper.deleteDevInfoProperties(Long.valueOf(maDevInfo.getMaId()));
                MaDevQc maDevQc = new MaDevQc();
                maDevQc.setMaId(maDevInfo.getMaId());
                maDevQc.setQcCode(maDevInfo.getCode());
                maDevQc.setCreateBy(String.valueOf(SecurityUtils.getUserId()));
                maDevQc.setCreateTime(DateUtils.getNowDate());
                maDevQc.setQcCom(Optional.ofNullable(SecurityUtils.getLoginUser().getSysUser().getCompanyId()).orElse(SecurityUtils.getLoginUser().getSysUser().getDeptId()).toString());
                maDevQc.setNextCheckTime(maDevInfo.getNextMaintenanceDate());
                qcMapper.insertDevQc(maDevQc);
                if (!CollectionUtils.isEmpty(maDevInfo.getPropertyVoList())) {
                    devInfoMapper.insertDevInfoProperties(Long.valueOf(maDevInfo.getMaId()), maDevInfo.getPropertyVoList());
                }
                devMergeMapper.insertOrderDevReal(String.valueOf(maDevInfo.getOrderId()), Long.valueOf(maDevInfo.getMaId()));
                maDevInfo.getAppearanceImages().forEach(item -> {
                    // 这里编写对每个 image 的处理逻辑，比如打印、处理等
                    item.setFileType(1);
                    item.setMaId(maDevInfo.getMaId());
                    item.setCreator(Math.toIntExact(SecurityUtils.getLoginUser().getUserid()));
                    devMergeMapper.interFile(item);
                });

                maDevInfo.getCertificates().forEach(item -> {
                    // 这里编写对每个 image 的处理逻辑，比如打印、处理等
                    item.setFileType(2);
                    item.setMaId(maDevInfo.getMaId());
                    item.setCreator(Math.toIntExact(SecurityUtils.getLoginUser().getUserid()));
                    devMergeMapper.interFile(item);
                });

                maDevInfo.getInspectionReports().forEach(item -> {
                    // 这里编写对每个 image 的处理逻辑，比如打印、处理等
                    item.setFileType(3);
                    item.setMaId(maDevInfo.getMaId());
                    item.setCreator(Math.toIntExact(SecurityUtils.getLoginUser().getUserid()));
                    devMergeMapper.interFile(item);
                });

                maDevInfo.getPurchaseInvoices().forEach(item -> {
                    // 这里编写对每个 image 的处理逻辑，比如打印、处理等
                    item.setFileType(4);
                    item.setMaId(maDevInfo.getMaId());
                    item.setCreator(Math.toIntExact(SecurityUtils.getLoginUser().getUserid()));
                    devMergeMapper.interFile(item);
                });
            }

            return i > 0 ? AjaxResult.success() : AjaxResult.error();
        } catch (Exception e) {
            log.error(e.getMessage());
            return AjaxResult.error();
        }
    }


    private String getString() {
        //根据前台传过来的数据，生成需求编号
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
        Date nowDate = DateUtils.getNowDate();
        String format = dateFormat.format(nowDate);
        String taskNum = devInfoMapper.selectTaskNumByMonth(nowDate);
        if (StringUtils.isNotBlank(taskNum)) {
            // 将字符串转换为整数
            int num = Integer.parseInt(taskNum);
            // 执行加一操作
            num++;
            // 将结果转换回字符串格式，并确保结果是4位数，不足4位则在前面补0
            taskNum = String.format("%04d", num);
        } else {
            taskNum = "0001";
        }
        return format + "-" + taskNum;
    }

    /**
     * @param o
     * @return
     */
    @Override
    public List<MaDevInfo> getDevice(MaDevInfo o) {
       /* Long thisLoginUserDeptId = SecurityUtils.getLoginUser().getSysUser().getDeptId();
        o.setPropertyUnitId(Math.toIntExact(thisLoginUserDeptId));*/
        List<MaDevInfo> list = devMergeMapper.getDevice(o);
        list.forEach(item -> {
            List<DevInfoPropertyVo> propertiiesList = mapper.getProperties(item);
            if (propertiiesList != null && !propertiiesList.isEmpty()) {
                item.setPropertyVoList(propertiiesList);
            }
            item.setAppearanceImages(mapper.getFileList(item.getMaId(), 1));
            item.setCertificates(mapper.getFileList(item.getMaId(), 2));
            item.setInspectionReports(mapper.getFileList(item.getMaId(), 3));
            item.setPurchaseInvoices(mapper.getFileList(item.getMaId(), 4));
        });
        return list;
    }


    /**
     * @param o
     * @return
     */
    @Override
    public List<MaDevInfo> getDeviceByOrderId(MaDevInfo o) {
        List<MaDevInfo> list = devMergeMapper.getDeviceByOrderId(o);
        list.forEach(item -> {
            log.info("查询装备特征项：maId={}", item.getMaId());
            List<DevInfoPropertyVo> propertiiesList = mapper.getProperties(item);
            log.info("查询到的特征项数量：{}", propertiiesList != null ? propertiiesList.size() : 0);
            if (propertiiesList != null && !propertiiesList.isEmpty()) {
                propertiiesList.forEach(prop -> {
                    log.info("特征项：name={}, value={}", prop.getPropertyName(), prop.getPropertyValue());
                });
                item.setPropertyVoList(propertiiesList);
            }
            item.setAppearanceImages(mapper.getFileList(item.getMaId(), 1));
            item.setCertificates(mapper.getFileList(item.getMaId(), 2));
            item.setInspectionReports(mapper.getFileList(item.getMaId(), 3));
            item.setPurchaseInvoices(mapper.getFileList(item.getMaId(), 4));
        });
        return list;
    }

    /**
     * @param maDevInfo
     * @return
     */
    @Override
    public AjaxResult updateDevice(MaDevInfo maDevInfo) {
        try {
            Integer i = devMergeMapper.updateDeviceByMaId(maDevInfo);

            if (i > 0) {
                devInfoMapper.deleteDevInfoProperties(Long.valueOf(maDevInfo.getMaId()));
                devMergeMapper.delFile(maDevInfo.getMaId());
                if (!CollectionUtils.isEmpty(maDevInfo.getPropertyVoList())) {
                    devInfoMapper.insertDevInfoProperties(Long.valueOf(maDevInfo.getMaId()), maDevInfo.getPropertyVoList());
                }
                MaDevQc maDevQc = new MaDevQc();
                maDevQc.setMaId(maDevInfo.getMaId());
                maDevQc.setQcCode(maDevInfo.getCode());
                maDevQc.setCreateBy(String.valueOf(SecurityUtils.getUserId()));
                maDevQc.setCreateTime(DateUtils.getNowDate());
                maDevQc.setQcCom(Optional.ofNullable(SecurityUtils.getLoginUser().getSysUser().getCompanyId()).orElse(SecurityUtils.getLoginUser().getSysUser().getDeptId()).toString());
                maDevQc.setNextCheckTime(maDevInfo.getNextMaintenanceDate());
                qcMapper.insertDevQc(maDevQc);
                maDevInfo.getAppearanceImages().forEach(item -> {
                    // 这里编写对每个 image 的处理逻辑，比如打印、处理等
                    item.setFileType(1);
                    item.setMaId(maDevInfo.getMaId());
                    item.setCreator(Math.toIntExact(SecurityUtils.getLoginUser().getUserid()));
                    devMergeMapper.interFile(item);
                });

                maDevInfo.getCertificates().forEach(item -> {
                    // 这里编写对每个 image 的处理逻辑，比如打印、处理等
                    item.setFileType(2);
                    item.setMaId(maDevInfo.getMaId());
                    item.setCreator(Math.toIntExact(SecurityUtils.getLoginUser().getUserid()));
                    devMergeMapper.interFile(item);
                });

                maDevInfo.getInspectionReports().forEach(item -> {
                    // 这里编写对每个 image 的处理逻辑，比如打印、处理等
                    item.setFileType(3);
                    item.setMaId(maDevInfo.getMaId());
                    item.setCreator(Math.toIntExact(SecurityUtils.getLoginUser().getUserid()));
                    devMergeMapper.interFile(item);
                });

                maDevInfo.getPurchaseInvoices().forEach(item -> {
                    // 这里编写对每个 image 的处理逻辑，比如打印、处理等
                    item.setFileType(4);
                    item.setMaId(maDevInfo.getMaId());
                    item.setCreator(Math.toIntExact(SecurityUtils.getLoginUser().getUserid()));
                    devMergeMapper.interFile(item);
                });
            }
            return i > 0 ? AjaxResult.success("修改成功") : AjaxResult.error("修改失败");
        } catch (Exception e) {
            log.error(e.getMessage());
            return AjaxResult.error();
        }
    }

    /**
     * @param list
     * @param orderId
     * @return
     */
    @Transactional(rollbackFor = Exception.class) // 添加事务，确保数据一致性
    @Override
    public AjaxResult importData(List<EquipmentImportDTO> list, String orderId) {
        // 1. 基础校验和过滤null元素（Excel空行会被解析为null）
        if (list == null || list.isEmpty()) {
            return AjaxResult.error("表格内没有数据");
        }

        // 2. 过滤null元素
        list = list.stream().filter(Objects::nonNull)
                .filter(item -> StringUtils.hasText(item.getProfession()) || isCoreFieldHasValue(item))
                .collect(Collectors.toList());

        // 2.1 过滤装备类目都没选择的数据
        //list = list.stream().filter(item -> StringUtils.isNotBlank(item.getProfession())).collect(Collectors.toList());

        // 3. 再次检查过滤后是否有有效数据
        if (list.isEmpty()) {
            return AjaxResult.error("表格内没有有效数据");
        }

        if (ObjectUtil.isEmpty(orderId)) {
            DevMergeVo o = new DevMergeVo();
            o.setCreateUser(SecurityUtils.getLoginUser().getSysUser().getNickName());
            o.setOrderNumber(generate());
            devMergeMapper.addOrder(o);
            orderId = o.getId();
        }

        // 2. 批量校验DTO字段合法性（JSR-303）
        List<String> errorMessages = new ArrayList<>();
//        for (int i = 0; i < list.size(); i++) {
//            EquipmentImportDTO item = list.get(i);
//            Set<ConstraintViolation<EquipmentImportDTO>> violations = validator.validate(item);
//            if (!violations.isEmpty()) {
//                StringBuilder sb = new StringBuilder();
//                sb.append("第").append(i + 1).append("行数据错误："); // 行号从1开始，方便用户定位
//                for (ConstraintViolation<EquipmentImportDTO> violation : violations) {
//                    sb.append(violation.getMessage()).append("；");
//                }
//                errorMessages.add(sb.toString());
//            }
//        }
//        if (!errorMessages.isEmpty()) {
//            return AjaxResult.error("导入失败，存在以下错误：" + String.join(" ", errorMessages));
//        }

        // 4. 遍历数据，进行最终校验和导入
        for (int i = 0; i < list.size(); i++) {
            EquipmentImportDTO item = list.get(i);
            StringBuilder rowError = new StringBuilder();
            rowError.append("第").append(i + 1).append("行：");

            // 4.4 校验日期格式（若框架未自动处理，可添加）
            // 示例：校验productionDate是否为有效日期（根据实际需求调整）
            LocalDate productionDate = item.getProductionDate().toInstant()
                    .atZone(ZoneId.systemDefault()).toLocalDate();
            LocalDate purchaseDate = item.getPurchaseDate().toInstant()
                    .atZone(ZoneId.systemDefault()).toLocalDate();
            if (productionDate.isAfter(purchaseDate)) {
                rowError.append("生产日期不能晚于采购日期；");
            }
            Integer typeId = devMergeMapper.getTypeId(item.getProfession());
            if (ObjectUtil.isEmpty(typeId)) {
                rowError.append("请选择类别");
            }
            // 4.5 若当前行有错误，记录并继续校验其他行
            if (rowError.length() > ("第" + (i + 1) + "行：").length()) {
                errorMessages.add(rowError.toString());
                continue;
            }

            // 5. 校验通过，执行导入逻辑
            try {
                MaDevInfo maDevInfo = new MaDevInfo();

                maDevInfo.setTypeId(typeId);
                maDevInfo.setProductionDate(item.getProductionDate());
                maDevInfo.setPurchaseDate(item.getPurchaseDate());
                maDevInfo.setSpecificationModel(item.getSpecification());
                maDevInfo.setOriginalValue(item.getOriginalValue());
                maDevInfo.setName(item.getEquipmentName());
                maDevInfo.setManufacturerId(devMergeMapper.getManufacturer(item.getManufacturer())); // 注意：这里假设getManufacturer返回的是ID，需根据实际Mapper方法调整
                maDevInfo.setNextMaintenanceDate(item.getNextMaintenanceDate());
                maDevInfo.setOriginalCode(item.getOriginalCode());
                maDevInfo.setMaxServiceLifeYears(item.getMaxServiceYears());
                maDevInfo.setUnit(item.getUnit());
                Long thisLoginUserDeptId = SecurityUtils.getLoginUser().getSysUser().getDeptId();
                maDevInfo.setPropertyUnitId(Math.toIntExact(thisLoginUserDeptId));
                maDevInfo.setCode(getString());

                Integer insertResult = devMergeMapper.interDevice(maDevInfo);
                devInfoMapper.deleteDevInfoProperties(Long.valueOf(maDevInfo.getMaId()));

                // 处理特征项数据
                List<DevInfoPropertyVo> devInfoPropertyVos = new ArrayList<>();

                // 从Excel导入的9组特征项数据中提取有效数据
                String[] propertyNames = {
                        item.getPropertyName1(), item.getPropertyName2(), item.getPropertyName3(),
                        item.getPropertyName4(), item.getPropertyName5(), item.getPropertyName6(),
                        item.getPropertyName7(), item.getPropertyName8(), item.getPropertyName9()
                };
                String[] propertyValues = {
                        item.getPropertyValue1(), item.getPropertyValue2(), item.getPropertyValue3(),
                        item.getPropertyValue4(), item.getPropertyValue5(), item.getPropertyValue6(),
                        item.getPropertyValue7(), item.getPropertyValue8(), item.getPropertyValue9()
                };

                // 遍历9组特征项，只保存有效的（特征项名称和特征值都不为空）
                for (int j = 0; j < 9; j++) {
                    String propertyName = propertyNames[j];
                    String propertyValue = propertyValues[j];

                    log.info("第{}行，特征项{}：name={}, value={}", i + 1, j + 1, propertyName, propertyValue);

                    // 只有当特征项名称和特征值都不为空时才保存
                    if (propertyName != null && !propertyName.trim().isEmpty() && propertyValue != null && !propertyValue.trim().isEmpty()) {

                        DevInfoPropertyVo entity = new DevInfoPropertyVo();
                        entity.setPropertyName(propertyName.trim());
                        entity.setPropertyValue(propertyValue.trim());
                        // 注意：这里不设置id，不需要关联ma_type_properties表的id
                        devInfoPropertyVos.add(entity);
                        log.info("第{}行，特征项{}有效，已添加到列表", i + 1, j + 1);
                    }
                }

                log.info("第{}行，共提取到{}个有效特征项", i + 1, devInfoPropertyVos.size());

                // 如果有特征项数据，则保存
                if (!devInfoPropertyVos.isEmpty()) {
                    log.info("开始保存特征项：maId={}, 数量={}", maDevInfo.getMaId(), devInfoPropertyVos.size());
                    devInfoMapper.insertDevInfoProperties(Long.valueOf(maDevInfo.getMaId()), devInfoPropertyVos);
                    log.info("特征项保存成功");
                } else {
                    log.info("第{}行没有有效的特征项数据，跳过保存", i + 1);
                }

                if (insertResult > 0) {
                    MaDevQc maDevQc = new MaDevQc();
                    maDevQc.setMaId(maDevInfo.getMaId());
                    maDevQc.setQcCode(maDevInfo.getCode());
                    maDevQc.setCreateBy(String.valueOf(SecurityUtils.getUserId()));
                    maDevQc.setCreateTime(DateUtils.getNowDate());
                    maDevQc.setQcCom(Optional.ofNullable(SecurityUtils.getLoginUser().getSysUser().getCompanyId()).orElse(SecurityUtils.getLoginUser().getSysUser().getDeptId()).toString());
                    maDevQc.setNextCheckTime(maDevInfo.getNextMaintenanceDate());
                    qcMapper.insertDevQc(maDevQc);

                    // 关联订单（仅当orderId有效时）
                    if (orderId != null && !orderId.trim().isEmpty() && !"undefined".equalsIgnoreCase(orderId.trim())
                            && !"null".equalsIgnoreCase(orderId.trim())) {
                        try {
                            devMergeMapper.insertOrderDevReal(orderId, Long.valueOf(maDevInfo.getMaId()));
                        } catch (NumberFormatException e) {
                            log.warn("订单ID格式无效，跳过关联订单：orderId={}", orderId);
                        }
                    }
                }
            } catch (Exception e) {
                log.error("导入第{}行数据失败：{}", i + 1, e.getMessage(), e);
                errorMessages.add("第" + (i + 1) + "行导入失败：系统错误");
            }
        }

        // 6. 返回最终结果
        if (!errorMessages.isEmpty()) {
            return AjaxResult.error("导入完成，但存在以下错误：" + String.join(" ", errorMessages));
        }
        return AjaxResult.success("导入完成", orderId);
    }

    /**
     * @return
     */
    @Override
    public List<String> listAllProfessionNames() {
        return devMergeMapper.listAllProfessionNames();
    }

    /**
     * @return
     */
    @Override
    public List<String> listAllManufacturerNames() {
        return devMergeMapper.listAllManufacturerNames();
    }

    /**
     * @param response
     */
    @Override
    public void downloadEmptyFolderZip(HttpServletResponse response, String orderId) {
        // 1. 从数据库查询一级文件夹名称列表
        List<String> firstLevelFolders = devMergeMapper.getFileName(orderId);
        if (firstLevelFolders == null || firstLevelFolders.isEmpty()) {
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            return; // 无文件夹时返回400
        }
        // 2. 定义一级文件夹下的固定子文件夹名称
        List<String> subFolders = Arrays.asList(
                "装备外观/",
                "合格证/",
                "定期检验报告/",
                "采购发票/"
        );
        // 3. 构建所有需要生成的文件夹路径
        List<String> allFolderPaths = new ArrayList<>();
        for (String firstFolder : firstLevelFolders) {
            // 添加一级文件夹
            allFolderPaths.add(firstFolder + "/");
            // 添加一级文件夹下的所有子文件夹
            for (String subFolder : subFolders) {
                allFolderPaths.add(firstFolder + "/" + subFolder);
            }
        }
        // 4. 生成 ZIP 包并下载（文件名为「订单+文件夹模板.zip」）
        FolderZipUtil.buildEmptyFolderZip(response, orderId + "-文件夹模板.zip", allFolderPaths);
    }

    private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyMMdd");
    private static final String SEPARATOR = "-";

    private static final int SEQUENCE_LENGTH = 4;

    /**
     * 生成带前缀的单号
     * 格式：251001-0001
     */
    public String generate() {
        // 获取今天的日期部分
        String datePart = LocalDate.now().format(DATE_FORMATTER);

        // 查询今天已生成的单号数量
        int todayCount = devMergeMapper.selectTodayOrderCount();

        // 下一个序列号 = 今天数量 + 1
        int nextSequence = todayCount + 1;

        // 格式化序列号
        String sequencePart = formatSequence(nextSequence);

        // 构建完整单号

        return datePart + SEPARATOR + sequencePart;
    }


    /**
     * 格式化序列号
     */
    private String formatSequence(int sequence) {
        if (sequence < 1 || sequence > 9999) {
            throw new IllegalArgumentException("序列号必须在 1-9999 范围内");
        }
        return String.format("%0" + SEQUENCE_LENGTH + "d", sequence);
    }


    // 固定子目录
    private static final Set<String> REQUIRED_SUB_FOLDERS = new HashSet<>(Arrays.asList(
            "装备外观", "合格证", "定期检验报告", "采购发票"
    ));
    // 允许的文件类型
    private static final Set<String> IMAGE_TYPES = new HashSet<>(Arrays.asList("jpg", "png"));
    private static final Set<String> PDF_TYPE = new HashSet<>(Collections.singletonList("pdf"));
    // 一级目录前缀
    private static final String ROOT_FOLDER_PREFIX = "Apply-";
    // 系统临时目录
    private static final String SYSTEM_TEMP_DIR = System.getProperty("java.io.tmpdir");

    /**
     * 上传并解压多一级目录的ZIP包（适配任意层级的Apply-目录）
     */
    @Override
    public AjaxResult uploadAndUnzipMultiDeviceZip(MultipartFile zipFile, String orderId) {
        Map<String, Object> map = devMergeMapper.getFileId(orderId);
        if (ObjectUtil.isEmpty(map)) {
            if (zipFile.isEmpty()) {
                return AjaxResult.error(400, "无设备可修改");
            }
        }
        Map<String, Object> result = new HashMap<>();
        int totalRootFolder = 0;
        int successFileCount = 0;
        List<String> errorMsgList = new ArrayList<>();
        Map<String, List<String>> rootFolderErrorMap = new HashMap<>();
        // 1. 基础校验
        if (zipFile.isEmpty()) {
            return AjaxResult.error(400, "上传的ZIP包不能为空");
        }

        String zipFileName = zipFile.getOriginalFilename();
        if (zipFileName == null || !zipFileName.toLowerCase().endsWith(".zip")) {
            return AjaxResult.error(400, "仅支持上传ZIP格式文件");
        }

        // 使用更安全的临时目录名
        String timestamp = String.valueOf(System.nanoTime());
        String random = UUID.randomUUID().toString().substring(0, 8);
        String tempSubDirName = "device_zip_" + timestamp + "_" + random;

        File tempUnzipDir = new File(SYSTEM_TEMP_DIR, tempSubDirName);

        try {
            // 2. 创建临时目录
            FileUtil.mkdir(tempUnzipDir);

            // 3. 验证ZIP文件完整性并解压
            boolean unzipSuccess = safeUnzip(zipFile, tempUnzipDir, errorMsgList);
            if (!unzipSuccess) {
                // 解压失败，但可能部分文件解压成功，可以选择继续处理或直接返回
                if (errorMsgList.stream().anyMatch(msg -> msg.contains("损坏") || msg.contains("无效") || msg.contains("MALFORMED"))) {
                    return AjaxResult.error(400, "ZIP文件损坏或格式错误：" + String.join("; ", errorMsgList));
                }
            }

            // 4. 核心：递归遍历所有层级，找出所有Apply-开头的目录
            Set<File> applyFolders = new HashSet<>();
            findAllApplyFolders(tempUnzipDir, applyFolders);
            Integer num = 0;
            // 5. 遍历所有Apply-目录并处理
            for (File applyFolder : applyFolders) {
                String applyFolderName = applyFolder.getName();
                totalRootFolder++;
                List<String> rootErrorList = new ArrayList<>();

                try {
                    // 6. 校验子目录结构（装备外观/合格证等）
                    validateSubFolderStructure(applyFolder, rootErrorList);

                    // 7. 遍历子目录文件并处理
                    for (String subFolder : REQUIRED_SUB_FOLDERS) {
                        File subDir = new File(applyFolder, subFolder);
                        if (FileUtil.exist(subDir)) {
                            Collection<File> files = FileUtil.loopFiles(subDir);
                            for (File file : files) {
                                try {
                                    validateFileType(subFolder, file, rootErrorList);
                                    // 移动文件到业务存储目录
                                    AjaxResult fileResult = remoteFileService.upload(convert(file, file.getName(), null));
                                    JSONObject json = (JSONObject) JSON.toJSON(fileResult);
                                    if (json.getInteger("code") == 200) {
                                        JSONObject jsonObject = (JSONObject) JSON.toJSON(map);
                                        if (jsonObject != null) {

                                            MaDevFile item = new MaDevFile();
                                            if ("装备外观".equals(subFolder)) {
                                                item.setFileType(1);
                                            } else if ("合格证".equals(subFolder)) {
                                                item.setFileType(2);
                                            } else if ("定期检验报告".equals(subFolder)) {
                                                item.setFileType(3);
                                            } else if ("采购发票".equals(subFolder)) {
                                                item.setFileType(4);
                                            }
                                            item.setMaId(jsonObject.getJSONObject(applyFolderName).getInteger("id"));
                                            JSONObject data = json.getJSONObject("data");
                                            String url = data.getString("url");
                                            item.setFileName(file.getName());
                                            // 这里编写对每个 image 的处理逻辑，比如打印、处理等
                                            item.setFileUrl(url);
                                            item.setCreator(Math.toIntExact(SecurityUtils.getLoginUser().getUserid()));
                                            num += devMergeMapper.interFile(item);
                                        }

                                    }
                                    successFileCount++;
                                } catch (IllegalArgumentException e) {
                                    rootErrorList.add(e.getMessage());
                                }
                            }
                        } else {
                            rootErrorList.add("缺失子目录：" + applyFolderName + "/" + subFolder);
                        }
                    }

                    if (!rootErrorList.isEmpty()) {
                        rootFolderErrorMap.put(applyFolderName, rootErrorList);
                    }
                } catch (Exception e) {
                    rootErrorList.add("Apply目录处理失败：" + e.getMessage());
                    rootFolderErrorMap.put(applyFolderName, rootErrorList);
                }
            }
            // 8. 组装结果
            if (totalRootFolder == 0) {
                return AjaxResult.error(400, "未找到有效的Apply-开头的目录");
            }

            if (num == 0) {
                return AjaxResult.error(400, "无数据导入，请检查模板");
            }
            result.put("code", 200);
            result.put("msg", "ZIP包解压完成（识别到" + totalRootFolder + "个Apply-目录）");
            result.put("totalRootFolder", totalRootFolder);
            result.put("successFileCount", successFileCount);
            result.put("rootFolderErrorMap", rootFolderErrorMap);
            result.put("otherError", errorMsgList);

            return AjaxResult.success(result);

        } catch (IllegalArgumentException e) {
            return AjaxResult.error(400, e.getMessage());
        } catch (Exception e) {
            log.error("ZIP解压/上传失败", e);
            return AjaxResult.error(500, "ZIP解压/上传失败：" + e.getMessage());
        } finally {
            // 9. 清理临时目录
            try {
                if (tempUnzipDir.exists()) {
                    FileUtil.del(tempUnzipDir);
                    log.debug("清理临时目录: {}", tempUnzipDir.getAbsolutePath());
                }
            } catch (Exception e) {
                log.warn("清理临时目录失败: {}", e.getMessage());
            }
        }
    }

    /**
     * 从File对象中获取Apply-目录的纯名称（核心方法）
     *
     * @param applyFolder Apply-开头的目录File对象
     * @return 文件夹名称（如Apply-22099-13-123）
     */
    private String getApplyFolderName(File applyFolder) {
        // 方式1：直接获取文件名（推荐，最简洁）
        String folderName = applyFolder.getName();

        // 可选：额外校验（确保是Apply-开头，避免异常）
        if (!folderName.startsWith(ROOT_FOLDER_PREFIX)) {
            throw new IllegalArgumentException("非合法的Apply-目录：" + folderName);
        }

        return folderName;
    }

    /**
     * 将 File 转换为 MultipartFile
     *
     * @param file             要转换的文件
     * @param originalFilename 原始文件名（可空，默认为file.getName()）
     * @param contentType      内容类型（可空，默认为null）
     * @return MultipartFile 对象
     * @throws IOException 文件读取异常
     */
    public static MultipartFile convert(File file, String originalFilename, String contentType) throws IOException {
        if (originalFilename == null) {
            originalFilename = file.getName();
        }

        try (FileInputStream input = new FileInputStream(file)) {
            return new MockMultipartFile(
                    "file",                  // form field name
                    originalFilename,        // original file name
                    contentType,            // content type
                    input                   // file content
            );
        }
    }

    /**
     * 简化的转换方法
     */
    public static MultipartFile convert(File file) throws IOException {
        return convert(file, file.getName(), null);
    }

    /**
     * 安全的ZIP解压方法
     */
    private boolean safeUnzip(MultipartFile zipFile, File destDir, List<String> errorMsgList) {
        long startTime = System.currentTimeMillis();
        int extractedCount = 0;

        // 先验证ZIP文件头
        try (InputStream inputStream = zipFile.getInputStream()) {
            byte[] header = new byte[4];
            if (inputStream.read(header) != 4 ||
                    !(header[0] == 0x50 && header[1] == 0x4B &&
                            header[2] == 0x03 && header[3] == 0x04)) {
                errorMsgList.add("无效的ZIP文件格式");
                return false;
            }
        } catch (IOException e) {
            errorMsgList.add("读取ZIP文件失败: " + e.getMessage());
            return false;
        }

        // 尝试多种编码方式解压
        Charset[] charsets = {
                StandardCharsets.UTF_8,
                Charset.forName("GBK"),
                Charset.forName("GB2312"),
                Charset.forName("ISO-8859-1"),
                StandardCharsets.US_ASCII
        };

        for (Charset charset : charsets) {
            try {
                log.debug("尝试使用编码 {} 解压ZIP文件", charset.name());
                if (unzipWithCharset(zipFile, destDir, charset)) {
                    extractedCount = countFiles(destDir);
                    log.info("使用编码 {} 成功解压 {} 个文件", charset.name(), extractedCount);
                    return extractedCount > 0;
                }
            } catch (Exception e) {
                log.debug("编码 {} 解压失败: {}", charset.name(), e.getMessage());
            }
        }

        errorMsgList.add("无法解压ZIP文件，尝试所有编码都失败");
        return false;
    }

    /**
     * 使用指定编码解压ZIP文件
     */
    private boolean unzipWithCharset(MultipartFile zipFile, File destDir, Charset charset) throws IOException {
        try (InputStream inputStream = zipFile.getInputStream();
             ZipInputStream zipIn = new ZipInputStream(inputStream, charset)) {

            ZipEntry entry;
            byte[] buffer = new byte[1024 * 1024]; // 1MB buffer
            int fileCount = 0;

            while ((entry = zipIn.getNextEntry()) != null) {
                fileCount++;
                String entryName = entry.getName();

                // 安全检查：防止目录遍历攻击
                File entryFile = new File(destDir, entryName);
                String canonicalDestPath = destDir.getCanonicalPath();
                String canonicalEntryPath = entryFile.getCanonicalPath();

                if (!canonicalEntryPath.startsWith(canonicalDestPath + File.separator)) {
                    throw new SecurityException("尝试访问外部目录: " + entryName);
                }

                if (entry.isDirectory()) {
                    if (!entryFile.exists() && !entryFile.mkdirs()) {
                        throw new IOException("创建目录失败: " + entryFile.getAbsolutePath());
                    }
                } else {
                    File parentDir = entryFile.getParentFile();
                    if (!parentDir.exists() && !parentDir.mkdirs()) {
                        throw new IOException("创建父目录失败: " + parentDir.getAbsolutePath());
                    }

                    try (OutputStream out = new FileOutputStream(entryFile)) {
                        int len;
                        long totalBytes = 0;
                        while ((len = zipIn.read(buffer)) > 0) {
                            out.write(buffer, 0, len);
                            totalBytes += len;

                            // 可选：限制单个文件大小
                            if (totalBytes > 100 * 1024 * 1024) { // 100MB
                                throw new IOException("文件过大: " + entryName);
                            }
                        }
                    }
                }
                zipIn.closeEntry();

                // 限制解压的文件数量
                if (fileCount > 10000) {
                    throw new IOException("ZIP包内文件数量过多，可能为恶意压缩包");
                }
            }

            return fileCount > 0;
        } catch (ZipException e) {
            throw new IOException("ZIP文件格式错误: " + e.getMessage(), e);
        }
    }

    /**
     * 计算目录下的文件数量
     */
    private int countFiles(File dir) {
        if (!dir.exists() || !dir.isDirectory()) {
            return 0;
        }

        int count = 0;
        Queue<File> queue = new LinkedList<>();
        queue.add(dir);

        while (!queue.isEmpty()) {
            File current = queue.poll();
            File[] files = current.listFiles();
            if (files != null) {
                for (File file : files) {
                    if (file.isDirectory()) {
                        queue.add(file);
                    } else {
                        count++;
                    }
                }
            }
        }

        return count;
    }

    /**
     * 改进的findAllApplyFolders方法，避免无限递归
     */
    private void findAllApplyFolders(File rootDir, Set<File> applyFolders) {
        if (rootDir == null || !rootDir.exists() || !rootDir.isDirectory()) {
            return;
        }

        // 防止无限递归（例如符号链接）
        try {
            String canonicalPath = rootDir.getCanonicalPath();
            if (visitedPaths.contains(canonicalPath)) {
                return;
            }
            visitedPaths.add(canonicalPath);
        } catch (IOException e) {
            return;
        }

        // 深度限制
        if (depth > 50) {
            return;
        }
        depth++;

        try {
            File[] files = rootDir.listFiles();
            if (files != null) {
                for (File file : files) {
                    if (file.isDirectory()) {
                        String name = file.getName();
                        if (name.startsWith("Apply-") || name.startsWith("apply-")) {
                            applyFolders.add(file);
                        } else {
                            // 递归查找子目录
                            findAllApplyFolders(file, applyFolders);
                        }
                    }
                }
            }
        } finally {
            depth--;
        }
    }

    // 添加成员变量用于防止无限递归
    private Set<String> visitedPaths = new HashSet<>();
    private int depth = 0;


    // 以下方法与之前一致，无需修改
    private void validateSubFolderStructure(File rootFolder, List<String> errorList) {
        Collection<File> subDirs = Arrays.asList(FileUtil.ls(rootFolder.getPath()));
        Set<String> actualSubFolders = new HashSet<>();
        for (File subDir : subDirs) {
            if (subDir.isDirectory()) {
                actualSubFolders.add(subDir.getName());
            }
        }
        Set<String> missingFolders = new HashSet<>(REQUIRED_SUB_FOLDERS);
        missingFolders.removeAll(actualSubFolders);
        if (!missingFolders.isEmpty()) {
            errorList.add("缺失必填子目录：" + String.join(",", missingFolders));
        }
    }

    private void validateFileType(String subFolder, File file, List<String> errorList) {
        if (file.isDirectory()) {
            return;
        }
        String fileExt = FileUtil.extName(file).toLowerCase();
        if (fileExt.isEmpty()) {
            throw new IllegalArgumentException(file.getParentFile().getName() + "/" + file.getName() + "：无后缀文件不允许上传");
        }
        switch (subFolder) {
            case "装备外观":
                if (!IMAGE_TYPES.contains(fileExt)) {
                    throw new IllegalArgumentException(
                            subFolder + "/" + file.getName() + "：仅支持图片（" + String.join(",", IMAGE_TYPES) + "）");
                }
                break;
            case "合格证":
            case "定期检验报告":
            case "采购发票":
                if (!IMAGE_TYPES.contains(fileExt) && !PDF_TYPE.contains(fileExt)) {
                    throw new IllegalArgumentException(
                            subFolder + "/" + file.getName() + "：仅支持图片+PDF（" + String.join(",", IMAGE_TYPES) + ",pdf）");
                }
                break;
            default:
                throw new IllegalArgumentException("不支持的子目录：" + subFolder);
        }
    }

    @Override
    public Map<String, List<EquipmentProperty>> getAllTypePropertiesMap() {
        // 1. 查询所有装备类型名称
        List<String> professionList = devMergeMapper.listAllProfessionNames();

        // 2. 为每个装备类型查询其特征项
        Map<String, List<EquipmentProperty>> typePropertiesMap = new LinkedHashMap<>();

        for (String profession : professionList) {
            // 根据装备类型名称获取typeId
            Integer typeId = devMergeMapper.getTypeId(profession);

            if (typeId != null) {
                // 查询该类型的特征项
                List<EquipmentProperty> properties = equipmentPropertyMapper.selectByTypeId(typeId.longValue());
                typePropertiesMap.put(profession, properties != null ? properties : new ArrayList<>());
            } else {
                // 如果没有typeId，放入空列表
                typePropertiesMap.put(profession, new ArrayList<>());
            }
        }

        return typePropertiesMap;
    }

    private boolean isCoreFieldHasValue(EquipmentImportDTO item) {
        return StringUtils.hasText(item.getEquipmentName())
                || StringUtils.hasText(item.getSpecification())
                || item.getOriginalValue() != null
                || StringUtils.hasText(item.getManufacturer())
                || item.getProductionDate() != null
                || item.getNextMaintenanceDate() != null
                || StringUtils.hasText(item.getOriginalCode())
                || item.getMaxServiceYears() != null
                || StringUtils.hasText(item.getUnit())
                || item.getPurchaseDate() != null;
    }


}
