package com.yeejoin.amos.api.openapi.service;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.read.metadata.ReadSheet;
import com.alibaba.excel.read.metadata.holder.ReadRowHolder;
import com.alibaba.fastjson.JSON;
import com.yeejoin.amos.api.common.restful.utils.ResponseHelper;
import com.yeejoin.amos.api.openapi.dto.XiAnEquipInfoExcelDto;
import com.yeejoin.amos.api.openapi.feign.TzsJgServiceFeignClient;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import org.typroject.tyboot.core.foundation.context.RequestContext;
import org.typroject.tyboot.core.foundation.utils.ValidationUtil;
import org.typroject.tyboot.core.restful.exception.instance.BadRequest;

import java.io.InputStream;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@Slf4j
@Service
public class XiAnDataDockServiceImpl {

    private final TzsJgServiceFeignClient jgServiceFeignClient;

    public XiAnDataDockServiceImpl(TzsJgServiceFeignClient jgServiceFeignClient) {
        this.jgServiceFeignClient = jgServiceFeignClient;
    }

    // 自定义线程池
    private final ExecutorService executorService = Executors.newFixedThreadPool(10);


    private final String excelErrorStr = "Excel 第[%s]行";

    private final Map<String, Object> resultError = new HashMap<>();
    List<String> useInnerCodeList = new ArrayList<>();// 单位内部编号集合
    List<String> equCodeList = new ArrayList<>();// 设备代码集合
    List<String> factoryNumList = new ArrayList<>();// 出厂编码集合


    /**
     * 西安设备数据导入
     *
     * @param file excel 文件
     * @return 导入结果
     * @throws Exception 异常
     */
    public Object importEquipmentData(MultipartFile file) {
        List<XiAnEquipInfoExcelDto> equipInfoExcelDtos;
        try {
            // 1, 数据读取 + 格式、类型异常检查
            equipInfoExcelDtos = this.dataHandler(file);
        } catch (Exception e) {
            return ResponseHelper.buildFailureResponse(e.getMessage(), "参数校验失败，详细请看返回信息", HttpStatus.BAD_REQUEST);
        }

        // 2, 分批保存设备数据
        this.batchSaveEquipmentData(equipInfoExcelDtos);
        return ResponseHelper.buildResponse("设备保存成功！");
    }

