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

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.yeejoin.amos.boot.module.common.api.entity.TzsUserPermission;
import com.yeejoin.amos.boot.module.jg.api.entity.SafetyProblemTracing;
import com.yeejoin.amos.boot.module.jg.api.enums.SafetyProblemSourceTypeEnum;
import com.yeejoin.amos.boot.module.jg.api.enums.SafetyProblemStatusEnum;
import com.yeejoin.amos.boot.module.jg.api.enums.SafetyProblemTypeEnum;
import com.yeejoin.amos.boot.module.jg.api.event.SafetyProblemEvent;
import com.yeejoin.amos.boot.module.jg.api.event.handler.SafetyProblemEventHandler;
import com.yeejoin.amos.boot.module.jg.biz.handler.SafetyProblemEventHandlerFactory;
import com.yeejoin.amos.boot.module.jg.biz.service.impl.SafetyProblemTracingServiceImpl;
import com.yeejoin.amos.boot.module.ymt.api.entity.BaseUnitLicence;
import com.yeejoin.amos.boot.module.ymt.api.entity.TzBaseEnterpriseInfo;
import com.yeejoin.amos.boot.module.ymt.api.entity.TzsUserInfo;
import com.yeejoin.amos.boot.module.ymt.api.mapper.*;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.typroject.tyboot.component.emq.EmqKeeper;
import org.typroject.tyboot.component.emq.EmqxListener;
import org.typroject.tyboot.core.foundation.utils.ValidationUtil;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
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;

@Component
@Slf4j
public class SafetyProblemTopicMessage extends EmqxListener {

    @Value("${safety.problem.message.handle.thread.number:3}")
    private int threadNumber;

    @Value("${spring.application.name}")
    private String applicationName;

    @Resource
    protected EmqKeeper emqKeeper;

    @Value("${safety.problem.tracing.topic:safetyProblemTracing/#}")
    private String problemEventTopic;

    private static TzBaseEnterpriseInfoMapper tzBaseEnterpriseInfoMapper;
    private static BaseUnitLicenceMapper baseUnitLicenceMapper;
    private static IdxBizJgUseInfoMapper idxBizJgUseInfoMapper;
    private static TzsUserPermissionMapper tzsUserPermissionMapper;
    private static TzsUserInfoMapper tzsUserInfoMapper;

    @Autowired
    public void setTzBaseEnterpriseInfoMapper(TzBaseEnterpriseInfoMapper mapper) {
        SafetyProblemTopicMessage.tzBaseEnterpriseInfoMapper = mapper;
    }
    @Autowired
    public void setBaseUnitLicenceMapper(BaseUnitLicenceMapper mapper) {
        SafetyProblemTopicMessage.baseUnitLicenceMapper = mapper;
    }
    @Autowired
    public void setIdxBizJgUseInfoMapper(IdxBizJgUseInfoMapper mapper) {
        SafetyProblemTopicMessage.idxBizJgUseInfoMapper = mapper;
    }
    @Autowired
    public void setTzsUserPermissionMapper(TzsUserPermissionMapper mapper) {
        SafetyProblemTopicMessage.tzsUserPermissionMapper = mapper;
    }
    @Autowired
    public void setTzsUserInfoMapper(TzsUserInfoMapper mapper) {
        SafetyProblemTopicMessage.tzsUserInfoMapper = mapper;
    }

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

    private ExecutorService executorService;

    @PostConstruct
    void init() throws Exception {
        emqKeeper.subscript(buildTopic(problemEventTopic), 2, this);
        executorService = Executors.newFixedThreadPool(threadNumber);
        for (int i = 0; i < threadNumber; i++) {
            final int threadIndex = i;
            executorService.execute(() -> {
                try {
                    log.info("启动消息处理线程-{}", threadIndex);
                    while (!Thread.currentThread().isInterrupted()) {
                        SafetyProblemEvent safetyProblemMessageEvent = blockingQueue.take();
                        try {
                            // 计算耗时开始
                            long start = System.currentTimeMillis();
                            log.info("接收到问题生产原始消息:{}", safetyProblemMessageEvent.getMessage());
                            JSONArray jsonObject = JSON.parseArray(safetyProblemMessageEvent.getMessage().toString());
                            log.info("接收到问题生产消息:{}", jsonObject);
                            SafetyProblemEventHandler eventHandler = SafetyProblemEventHandlerFactory.createProblemHandler(safetyProblemMessageEvent.getTopic());
                            eventHandler.handle(safetyProblemMessageEvent);
                            log.info("处理问题生产消息完成，耗时：{}", System.currentTimeMillis() - start);
                        } catch (JSONException | ClassCastException | IllegalArgumentException e) {
                            log.error("处理消息异常", e);
                        } catch (Exception e) {
                            log.error("处理消息时发生未预期异常", e);
                        }
                    }
                } catch (InterruptedException e) {
                    log.warn("处理线程{}被中断，准备退出", threadIndex);
                    Thread.currentThread().interrupt();
                } catch (Exception e) {
                    log.error("处理线程{}发生异常，准备退出", threadIndex, e);
                } finally {
                    log.info("消息处理线程-{}退出", threadIndex);
                }
            });
        }
        log.info("消息监听器初始化完成，线程数:{}", threadNumber);
    }

