package com.yeejoin.amos.boot.module.jg.biz.config;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.read.metadata.holder.ReadRowHolder;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.yeejoin.amos.boot.biz.common.bo.CompanyBo;
import com.yeejoin.amos.boot.module.common.api.dao.ESEquipmentCategory;
import com.yeejoin.amos.boot.module.common.api.dto.ESEquipmentCategoryDto;
import com.yeejoin.amos.boot.module.jg.api.dto.EquipInfoCylinderExcelDto;
import com.yeejoin.amos.boot.module.jg.biz.service.ICommonService;
import com.yeejoin.amos.boot.module.jg.biz.service.IIdxBizJgOtherInfoService;
import com.yeejoin.amos.boot.module.jg.biz.service.IIdxBizJgUseInfoService;
import com.yeejoin.amos.boot.module.jg.biz.service.impl.*;
import com.yeejoin.amos.boot.module.ymt.api.entity.*;
import com.yeejoin.amos.boot.module.ymt.api.mapper.CategoryOtherInfoMapper;
import com.yeejoin.amos.boot.module.ymt.api.mapper.IdxBizJgRegisterInfoMapper;
import com.yeejoin.amos.boot.module.ymt.api.mapper.SuperviseInfoMapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;
import org.typroject.tyboot.core.restful.exception.instance.BadRequest;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicLong;
import static com.alibaba.fastjson.JSON.toJSONString;
import static com.yeejoin.amos.boot.module.jg.api.enums.CylinderTypeEnum.SPECIAL_CYLINDER;

@Slf4j
@Data
@EqualsAndHashCode(callSuper = true)
@Component
public class PressureVesselListener extends AnalysisEventListener<EquipInfoCylinderExcelDto> {
    private static final int BATCH_COUNT = 200;
    //数据集合
    List<String> useInnerCodeList = new ArrayList<>();//单位内部编号集合
    List<String> equCodeList = new ArrayList<>();//设备代码集合
    List<String> records = new ArrayList<>();//设备代码集合
    List<String> factoryNumList = new ArrayList<>();//出厂编码集合
    List<IdxBizJgUseInfo> useInfoList = new ArrayList<>();
    List<IdxBizJgRegisterInfo> registerInfoList = new ArrayList<>();
    List<IdxBizJgDesignInfo> designInfoList = new ArrayList<>();
    List<IdxBizJgFactoryInfo> factoryInfoList = new ArrayList<>();
    List<IdxBizJgOtherInfo> otherInfoList = new ArrayList<>();
    List<IdxBizJgTechParamsVessel> paramsVesselList = new ArrayList<>();
    List<IdxBizJgInspectionDetectionInfo> inspectionDetectionInfoList = new ArrayList<>();
    List<ESEquipmentCategoryDto> esEquipmentCategoryList = new ArrayList<>();
    private StringBuilder result = new StringBuilder();
    private JgInstallationNoticeServiceImpl jgInstallationNoticeService;
    private IIdxBizJgUseInfoService idxBizJgUseInfoService;
    private IdxBizJgDesignInfoServiceImpl idxBizJgDesignInfoService;
    private IdxBizJgFactoryInfoServiceImpl idxBizJgFactoryInfoService;
    private IIdxBizJgOtherInfoService idxBizJgOtherInfoService;
    private IdxBizJgRegisterInfoServiceImpl idxBizJgRegisterInfoService;
    private IdxBizJgTechParamsVesselServiceImpl idxBizJgTechParamsVesselService;
    private IdxBizJgInspectionDetectionInfoServiceImpl idxBizJgInspectionDetectionInfoService;
    private ICommonService commonService;
    private IdxBizJgRegisterInfoMapper idxBizJgRegisterInfoMapper;
    private ESEquipmentCategory esEquipmentCategory;
    private CategoryOtherInfoMapper categoryOtherInfoMapper;
    private SuperviseInfoMapper superviseInfoMapper;
    private Map<String, Object> paramMap;
    private CompanyBo company;

