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


import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import com.yeejoin.amos.api.openapi.constant.Constant;
import com.yeejoin.amos.api.openapi.enums.CylinderType;
import com.yeejoin.amos.api.openapi.face.model.*;
import com.yeejoin.amos.api.openapi.face.orm.dao.ESCylinderFillingInfoRepository;
import com.yeejoin.amos.boot.module.cylinder.api.entity.CylinderFillingMessageEntity;
import com.yeejoin.amos.boot.module.cylinder.api.entity.ESCylinderFillingInfoDto;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.typroject.tyboot.component.cache.Redis;
import org.typroject.tyboot.component.emq.EmqKeeper;
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 javax.annotation.PostConstruct;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.stream.Collectors;

/**
 * 气瓶service
 */
@Component
public class CylinderService {

    private static final Logger logger = LogManager.getLogger(CylinderService.class);

    private final RedisTemplate redisTemplate;
    private final CylinderFillingDataValidationService cylinderFillingDataValidationService;
    private final TmCylinderFillingMessageService cylinderFillingMessageService;
    private final EmqKeeper emqKeeper;
    private final TmCylinderFillingCheckService cylinderFillingCheckService;
    private final TmCylinderFillingRecordService cylinderFillingRecordService;
    private final TmCylinderFillingService cylinderFillingService;

    private final ESCylinderFillingInfoRepository esCylinderFillingInfoRepository;

    private final SyncCylinderDataService syncCylinderDataService;

    private final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    private final BlockingQueue<CylinderFillingMessageEntity> blockingQueue = new LinkedBlockingQueue<>();

    public CylinderService(RedisTemplate redisTemplate, CylinderFillingDataValidationService cylinderFillingDataValidationService, TmCylinderFillingMessageService cylinderFillingMessageService, EmqKeeper emqKeeper, TmCylinderFillingCheckService cylinderFillingCheckService, TmCylinderFillingRecordService cylinderFillingRecordService, TmCylinderFillingService cylinderFillingService, ESCylinderFillingInfoRepository esCylinderFillingInfoRepository, SyncCylinderDataService syncCylinderDataService) {
        this.redisTemplate = redisTemplate;
        this.cylinderFillingDataValidationService = cylinderFillingDataValidationService;
        this.cylinderFillingMessageService = cylinderFillingMessageService;
        this.emqKeeper = emqKeeper;
        this.cylinderFillingCheckService = cylinderFillingCheckService;
        this.cylinderFillingRecordService = cylinderFillingRecordService;
        this.cylinderFillingService = cylinderFillingService;
        this.esCylinderFillingInfoRepository = esCylinderFillingInfoRepository;
        this.syncCylinderDataService = syncCylinderDataService;
    }

    @DSTransactional
    public void cylinderFillingHandler(String fillingData) throws MqttException {
        String tokenKey = Redis.genKey(Constant.TOKEN_PREFIX, RequestContext.getToken());
        if (ValidationUtil.isEmpty(this.redisTemplate.opsForValue().get(tokenKey))) {
            return;
        }
        BizTokenModel bizTokenModel = (BizTokenModel) this.redisTemplate.opsForValue().get(tokenKey);

        JSONObject jsonobject = JSONObject.fromObject(fillingData);
        CylinderFillingMessageEntity cylinderFillingMessageEntity = new CylinderFillingMessageEntity();
        cylinderFillingMessageEntity.setTime(simpleDateFormat.format(new Date()));
        cylinderFillingMessageEntity.setFillingCompanyName(bizTokenModel.getApiCompanyName());
        cylinderFillingMessageEntity.setFillingCompanyCode(bizTokenModel.getApiCompanyCode());
        cylinderFillingMessageEntity.setAppId(cylinderFillingService.getAppId());
        cylinderFillingMessageEntity.setRawData(fillingData);

        // 校验充装记录信息
        JSONArray validateResult = validateFillingData(jsonobject, cylinderFillingMessageEntity);

        // 有问题的充装数据记录日志
        blockingQueue.add(cylinderFillingMessageEntity);

        if (!validateResult.isEmpty()) {
            throw new BadRequest(validateResult.toString());
        }

        TmCylinderFillingModelList cylinderFillingModelList = com.alibaba.fastjson.JSONObject.parseObject(fillingData, TmCylinderFillingModelList.class);

        List<TmCylinderFillingModel> fillingBeforeList = cylinderFillingModelList.getFillingBefore();
        List<TmCylinderFillingRecordModel> fillingList = cylinderFillingModelList.getFilling();
        List<TmCylinderFillingCheckModel> fillingAfterList = cylinderFillingModelList.getFillingAfter();

        cylinderFillingService.createCylinderFillingBefore(fillingBeforeList);
        cylinderFillingRecordService.createCylinderFilling(fillingList);
        cylinderFillingCheckService.createCylinderFillingAfter(fillingAfterList);

        // 处理同步到气瓶es索引 cylinder_filling_info
        createCylinderFillingInfo2es(bizTokenModel, fillingBeforeList, fillingList, fillingAfterList);
    }