    @Override
    public void processMessage(String topic, MqttMessage message) {
        log.info("接收问题生产消息开始，当前队列大小:{}", blockingQueue.size());
        blockingQueue.add(new SafetyProblemEvent(topic, message));
        log.info("接收问题生产消息完成，添加后队列大小:{}", blockingQueue.size());
    }

    public static void generateProblem(JSONArray jsonArray, SafetyProblemTypeEnum problemTypeEnum, SafetyProblemTracingServiceImpl safetyProblemTracingService) {
        if (jsonArray == null || problemTypeEnum == null) {
            throw new IllegalArgumentException("jsonObject and problemTypeEnum must not be null.");
        }
//        JSONArray records = jsonObject.getJSONArray(problemTypeEnum.getMsgKey());
        if (!ValidationUtil.isEmpty(jsonArray)) {
            List<SafetyProblemTracing> safetyProblemTracingList = jsonArray.stream().map(item -> {
                if (!(item instanceof JSONObject)) {
                    throw new IllegalArgumentException("item is not a JSONObject.");
                }
                JSONObject json = (JSONObject) item;
                SafetyProblemTracing safetyProblemTracing = new SafetyProblemTracing();
//            safetyProblemTracing.setProblemNum(json.getString("problemNum"));
                safetyProblemTracing.setProblemType(problemTypeEnum.getName());
                safetyProblemTracing.setProblemTypeCode(problemTypeEnum.getProblemTypeCode());
                safetyProblemTracing.setProblemDesc(problemTypeEnum.getDesc());
//            safetyProblemTracing.setProblemLevel(json.getString("problemLevel"));
//            safetyProblemTracing.setProblemLevelCode(json.getString("problemLevelCode"));
                safetyProblemTracing.setSourceType(SafetyProblemSourceTypeEnum.EQUIP.getName());
                safetyProblemTracing.setSourceTypeCode(SafetyProblemSourceTypeEnum.EQUIP.getCode());
                safetyProblemTracing.setSourceId(json.getOrDefault("RECORD", "").toString());
                safetyProblemTracing.setProblemTime(getProblemTime(json));
                safetyProblemTracing.setEquipSuperviseCode(json.getOrDefault("SUPERVISORY_CODE", "").toString());
                safetyProblemTracing.setEquipList(json.getOrDefault("equList", "").toString());
                safetyProblemTracing.setEquipListCode(json.getOrDefault("EQU_LIST", "").toString());
                safetyProblemTracing.setPrincipalUnit(json.getOrDefault("USE_UNIT_NAME", "").toString());
                safetyProblemTracing.setPrincipalUnitCode(json.getOrDefault("USE_UNIT_CREDIT_CODE", "").toString());
                safetyProblemTracing.setPrincipalUnitType(json.getOrDefault("unitType", "").toString());
                safetyProblemTracing.setGoverningBody(json.getOrDefault("ORG_BRANCH_NAME", "").toString());
                safetyProblemTracing.setGoverningBodyCode(json.getOrDefault("COMPANY_ORG_BRANCH_CODE", "").toString());
                safetyProblemTracing.setGoverningBodyOrgCode(json.getOrDefault("ORG_BRANCH_CODE", "").toString());
                safetyProblemTracing.setExtraInfo(json.getOrDefault("extraInfo", "").toString());
                JSONObject region = getRegionBySourceTypeAndId(SafetyProblemSourceTypeEnum.EQUIP.getName(), json.getOrDefault("RECORD", "").toString());
                safetyProblemTracing.setRegionName(region.getString("regionName"));
                safetyProblemTracing.setRegionCode(region.getString("regionCode"));
                safetyProblemTracing.setCreateDate(new Date());
//            safetyProblemTracing.setCreateUser(json.getString("createUser"));
//            safetyProblemTracing.setCreateUserId(json.getString("createUserId"));
                safetyProblemTracing.setProblemStatus(SafetyProblemStatusEnum.UNHANDLED.getName());
                safetyProblemTracing.setProblemStatusCode(SafetyProblemStatusEnum.UNHANDLED.getCode());
                return safetyProblemTracing;
            }).collect(Collectors.toList());
            safetyProblemTracingService.saveOrUpdateBatchByColumns(safetyProblemTracingList,
                    safetyProblemTracing -> new QueryWrapper<>()
                            .eq("problem_type_code", safetyProblemTracing.getProblemTypeCode())
                            .eq("source_id", safetyProblemTracing.getSourceId())
                            .eq("problem_status_code", SafetyProblemStatusEnum.UNHANDLED.getCode()));
        }
    }