    final private static AtomicLong sendThreadPoolCounter = new AtomicLong(0);
    final public static ExecutorService pooledExecutor =
            Executors.newFixedThreadPool(20 + Runtime.getRuntime().availableProcessors(),
                    createThreadFactory());
    private static ThreadFactory createThreadFactory() {
        return runnable -> {
            Thread thread = new Thread(runnable);
            thread.setName(String.format("kafka-consumer-iot-pool-%d", PressureVesselListener.sendThreadPoolCounter.getAndIncrement()));
            return thread;
        };
    }

    @Override
    public void invoke(EquipInfoCylinderExcelDto data, AnalysisContext context) {
        ReadRowHolder readRowHolder = context.readRowHolder();
        int rowIndex = readRowHolder.getRowIndex() + 1;
        try {
            log.info("解析第{}行数据：{}", rowIndex, JSON.toJSONString(data));
            // 检查各字段是否为空，如果为空则追加错误信息
            checkNotBlank(data.getProductName(), "设备名称不能为空");
            checkNotBlank(data.getBrandName(), "品牌名称不能为空");
            checkNotBlank(data.getEquType(), "设备型号不能为空");
            checkNotBlank(data.getUseInnerCode(), "单位内部编号不能为空");
            if (useInnerCodeList.contains(data.getUseInnerCode())) {
                result.append("单位内部编号不能重复；");
            }
            checkNotBlank(data.getWhetherVehicleCylinder(), "是否车用气瓶不能为空");
            checkNotBlank(data.getEquCodeType(), "是否有设备代码不能为空");
            if ("1".equals(data.getEquCodeType())) {
                checkNotBlank(data.getEquCode(), "设备代码不能为空");
                String equCode = data.getEquCode();
                if (equCode.matches("[a-zA-Z0-9]+")) {
                    if (equCode.length() <= 17) {
                        result.append("设备代码不能小于17位");
                    }
                    if (equCode.length() >= 20) {
                        result.append("设备代码不能大于20位");
                    }
                } else {
                    result.append("设备代码不能包含特殊字符");
                }
                if (equCodeList.contains(data.getEquCode())) {
                    result.append("设备代码不能重复；");
                }
                this.checkEquCodeUniqueness(data.getEquCode());
            } else {
                data.setEquCode("");
            }
            checkNotBlank(data.getDesignUnitCreditCode(), "设计单位统一社会信用代码不能为空");
            checkNotBlank(data.getDesignUnitName(), "设计单位名称不能为空");
            Optional.ofNullable(data.getDesignDate()).ifPresent(v -> checkDateFormatCorrect(v, "设计日期格式不正确"));
            Optional.ofNullable(data.getAppraisalDate()).ifPresent(v -> checkDateFormatCorrect(v, "设计文件鉴定日期格式不正确"));
            checkNotBlank(data.getProduceUnitCreditCode(), "制造单位统一社会信用代码不能为空");
            checkNotBlank(data.getProduceUnitName(), "制造单位名称不能为空");
            checkNotBlank(data.getProduceLicenseNum(), "制造许可编号不能为空");
            checkNotBlank(data.getFactoryNum(), "出厂编号/产品编码不能为空");
            if ("0".equals(data.getWhetherVehicleCylinder()) && factoryNumList.contains(data.getFactoryNum()))
                result.append("出厂编号/产品编码不能重复；");
            checkFactoryNumUniqueness(data.getFactoryNum(), null);
            checkNotBlank(data.getProduceDate(), "制造日期不能为空");
            Optional.ofNullable(data.getProduceDate()).ifPresent(v -> checkDateFormatCorrect(v, "制造日期格式不正确"));
            checkNotBlank(data.getInspectOrgName(), "检测机构名称不能为空");
            checkNotBlank(data.getInspectOrgCode(), "检测机构代码不能为空");
            checkInspectOrg(data.getInspectOrgCode());
            checkNotBlank(data.getInspectStaff(), "检测人员名称不能为空");
            checkNotBlank(data.getInspectDate(), "检测日期不能为空");
            checkDateFormatCorrect(data.getInspectDate(), "检测日期格式不正确");
            checkNotBlank(data.getSingleBottleVolume(), "单瓶容积不能为空");
            checkNotBlank(data.getChargingMedium(), "充装介质不能为空");
            checkNotBlank(data.getNominalWorkingPressure(), "公称工作压力不能为空");

            // 如果存在错误信息，则抛出 BadRequest 异常
            if (result.length() > 0) {
                result.insert(0, "Excel第[" + rowIndex + "]行 -> ");
                throw new BadRequest(result.toString());
            }
            this.dealExcelData(data);
        } catch (Exception e) {
            log.error(String.format("行索引数: [%s] -> 失败的 Excel 数据: [%s]", rowIndex, JSON.toJSONString(data)), e);
            throw e;
        }
    }