    private void createCylinderFillingInfo2es(BizTokenModel bizTokenModel, List<TmCylinderFillingModel> fillingBeforeList,
                                              List<TmCylinderFillingRecordModel> fillingList,
                                              List<TmCylinderFillingCheckModel> fillingAfterList) {
        if (ValidationUtil.isEmpty(fillingList)) {
            logger.info("气瓶充装信息对接--->没有充装数据");
        }
        Map<String, List<TmCylinderFillingModel>> before = fillingBeforeList.stream().collect(Collectors.groupingBy((TmCylinderFillingModel::getFillingBeforeId),
                Collectors.collectingAndThen(
                        Collectors.toList(),
                        list -> {
                            list.sort(Comparator.comparing(TmCylinderFillingModel::getInspectionDate).reversed());
                            return list;
                        }
                )));
        Map<String, List<TmCylinderFillingRecordModel>> filling = fillingList.stream().collect(Collectors.groupingBy((TmCylinderFillingRecordModel::getFillingBeforeId),
                Collectors.collectingAndThen(
                        Collectors.toList(),
                        list -> {
                            list.sort(Comparator.comparing(TmCylinderFillingRecordModel::getFillingStartTime).reversed());
                            return list;
                        }
                )));
        Map<String, List<TmCylinderFillingCheckModel>> after = fillingAfterList.stream().collect(Collectors.groupingBy((TmCylinderFillingCheckModel::getFillingBeforeId),
                Collectors.collectingAndThen(
                        Collectors.toList(),
                        list -> {
                            list.sort(Comparator.comparing(TmCylinderFillingCheckModel::getInspectionDate).reversed());
                            return list;
                        }
                )));

        List<ESCylinderFillingInfoDto> esCylinderFillingInfoList = new ArrayList<>();
        Set<String> sequenceCodeList = fillingList.stream().map(TmCylinderFillingRecordModel::getSequenceCode).collect(Collectors.toSet());
        // 查询气瓶的基础信息
        List<Map<String, String>> cylinderInfoList = syncCylinderDataService.queryTzsCylinderInfo(sequenceCodeList);
        filling.forEach((key, value) -> {
            ESCylinderFillingInfoDto esCylinderFillingInfo = new ESCylinderFillingInfoDto();

            TmCylinderFillingModel beforeInfo = ValidationUtil.isEmpty(before.get(key)) ? null : before.get(key).get(0);
            TmCylinderFillingRecordModel fillingInfo = value.get(0);
            TmCylinderFillingCheckModel afterInfo = ValidationUtil.isEmpty(after.get(key)) ? null : after.get(key).get(0);
            // 充装信息
            BeanUtils.copyProperties(fillingInfo, esCylinderFillingInfo);
            esCylinderFillingInfo.setCylinderTypeName(Objects.requireNonNull(CylinderType.getByCode(fillingInfo.getCylinderType())).getName());
            esCylinderFillingInfo.setAppId(bizTokenModel.getAppId());
            esCylinderFillingInfo.setFillingUnitName(bizTokenModel.getApiCompanyName());
            esCylinderFillingInfo.setFillingUnitCreditCode(bizTokenModel.getApiCompanyCode());

            Map<String, String> cylinder = cylinderInfoList.stream().filter(cylinderInfo -> cylinderInfo.get("sequenceCode").equals(fillingInfo.getSequenceCode())).findFirst().orElse(new HashMap<>());
            esCylinderFillingInfo.setEquList(cylinder.get("equList"));
            esCylinderFillingInfo.setEquListName(cylinder.get("equListName"));
            esCylinderFillingInfo.setEquCategory(cylinder.get("equCategory"));
            esCylinderFillingInfo.setEquCategoryName(cylinder.get("equCategoryName"));
            esCylinderFillingInfo.setEquDefine(cylinder.get("equDefine"));
            esCylinderFillingInfo.setEquDefineName(cylinder.get("equDefineName"));
            esCylinderFillingInfo.setFactoryNum(cylinder.get("factoryNum"));
            esCylinderFillingInfo.setOrgBranchCode(cylinder.get("orgBranchCode"));
            esCylinderFillingInfo.setOrgBranchName(cylinder.get("orgBranchName"));
            esCylinderFillingInfo.setEstateUnitName(cylinder.get("estateUnitName"));
            esCylinderFillingInfo.setCylinderStatus(cylinder.get("cylinderStatus"));
            esCylinderFillingInfo.setCylinderStatusName(cylinder.get("cylinderStatusName"));

            // 充装前检查信息
            if (beforeInfo != null) {
                ESCylinderFillingInfoDto.BeforeCheck beforeCheck = new ESCylinderFillingInfoDto.BeforeCheck();
                BeanUtils.copyProperties(beforeInfo, beforeCheck);
                esCylinderFillingInfo.setBeforeCheck(beforeCheck);
            }

            // 充装后复查信息
            if (afterInfo != null) {
                ESCylinderFillingInfoDto.AfterCheck afterCheck = new ESCylinderFillingInfoDto.AfterCheck();
                BeanUtils.copyProperties(afterInfo, afterCheck);
                esCylinderFillingInfo.setAfterCheck(afterCheck);
            }

            esCylinderFillingInfoList.add(esCylinderFillingInfo);
        });
        esCylinderFillingInfoRepository.saveAll(esCylinderFillingInfoList);
    }