    /**
     * 批量异步保存设备数据
     *
     * @param equipInfoExcelDtos
     */
    public void batchSaveEquipmentData(List<XiAnEquipInfoExcelDto> equipInfoExcelDtos) {
        int batchSize = 1000;
        int totalSize = equipInfoExcelDtos.size();
        // 主线程中获取登录信息传递到异步线程中
        String appKey = RequestContext.getAppKey();
        String product = RequestContext.getProduct();
        String token = RequestContext.getToken();

        List<CompletableFuture<Void>> futures = new ArrayList<>();
        for (int i = 0; i < totalSize; i += batchSize) {
            List<XiAnEquipInfoExcelDto> batch = equipInfoExcelDtos.subList(i, Math.min(totalSize, i + batchSize));
            CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                try {
                    RequestContext.setAppKey(appKey);
                    RequestContext.setProduct(product);
                    RequestContext.setToken(token);
                    jgServiceFeignClient.saveEquipmentData(batch);
                } catch (Exception e) {
                    log.error("西安数据上传，保存设备数据失败: ", e);
                }
            }, executorService);
            futures.add(future);
        }
        // 等待所有异步任务完成
        CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
    }


    /**
     * excel 文件读取，字段校验
     *
     * @param file excel 文件
     * @return 设备数据
     * @throws Exception 异常信息
     */
    public List<XiAnEquipInfoExcelDto> dataHandler(MultipartFile file) {
        List<XiAnEquipInfoExcelDto> dataList = new ArrayList<>();
        resultError.clear();
        useInnerCodeList.clear();
        equCodeList.clear();
        factoryNumList.clear();

        try {
            InputStream inputStream = file.getInputStream();
            ExcelReader excelReader = EasyExcel.read(inputStream).build();
            List<ReadSheet> sheetList = excelReader.excelExecutor().sheetList(); // 获取所有 sheet

            for (ReadSheet readSheet : sheetList) {
                String sheetName = readSheet.getSheetName();
                int sheetNo = readSheet.getSheetNo();

                // 忽略填充设备种类类别品种的三级联动sheet页
                if (sheetName.contains("忽略")) {
                    continue;
                }
                Map<String, Object> sheetError = new HashMap<>();
                EasyExcel.read(file.getInputStream(), XiAnEquipInfoExcelDto.class, new AnalysisEventListener<XiAnEquipInfoExcelDto>() {
                    // 每读一行都会执行
                    @Override
                    public void invoke(XiAnEquipInfoExcelDto data, AnalysisContext context) {
                        // 数据检查
                        checkExcelData(sheetName, data, context, sheetError);
                        useInnerCodeList.add(data.getUseInnerCode());
                        equCodeList.add(data.getEquCode());
                        dataList.add(data);
                    }

                    // 每个sheet页读完都会执行
                    @Override
                    public void doAfterAllAnalysed(AnalysisContext context) {
                        // 记录每一个sheet页的错误集
                        if (!ValidationUtil.isEmpty(sheetError)) {
                            resultError.put(String.format("%s sheet页", sheetName), sheetError);
                        }
                    }
                }).headRowNumber(4).sheet(sheetNo, sheetName).doRead();
            }

            excelReader.finish(); // 关闭 reader，释放资源

            if (CollectionUtils.isEmpty(dataList)) {
                resultError.put("allSheet", "你上传了一个空数据的Excel文档!");
                throw new BadRequest("你上传了一个空数据的Excel文档!");
            }

            // 检查 resultError中的每一项，若存在内容，则有错误信息，直接抛异常
            if (!ValidationUtil.isEmpty(resultError)) {
                throw new BadRequest(JSON.toJSONString(resultError));
            }

            return dataList;
        } catch (Exception e) {
            throw new BadRequest(e.getMessage());
        }
    }

    /**
     * excel数据字段校验
     *
     * @param sheetName sheet页名称
     * @param data      excel数据
     * @param context
     */
    public void checkExcelData(String sheetName, XiAnEquipInfoExcelDto data, AnalysisContext context, Map<String, Object> sheetError) {
        ReadRowHolder readRowHolder = context.readRowHolder();
        int rowIndex = readRowHolder.getRowIndex() + 1;
        StringBuilder rowError = new StringBuilder();

        // 是否起重机械
        boolean isQZJX = !ValidationUtil.isEmpty(data.getEquList()) && "4000".equals(data.getEquList());
        // 是否场内机动车辆
        boolean isCNJDXL = !ValidationUtil.isEmpty(data.getEquList()) && "5000".equals(data.getEquList());
        // 是否锅炉
        boolean isGL = !ValidationUtil.isEmpty(data.getEquList()) && "1000".equals(data.getEquList());
        // 是否压力容器
        boolean isYLRQ = !ValidationUtil.isEmpty(data.getEquList()) && "2000".equals(data.getEquList());
        // 是否压力管道
        boolean isYLGD = !ValidationUtil.isEmpty(data.getEquList()) && "8000".equals(data.getEquList());
        // 是否大型游乐设施
        boolean isDXYNSS = !ValidationUtil.isEmpty(data.getEquList()) && "6000".equals(data.getEquList());
        // 是否客运索道
        boolean isKYSD = !ValidationUtil.isEmpty(data.getEquList()) && "9000".equals(data.getEquList());

        try {
            log.info("解析第{}行数据：{}", rowIndex, JSON.toJSONString(data));

            // 通用字段检查 每一个sheet页都有的基本信息，设计信息，制造信息和使用信息
            this.commonFieldCheck(data, rowError);

            // 起重机械----技术参数 检查
            if (isQZJX) {
                this.QZJXTechnicalParamsCheck(data, rowError);
            }
            // 场内机动车辆----技术参数 检查
            if (isCNJDXL) {
                this.CNJDCLTechnicalParamsCheck(data, rowError);
            }
            // 锅炉----技术参数 检查
            if (isGL) {
                this.GLTechnicalParamsCheck(data, rowError);
            }
            // 压力容器----技术参数 检查
            if (isYLRQ) {
                this.YLRQTechnicalParamsCheck(data, rowError);
            }
            // 压力管道----技术参数 检查
            if (isYLGD) {
                this.YLGDTechnicalParamsCheck(data, rowError);
            }
            // 大型游乐设施----技术参数 检查
            if (isDXYNSS) {
                this.DXYLSSTechnicalParamsCheck(data, rowError);
            }
            // 客运索道----技术参数 检查
            if (isKYSD) {
                this.KYSDTechnicalParamsCheck(data, rowError);
            }

            if (!StringUtils.isBlank(rowError)) {
                sheetError.put(String.format(excelErrorStr, rowIndex), rowError);
            }

        } catch (Exception e) {
            log.error(String.format("sheet页：[%s] -> 对应行索引数: [%s] -> 失败的 Excel 数据: [%s]", sheetName, rowIndex, JSON.toJSONString(data)), e);
            throw e;
        }
    }

    /**
     * 检查字段是否为空，如果为空则追加错误信息到result
     *
     * @param value        待检查字段
     * @param errorMessage 错误信息
     * @param rowError     结果集
     */
    private void checkNotBlank(String value, String errorMessage, StringBuilder rowError) {
        if (StringUtils.isBlank(value)) {
            rowError.append(errorMessage);
        }
    }

    /**
     * 通用字段检查 每一个sheet页都有的基本信息，设计信息，制造信息和使用信息
     *
     * @param data     源数据
     * @param rowError 错误集合
     */
    private void commonFieldCheck(XiAnEquipInfoExcelDto data, StringBuilder rowError) {

        boolean isPressureVessel = !ObjectUtils.isEmpty(data.getEquList()) && "2000".equals(data.getEquList());// 是否压力容器
        boolean isPressurePiping = !ObjectUtils.isEmpty(data.getEquList()) && "8000".equals(data.getEquList());// 是否压力管道

        // 基本信息
        checkNotBlank(data.getEquList(), "设备种类不能为空；", rowError);
        checkNotBlank(data.getEquCategory(), "设备类别不能为空；", rowError);
        checkNotBlank(data.getUseInnerCode(), "单位内编号不能为空；", rowError);
        if (useInnerCodeList.contains(data.getUseInnerCode())) {
            rowError.append("单位内编号不能重复；");
        }
        checkNotBlank(data.getProductName(), "产品名称不能为空；", rowError);
        checkNotBlank(data.getEquType(), "设备型号不能为空；", rowError);
        checkNotBlank(data.getEquCodeType(), "有无设备代码不能为空；", rowError);
        if ("1".equals(data.getEquCodeType())) {
            checkNotBlank(data.getEquCode(), "设备代码不能为空；", rowError);
            String equCode = data.getEquCode();
            if (equCode.matches("[a-zA-Z0-9]+")) {
                if (equCode.length() <= 17) {
                    rowError.append("设备代码不能小于17位；");
                }
                if (equCode.length() >= 20) {
                    rowError.append("设备代码不能大于20位；");
                }
            } else {
                rowError.append("设备代码不能包含特殊字符；");
            }
            if (equCodeList.contains(data.getEquCode())) {
                rowError.append("设备代码不能重复；");
            }
            this.checkEquCodeUniqueness(data.getEquCode(), rowError);
        } else {
            data.setEquCode("");
        }
        if (isPressurePiping) {// 校验压力管道特有的参数
            checkNotBlank(data.getProjectContraption(), "工程（装置）名称不能为空；", rowError);
        } else {
            data.setProjectContraption("");
        }
        if (isPressureVessel) {// 校验压力容器特有的参数
            boolean isGasCylinder = !ObjectUtils.isEmpty(data.getEquCategory()) && "2300".equals(data.getEquCategory());// 是否气瓶
            boolean isFixedGasCylinder = !ObjectUtils.isEmpty(data.getEquCategory()) && "2100".equals(data.getEquCategory());//是否固定式压力容器
            boolean isOxygenChamber = !ObjectUtils.isEmpty(data.getEquCategory()) && "2400".equals(data.getEquCategory());//是否氧舱
            boolean isMobilePressureVessel = !ObjectUtils.isEmpty(data.getEquCategory()) && "2200".equals(data.getEquCategory());//是否移动式压力容器
            boolean isSpecialGasCylinder = isGasCylinder && !ObjectUtils.isEmpty(data.getEquDefine()) && "23T0".equals(data.getEquDefine());// 是否特种气瓶
            boolean isCarGasCylinder = isSpecialGasCylinder && (!ObjectUtils.isEmpty(data.getWhetherVehicleCylinder()) && "1".equals(data.getWhetherVehicleCylinder()));// 是否车用气瓶
            if (isGasCylinder) { // 是气瓶
                if (!isSpecialGasCylinder) { // 不是特种气瓶
                    data.setWhetherVehicleCylinder("");
                    checkNotBlank(data.getInformationSituation(), "信息化管理情况不能为空；", rowError);
                    if (!ObjectUtils.isEmpty(data.getInformationSituation())) {
                        checkNotBlank(data.getInformationManageCode(), "二维码或者电子标签编号不能为空；", rowError);
                    }
                }
                if (isSpecialGasCylinder) { // 是特种气瓶
                    checkNotBlank(data.getWhetherVehicleCylinder(), "是否车用气瓶不能为空；", rowError);
                    if (!isCarGasCylinder) {// 不是车用气瓶
                        checkNotBlank(data.getInformationSituation(), "信息化管理情况不能为空；", rowError);
                        if (!ObjectUtils.isEmpty(data.getInformationSituation())) {
                            checkNotBlank(data.getInformationManageCode(), "二维码或者电子标签编号不能为空；", rowError);
                        }
                    }
                    if (isCarGasCylinder) { // 是车用气瓶
                        data.setInformationSituation("");
                        data.setInformationManageCode("");
                        checkFactoryNumUniqueness(data, rowError);// 校验出厂编号/产品编码唯一性
                    }
                }
                data.setWhetherSphericalTank("");
                data.setWhetherSkidMountedPressureVessel("");
            }
            if (isFixedGasCylinder) { // 是固定式压力容器
                checkNotBlank(data.getWhetherSphericalTank(), "是否球罐不能为空；", rowError);
                checkNotBlank(data.getWhetherSkidMountedPressureVessel(), "是否撬装式压力容器不能为空；", rowError);
                data.setInformationSituation("");
                data.setInformationManageCode("");
            }
            if (isMobilePressureVessel) {// 是移动式压力容器
                data.setWhetherSkidMountedPressureVessel("");
                data.setWhetherVehicleCylinder("");
                data.setWhetherSphericalTank("");
                data.setInformationSituation("");
                data.setInformationManageCode("");
            }
            if (isOxygenChamber) {//是氧舱
                data.setWhetherSkidMountedPressureVessel("");
                data.setWhetherVehicleCylinder("");
                data.setWhetherSphericalTank("");
                data.setInformationSituation("");
                data.setInformationManageCode("");
            }
        }
        // 使用信息
        checkNotBlank(data.getUseUnitCode(), "使用单位统一社会信用代码不能为空；", rowError);
        Optional.ofNullable(data.getUseUnitCode()).ifPresent(v -> checkCreditCode(v, "使用单位统一社会信用代码格式不正确；", rowError));
        checkNotBlank(data.getUseUnit(), "使用单位名称不能为空；", rowError);
        // 设计信息
        checkNotBlank(data.getDesignUnitCreditCode(), "设计单位统一社会信用代码不能为空；", rowError);
        Optional.ofNullable(data.getDesignUnitCreditCode()).ifPresent(v -> checkCreditCode(v, "设计单位统一社会信用代码格式不正确；", rowError));
        checkNotBlank(data.getDesignUnitName(), "设计单位名称不能为空；", rowError);
        checkNotBlank(data.getDesignUseDate(), "设计使用年限不能为空；", rowError);
        Optional.ofNullable(data.getDesignUseDate()).ifPresent(v -> checkDateFormatNumber(v, "设计使用年限不能为数字以外的其他类型；", rowError));
        checkNotBlank(data.getDesignUseDate(), "设计日期不能为空；", rowError);
        Optional.ofNullable(data.getDesignDate()).ifPresent(v -> checkDateFormatCorrect(v, "设计日期格式不正确；", rowError));
        Optional.ofNullable(data.getAppraisalDate()).ifPresent(v -> checkDateFormatCorrect(v, "设计文件鉴定日期格式不正确；", rowError));
        // 制造信息
        checkNotBlank(data.getProduceUnitCreditCode(), "制造单位统一社会信用代码不能为空；", rowError);
        Optional.ofNullable(data.getProduceUnitCreditCode()).ifPresent(v -> checkCreditCode(v, "制造单位统一社会信用代码格式不正确；", rowError));
        checkNotBlank(data.getProduceUnitName(), "制造单位名称不能为空；", rowError);
        checkNotBlank(data.getProduceLicenseNum(), "制造许可编号不能为空；", rowError);
        checkNotBlank(data.getFactoryNum(), "出厂编号/产品编码不能为空；", rowError);
        checkNotBlank(data.getProduceDate(), "制造日期不能为空；", rowError);
        Optional.ofNullable(data.getProduceDate()).ifPresent(v -> checkDateFormatCorrect(v, "制造日期格式不正确；", rowError));
    }

    /**
     * 检查上传Excel中的日期格式是否正确
     *
     * @param date         待检查数据
     * @param errorMessage 错误内容
     * @param rowError     错误集
     */
    private void checkDateFormatCorrect(String date, String errorMessage, StringBuilder rowError) {
        if (!date.matches("\\d{4}-\\d{2}-\\d{2}")) {
            rowError.append(errorMessage);
        }
    }

    /**
     * 检查上传Excel中的统一信用代码格式
     *
     * @param code         待检查数据
     * @param errorMessage 错误内容
     * @param rowError     错误集
     */
    private void checkCreditCode(String code, String errorMessage, StringBuilder rowError) {
        String regex = "^[A-Z0-9]{15}(?:[A-Z0-9]{3})?$";
        if (!code.matches(regex)) {
            rowError.append(errorMessage);
        }
    }

    /**
     * 检查上传Excel中的数字格式是否正确
     *
     * @param date         待检查数据
     * @param errorMessage 错误内容
     * @param rowError     错误集
     */
    private void checkDateFormatNumber(String date, String errorMessage, StringBuilder rowError) {
        if (!NumberUtils.isCreatable(date)) {
            rowError.append(errorMessage);
        }
    }

    /**
     * 车用气瓶业务里面的 出厂编号/产品编码 校验唯一性（产品编号在车用气瓶范围内全局唯一）
     *
     * @param data
     */
    private void checkFactoryNumUniqueness(XiAnEquipInfoExcelDto data, StringBuilder rowError) {
        Integer count = jgServiceFeignClient.checkFactoryNumUniquenessForVehicleCylinder(data.getFactoryNum());
        if (count > 0) {
            rowError.append("出厂编号/产品编码系统中已存在！");
        }
    }

    /**
     * 根据设备代码检查唯一性
     *
     * @param equCode
     * @param rowError
     */
    private void checkEquCodeUniqueness(String equCode, StringBuilder rowError) {
        Boolean bool = jgServiceFeignClient.selectByEquCodeAndClaimStatus(equCode);
        if (bool) {
            rowError.append("设备代码系统中已存在；");
        }
    }

    /**
     * 起重机械----技术参数 检查
     *
     * @param data
     * @param rowError
     */
    private void QZJXTechnicalParamsCheck(XiAnEquipInfoExcelDto data, StringBuilder rowError) {
    }

    /**
     * 场内机动车辆----技术参数 检查
     *
     * @param data
     * @param rowError
     */
    private void CNJDCLTechnicalParamsCheck(XiAnEquipInfoExcelDto data, StringBuilder rowError) {
    }

    /**
     * 锅炉----技术参数 检查
     *
     * @param data
     * @param rowError
     */
    private void GLTechnicalParamsCheck(XiAnEquipInfoExcelDto data, StringBuilder rowError) {
    }

    /**
     * 压力容器----技术参数 检查
     *
     * @param data
     * @param rowError
     */
    private void YLRQTechnicalParamsCheck(XiAnEquipInfoExcelDto data, StringBuilder rowError) {
        checkNotBlank(data.getSingleBottleVolume(), "单瓶容积不能为空；", rowError);
        checkNotBlank(data.getChargingMedium(), "充装介质不能为空；", rowError);
        checkNotBlank(data.getNominalWorkingPressure(), "公称工作压力不能为空；", rowError);
    }

    /**
     * 压力管道----技术参数 检查
     *
     * @param data
     * @param rowError
     */
    private void YLGDTechnicalParamsCheck(XiAnEquipInfoExcelDto data, StringBuilder rowError) {
        checkNotBlank(data.getWallThickness_YLGD(), "公称壁厚不能为空；", rowError);
        checkNotBlank(data.getNominalDiameter(), "公称直径不能为空；", rowError);
        checkNotBlank(data.getPipeLength(), "管道长度不能为空；", rowError);
        checkNotBlank(data.getPressure(), "压力不能为空；", rowError);
        checkNotBlank(data.getMedium_YLGD(), "介质不能为空；", rowError);
        checkNotBlank(data.getTemperature_YLGD(), "温度不能为空；", rowError);
        checkNotBlank(data.getPipelineNumber(), "管道编号不能为空；", rowError);
        checkNotBlank(data.getDeviceLevel_YLGD(), "管道级别不能为空；", rowError);
        Optional.ofNullable(data.getStartePosition()).ifPresent(v -> {
            if (v.split("-").length <= 1) {
                rowError.append("起/始位置 （经纬度）格式不正确");
            }
        });
        if (!ObjectUtils.isEmpty(data.getStartePosition()) && data.getStartePosition().split("-").length == 2) {
            String[] split = data.getStartePosition().split("-");
            data.setStartePosition(String.format("{\"latitude\":%s,\"longitude\":%s}", split[0], split[1]));
        }
    }

    /**
     * 大型游乐设施----技术参数 检查
     *
     * @param data
     * @param rowError
     */
    private void DXYLSSTechnicalParamsCheck(XiAnEquipInfoExcelDto data, StringBuilder rowError) {
    }

    /**
     * 客运索道----技术参数 检查
     *
     * @param data
     * @param rowError
     */
    private void KYSDTechnicalParamsCheck(XiAnEquipInfoExcelDto data, StringBuilder rowError) {
    }

}