    private void checkEquCodeUniqueness(String equCode) {
        // 根据设备代码检查唯一性
        Integer count = idxBizJgRegisterInfoMapper.selectByEquCodeAndClaimStatus(equCode,"");
        if (count > 0) {
            result.append("设备代码系统中已存在");
        }
    }

    // 检查上传Excel中的日期格式是否正确
    private void checkDateFormatCorrect(String date, String errorMessage) {
        if (!date.matches("\\d{4}-\\d{2}-\\d{2}")) {
            result.append(errorMessage).append("；");
        }
    }

    private void checkInspectOrg(String inspectOrgCode){
        List<Map<String, Object>> InspectOrgList = commonService.getAllInspectionInfo("检验检测机构");
        if (findUseCode(InspectOrgList, inspectOrgCode) == null) {
            result.append("未查询到该检验机构");
        }
    }

    // 检查字段是否为空，如果为空则追加错误信息到result
    private void checkNotBlank(String value, String errorMessage) {
        if (StringUtils.isBlank(value)) {
            result.append(errorMessage).append("；");
        }
    }

    private void checkFactoryNumUniqueness(String factoryNum, String sequenceNbr) {
        // 车用气瓶业务里面的 出厂编号/产品编码 校验唯一性（产品编号在车用气瓶范围内全局唯一）
        if (commonService.checkFactoryNumUniquenessForVehicleCylinder(factoryNum, sequenceNbr) > 0) {
            result.append("出厂编号/产品编码系统中已存在！");
        }
    }