    public JSONArray validateFillingData(JSONObject jsonobject, CylinderFillingMessageEntity cylinderFillingMessageEntity) throws MqttException {
        CylinderFillingDataValidationResultModel validateResult = null;

        try {
            validateResult = cylinderFillingDataValidationService.validateFilling(jsonobject);
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("气瓶充装信息上报，数据校验失败");
            cylinderFillingMessageEntity.setMessage(e.getMessage());
        }

        JSONArray errorData = new JSONArray();

        if (!ObjectUtils.isEmpty(validateResult)) {
            cylinderFillingMessageEntity.setCylinderNumber(validateResult.getCylinderNumber());
            List<String> logMessage = new ArrayList<>();
            int errorNumber = 0;
            JSONObject error = new JSONObject();
            if (!ObjectUtils.isEmpty(validateResult.getBeforeErrorData())) {
                errorNumber += validateResult.getBeforeErrorData().size();
                error.put("充装前检查错误数据：", validateResult.getBeforeErrorData());
                errorData.add(error);

                logMessage.add("充装前检查数据异常气瓶数：" + validateResult.getBeforeErrorCylinderNumber());
            }

            if (!ObjectUtils.isEmpty(validateResult.getRecordErrorData())) {
                errorNumber += validateResult.getRecordErrorData().size();
                error.put("充装记录错误数据：", validateResult.getRecordErrorData());
                errorData.add(error);

                logMessage.add("充装记录数据异常气瓶数：" + validateResult.getRecordErrorCylinderNumber());
            }

            if (!ObjectUtils.isEmpty(validateResult.getAfterErrorData())) {
                errorNumber += validateResult.getAfterErrorData().size();
                error.put("充装后复查错误数据：", validateResult.getAfterErrorData());
                errorData.add(error);

                logMessage.add("充装后复查数据异常气瓶数：" + validateResult.getAfterErrorCylinderNumber());
            }

            if (!ObjectUtils.isEmpty(validateResult.getSeqCodeErrorData())) {
                errorNumber += validateResult.getSeqCodeErrorData().size();
                error.put("气瓶信息不存在：", validateResult.getSeqCodeErrorData());
                errorData.add(error);

                logMessage.add("气瓶信息不存在数量：" + validateResult.getSeqCodeErrorData().size() + "个");
            }

            if (errorNumber <= 0) {
                cylinderFillingMessageEntity.setMessage("气瓶充装信息校验成功的气瓶数：" + validateResult.getSuccessCylinderNumber() + "个");
            } else {
                cylinderFillingMessageEntity.setMessage(String.join("条; ", logMessage));
            }
        } else {
            JSONObject error = new JSONObject();
            error.put("数据校验失败", "上传数据不能为空");
            errorData.add(error);
        }
        return errorData;
    }

    @PostConstruct
    void init() {
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        for (int i = 0; i < 3; i++) {
            executorService.execute(() -> {
                try {
                    while (true) {
                        logger.info("处理气瓶对接错误信息入库~开始");
                        CylinderFillingMessageEntity cylinderFillingMessageModelEntity = blockingQueue.take();
                        cylinderFillingMessageService.save(cylinderFillingMessageModelEntity);
                        logger.info("处理气瓶对接错误信息入库~完成");
                    }
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    logger.error("处理气瓶对接错误信息入库异常", e);
                    throw new RuntimeException(e);
                }
            });
        }
    }
}