    /**
     * 根据SourceType和SourceId 获取所属区域的code name
     *
     * @param sourceType SafetyProblemSourceTypeEnum
     * @param sourceId   设备record/企业许可id/人员资质id
     * @return JSONObject
     */
    public static JSONObject getRegionBySourceTypeAndId(String sourceType, String sourceId) {
        String regionCode = "";
        String regionName = "";
        if (SafetyProblemSourceTypeEnum.EQUIP.getName().equals(sourceType)) {
            // 取设备的使用地点
            JSONObject usePlaceAndCode = idxBizJgUseInfoMapper.getUsePlaceAndCodeByRecord(sourceId);
            if (null != usePlaceAndCode) {
                regionCode = usePlaceAndCode.getString("USE_PLACE_CODE");
                regionName = usePlaceAndCode.getString("USE_PLACE");
            }
        }
        if (SafetyProblemSourceTypeEnum.UNIT.getName().equals(sourceType)) {
            // 单位的注册地点
            BaseUnitLicence baseUnitLicence = baseUnitLicenceMapper.selectById(sourceId);
            if (baseUnitLicence != null) {
                String unitCode = baseUnitLicence.getUnitCode();
                TzBaseEnterpriseInfo enterpriseInfo = tzBaseEnterpriseInfoMapper.selectOne(new LambdaQueryWrapper<TzBaseEnterpriseInfo>().eq(TzBaseEnterpriseInfo::getUseUnitCode, unitCode));
                if (enterpriseInfo != null) {
                    // regionCode 目前数据不支持，暂定为空
                    regionCode = null;
                    regionName = String.join("/", getRegionParts(enterpriseInfo));
                }
            }
        }
        if (SafetyProblemSourceTypeEnum.PERSONNEL.getName().equals(sourceType)) {
            // 人员所在单位的单位地点
            TzsUserPermission userPermission = tzsUserPermissionMapper.selectById(sourceId);
            if (userPermission != null) {
                Long userSeq = userPermission.getUserSeq();
                TzsUserInfo userInfo = tzsUserInfoMapper.selectById(userSeq);
                if (userInfo != null) {
                    String unitCode = userInfo.getUnitCode();
                    TzBaseEnterpriseInfo enterpriseInfo = tzBaseEnterpriseInfoMapper.selectOne(new LambdaQueryWrapper<TzBaseEnterpriseInfo>().eq(TzBaseEnterpriseInfo::getUseUnitCode, unitCode));
                    if (enterpriseInfo != null) {
                        // regionCode 目前数据不支持，暂定为空
                        regionCode = null;
                        regionName = String.join("/", getRegionParts(enterpriseInfo));
                    }
                }
            }
        }
        return new JSONObject().fluentPut("regionCode", regionCode).fluentPut("regionName", regionName);
    }

    private static List<String> getRegionParts(TzBaseEnterpriseInfo enterpriseInfo) {
        List<String> regionParts = new ArrayList<>();
        if (enterpriseInfo.getProvince() != null && !enterpriseInfo.getProvince().isEmpty()) {
            regionParts.add(enterpriseInfo.getProvince());
        }
        if (enterpriseInfo.getCity() != null && !enterpriseInfo.getCity().isEmpty()) {
            regionParts.add(enterpriseInfo.getCity());
        }
        if (enterpriseInfo.getDistrict() != null && !enterpriseInfo.getDistrict().isEmpty()) {
            regionParts.add(enterpriseInfo.getDistrict());
        }
        return regionParts;
    }