    /**
     * 处理导入数据
     *
     * @param data excelData
     */
    private void dealExcelData(EquipInfoCylinderExcelDto data) {
        useInnerCodeList.add(data.getUseInnerCode());
        equCodeList.add(data.getEquCode());
        factoryNumList.add("0".equals(data.getWhetherVehicleCylinder()) ? data.getFactoryNum() : null);
        String equListCode = (String) paramMap.get("EQU_LIST");
        String equCategoryCode = (String) paramMap.get("EQU_CATEGORY");
        String equDefineCode = (String) paramMap.get("EQU_DEFINE");
        String dataSource = (String) paramMap.get("DATA_SOURCE");

        Date date = new Date();
        String record = UUID.randomUUID().toString();
        records.add(record);

        //使用信息
        IdxBizJgUseInfo useInfo = new IdxBizJgUseInfo();
        BeanUtils.copyProperties(data, useInfo);
        useInfo.setRecord(record);
        useInfo.setRecDate(date);
        useInfo.setDataSource(dataSource != null ? dataSource : "jg");//区分历史设备和新增设备
        useInfo.setEquState(null);
        useInfo.setIsIntoManagement(dataSource != null);//历史气瓶导入为已纳管设备
        // 使用单位信息
        if("个人主体".equals(company.getCompanyType())){
            useInfo.setUseUnitCreditCode(company.getCompanyCode().split("_")[1]);
            useInfo.setUseUnitName(company.getCompanyName().split("_")[1]);
        }else {
            useInfo.setUseUnitCreditCode(company.getCompanyCode());
            useInfo.setUseUnitName(company.getCompanyName());
        }
        useInfoList.add(useInfo);

        //设计信息
        IdxBizJgDesignInfo designInfo = new IdxBizJgDesignInfo();
        BeanUtils.copyProperties(data, designInfo);
        designInfo.setRecord(record);
        designInfo.setRecDate(date);
        if (data.getDesignDate() != null){
            designInfo.setDesignDate(DateUtil.parse(data.getDesignDate(), "yyyy-MM-dd"));
        }
        designInfoList.add(designInfo);

        //制造信息
        IdxBizJgFactoryInfo factoryInfo = new IdxBizJgFactoryInfo();
        BeanUtils.copyProperties(data, factoryInfo);
        factoryInfo.setRecord(record);
        factoryInfo.setRecDate(date);
        factoryInfo.setProduceDate(DateUtil.parse(data.getProduceDate(), "yyyy-MM-dd"));
        factoryInfo.setImported(Optional.ofNullable(data.getImported()).orElse("0"));
        factoryInfoList.add(factoryInfo);

        //注册登记
        IdxBizJgRegisterInfo registerInfo = new IdxBizJgRegisterInfo();
        BeanUtils.copyProperties(data, registerInfo);
        registerInfo.setRecord(record);
        registerInfo.setRecDate(date);
        registerInfo.setEquCategory(equCategoryCode);
        registerInfo.setEquDefine(equDefineCode);
        registerInfo.setEquList(equListCode);
        registerInfo.setRegisterState(idxBizJgRegisterInfoService.getRegCode());
        registerInfoList.add(registerInfo);

        //检验检测
        IdxBizJgInspectionDetectionInfo inspectionDetectionInfo = new IdxBizJgInspectionDetectionInfo();
        BeanUtils.copyProperties(data, inspectionDetectionInfo);
        inspectionDetectionInfo.setInspectOrgCode( data.getInspectOrgCode());
        inspectionDetectionInfo.setRecord(record);
        inspectionDetectionInfo.setRecDate(date);
        inspectionDetectionInfo.setInspectType("ZZJDJY");
        //inspectionDetectionInfo.setInspectConclusion("6040");//默认合格
        inspectionDetectionInfo.setInspectDate(DateUtil.parse(data.getInspectDate(), "yyyy-MM-dd"));
        // 根据条件确定增加的年数
        inspectionDetectionInfo.setNextInspectDate(Date.from(
                LocalDate.parse(data.getInspectDate(), DateTimeFormatter.ofPattern("yyyy-MM-dd"))
                        .plusYears(SPECIAL_CYLINDER.getCode().equals(equDefineCode) ? 3 : 4)
                        .atStartOfDay(ZoneId.systemDefault())
                        .toInstant()));
        inspectionDetectionInfoList.add(inspectionDetectionInfo);

        // 其他信息
        IdxBizJgOtherInfo otherInfo = new IdxBizJgOtherInfo();
        BeanUtils.copyProperties(data, otherInfo);
        otherInfo.setRecord(record);
        otherInfo.setRecDate(date);
        otherInfoList.add(otherInfo);

        //技术参数
        IdxBizJgTechParamsVessel paramsVessel = new IdxBizJgTechParamsVessel();
        BeanUtils.copyProperties(data, paramsVessel);
        paramsVessel.setRecord(record);
        paramsVessel.setRecDate(date);
        paramsVesselList.add(paramsVessel);

        ESEquipmentCategoryDto dto = JSON.parseObject(toJSONString(data), ESEquipmentCategoryDto.class);
        List<EquipmentCategory> equList = commonService.getEquipmentCategoryList(equListCode, null);
        List<EquipmentCategory> equCategory = commonService.getEquipmentCategoryList(equCategoryCode, null);
        List<EquipmentCategory> equDefine = commonService.getEquipmentCategoryList(equDefineCode, null);

        // 使用单位信息
        dto.setDATA_SOURCE(useInfo.getDataSource());
        if(inspectionDetectionInfo.getNextInspectDate() != null){
            dto.setNEXT_INSPECT_DATE(DateUtil.parse(DateUtil.format(inspectionDetectionInfo.getNextInspectDate(), DatePattern.NORM_DATE_PATTERN)).getTime());
        }
        dto.setREC_DATE(System.currentTimeMillis());
        dto.setSEQUENCE_NBR(record);
        dto.setFACTORY_NUM(factoryInfo.getFactoryNum());
        dto.setUSE_INNER_CODE(useInfo.getUseInnerCode());
        dto.setEQU_CATEGORY_CODE(equCategoryCode);
        if (CollectionUtils.isNotEmpty(equCategory)) {
            dto.setEQU_CATEGORY(equCategory.get(0).getName());
        }
        dto.setEQU_LIST_CODE(equListCode);
        if (CollectionUtils.isNotEmpty(equList)) {
            dto.setEQU_LIST(equList.get(0).getName());
        }
        dto.setEQU_DEFINE_CODE(equDefineCode);
        if (CollectionUtils.isNotEmpty(equDefine)) {
            dto.setEQU_DEFINE(equDefine.get(0).getName());
        }
        // 使用单位信息
        if("个人主体".equals(company.getCompanyType())){
            dto.setUSE_UNIT_CREDIT_CODE(company.getCompanyCode().split("_")[1]);
            dto.setUSE_UNIT_NAME(company.getCompanyName().split("_")[1]);
        }else {
            dto.setUSE_UNIT_CREDIT_CODE(company.getCompanyCode());
            dto.setUSE_UNIT_NAME(company.getCompanyName());
        }
        esEquipmentCategoryList.add(dto);
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        if (paramsVesselList.size() > BATCH_COUNT) {
            throw new BadRequest(result.append("单次导入限制200条") + "");
        }

       pooledExecutor.execute(() -> {
            Optional.of(useInfoList).filter(list -> !list.isEmpty()).ifPresent(idxBizJgUseInfoService::saveBatch);
            Optional.of(designInfoList).filter(list -> !list.isEmpty()).ifPresent(idxBizJgDesignInfoService::saveBatch);
            Optional.of(registerInfoList).filter(list -> !list.isEmpty()).ifPresent(idxBizJgRegisterInfoService::saveBatch);
            Optional.of(factoryInfoList).filter(list -> !list.isEmpty()).ifPresent(idxBizJgFactoryInfoService::saveBatch);
            Optional.of(otherInfoList).filter(list -> !list.isEmpty()).ifPresent(idxBizJgOtherInfoService::saveBatch);
            Optional.of(paramsVesselList).filter(list -> !list.isEmpty()).ifPresent(idxBizJgTechParamsVesselService::saveBatch);
            Optional.of(inspectionDetectionInfoList).filter(list -> !list.isEmpty()).ifPresent(idxBizJgInspectionDetectionInfoService::saveBatch);
            Optional.of(esEquipmentCategoryList).filter(list -> !list.isEmpty()).ifPresent(esEquipmentCategory::saveAll);
        });

        result.append("导入完成，成功导入");
        result.append(useInfoList.size());
        result.append("条数据");
        log.info("所有数据解析完成！");
    }

    @Override
    public void onException(Exception exception, AnalysisContext context) throws Exception {
        if (!esEquipmentCategoryList.isEmpty()) {
            superviseInfoMapper.deleteDataAll(records);
            esEquipmentCategory.deleteAll(esEquipmentCategoryList);
        }
        throw exception;
    }

    public String getResult() {
        return result.toString();
    }

    public String findUseCode(List<Map<String, Object>> unitList, String inspectOrgCode) {
        Optional<Map<String, Object>> optional = unitList.stream()
                .filter(map -> map.get("useCode").equals(inspectOrgCode))
                .findFirst();
        return optional.map(map -> (String) map.get("useCode")).orElse(null);
    }
}