    public static Date getProblemTime(JSONObject json) {
        // 安全处理problemTime字段，支持时间戳转换
        Object problemTimeObj = json.getOrDefault("problemTime", "");
        Date problemTime = null;
        if (problemTimeObj instanceof Long) {
            problemTime = new Date((Long) problemTimeObj);
        } else if (problemTimeObj instanceof String && !((String) problemTimeObj).isEmpty()) {
            try {
                problemTime = new Date(Long.parseLong((String) problemTimeObj));
            } catch (NumberFormatException e) {
                log.warn("无法解析problemTime: {}", problemTimeObj);
            }
        } else if (problemTimeObj instanceof Date) {
            problemTime = (Date) problemTimeObj;
        }
        return problemTime;
    }

    public static void generateUnitProblem(JSONArray jsonArray, SafetyProblemTypeEnum problemTypeEnum, SafetyProblemTracingServiceImpl safetyProblemTracingService) {
        generateProblem2(jsonArray, problemTypeEnum, safetyProblemTracingService);
    }

    private static void generateProblem2(JSONArray jsonArray, SafetyProblemTypeEnum problemTypeEnum, SafetyProblemTracingServiceImpl safetyProblemTracingService) {
        if (jsonArray == null || problemTypeEnum == null) {
            throw new IllegalArgumentException("jsonObject and problemTypeEnum must not be null.");
        }
        if (!ValidationUtil.isEmpty(jsonArray)) {
            List<SafetyProblemTracing> safetyProblemTracingList = jsonArray.stream().map(item -> {
                if (!(item instanceof JSONObject)) {
                    throw new IllegalArgumentException("item is not a JSONObject.");
                }
                JSONObject json = (JSONObject) item;
                SafetyProblemTracing safetyProblemTracing = new SafetyProblemTracing();
                safetyProblemTracing.setProblemType(problemTypeEnum.getName());
                safetyProblemTracing.setProblemTypeCode(problemTypeEnum.getProblemTypeCode());
                safetyProblemTracing.setProblemDesc(json.getString("problemDesc"));
                safetyProblemTracing.setSourceType(json.getString("sourceType"));
                safetyProblemTracing.setSourceTypeCode(json.getString("sourceTypeCode"));
                safetyProblemTracing.setSourceId(json.getString("problemSourceId"));
                safetyProblemTracing.setProblemTime(getProblemTime(json));
                safetyProblemTracing.setPrincipalUnit(json.getOrDefault("useUnit", "").toString());
                safetyProblemTracing.setPrincipalUnitCode(json.getOrDefault("useUnitCode", "").toString());
                safetyProblemTracing.setPrincipalUnitType(json.getOrDefault("unitType", "").toString());
                safetyProblemTracing.setGoverningBody(json.getOrDefault("superviseOrgName", "").toString());
                safetyProblemTracing.setGoverningBodyCode(json.getOrDefault("superviseCode", "").toString());
                safetyProblemTracing.setGoverningBodyOrgCode(json.getOrDefault("superviseOrgCode", "").toString());
                safetyProblemTracing.setUserName(ValidationUtil.isEmpty(json.getString("userName")) ? null : json.getString("userName"));
                safetyProblemTracing.setCreateDate(new Date());
                JSONObject region = getRegionBySourceTypeAndId(json.getString("sourceType"), json.getString("problemSourceId"));
                safetyProblemTracing.setRegionName(region.getString("regionName"));
                safetyProblemTracing.setRegionCode(region.getString("regionCode"));
                safetyProblemTracing.setProblemStatus(SafetyProblemStatusEnum.UNHANDLED.getName());
                safetyProblemTracing.setProblemStatusCode(SafetyProblemStatusEnum.UNHANDLED.getCode());
                return safetyProblemTracing;
            }).collect(Collectors.toList());
            safetyProblemTracingService.saveOrUpdateBatchByColumns(safetyProblemTracingList,
                    safetyProblemTracing -> new QueryWrapper<>()
                            .eq("problem_type_code", safetyProblemTracing.getProblemTypeCode())
                            .eq("source_id", safetyProblemTracing.getSourceId())
                            .eq("problem_status_code", SafetyProblemStatusEnum.UNHANDLED.getCode()));
        }
    }

    public static void generatePersonnelProblem(JSONArray jsonArray, SafetyProblemTypeEnum problemTypeEnum, SafetyProblemTracingServiceImpl safetyProblemTracingService) {
        generateProblem2(jsonArray, problemTypeEnum, safetyProblemTracingService);
    }

    public String buildTopic(String topic) {
        String topicPrefix = "$share/" + applicationName;
        return String.format("%s/%s", topicPrefix, topic);
    }

    @PreDestroy
    public void shutdown() {
        if (executorService != null) {
            executorService.shutdown();
        }
    }
}
