package com.huashe.park.application.engine.impl;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;

import javax.annotation.PostConstruct;

import com.ruoyi.common.core.redis.RedisCache;
import org.apache.commons.lang3.ObjectUtils;
import org.locationtech.jts.geom.Polygon;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.huashe.park.application.engine.EvtFusionEngine;
import com.huashe.park.application.engine.LocationInfo;
import com.huashe.park.application.socket.server.FenceVioEvtSocketServer;
import com.huashe.park.infrastructure.cfg.mqtt.MqttCfg;
import com.huashe.park.common.DateTimeUtil;
import com.huashe.park.common.consts.BDConst;
import com.huashe.park.common.consts.enums.EvtStatus;
import com.huashe.park.common.consts.enums.EvtType;
import com.huashe.park.common.consts.enums.FenceType;
import com.huashe.park.common.geo.GeoUtils;
import com.huashe.park.core.service.IBdFenceInfoService;
import com.huashe.park.core.service.IBdFenceVioEvtService;
import com.huashe.park.domain.entity.BdFenceInfo;
import com.huashe.park.domain.entity.BdFenceVioEvt;

import net.dreamlu.iot.mqtt.spring.client.MqttClientTemplate;

@Service
@ConditionalOnBean(MqttClientTemplate.class)
public class FenceBreakInEngine extends EvtFusionEngine {
    private static final Logger logger = LoggerFactory.getLogger(FenceBreakInEngine.class);

    @Autowired
    private IBdFenceInfoService fenceInfoService;

    @Autowired
    private IBdFenceVioEvtService vioEvtService;

    @Autowired
    private RedisCache redisService;

    @Autowired
    private MqttClientTemplate client;

    /**
     * The constant MAX_EVT_TIME_GAP. 单位：秒
     *
     * @author chen.cheng
     */
    private static final int MAX_EVT_TIME_GAP = 300;

    @Autowired
    private FenceVioEvtSocketServer fenceVioEvtSocketServer;

    private final List<LinkedBlockingQueue<JSONObject>> messageQueueList = new ArrayList<>(5);

    @PostConstruct
    public void init() {
        super.init();
        this.setEngineName("室外围栏闯禁事件融合");
        if (!redisService.hasKey(BDConst.REDIS_KEY.FENCE)) {
            List<BdFenceInfo> bdFenceInfos = fenceInfoService.selectBdFenceInfoList(new BdFenceInfo() {
                {
                    setFenceType(FenceType.OUT_SIDE_FENCE_IN.getCode());
                }
            });
            if (CollectionUtils.isEmpty(bdFenceInfos)) {
                return;
            }
            bdFenceInfos.removeIf(item -> ObjectUtils.isEmpty(item.getPoly())
                || ObjectUtils.isEmpty(GeoUtils.getPolygon(item.getPoly())));

            redisService.setCacheList(BDConst.REDIS_KEY.FENCE, bdFenceInfos);
        }
    }

    @Override
    public Boolean check(LocationInfo older, LocationInfo newer) {
        long gap = newer.getSrcTimestamp() - older.getSrcTimestamp();
        // 将gap 转换为秒
        gap = gap / 1000;

        String oldFenceId = older.getMsg().getString("fenceId");
        String newerFenceId = newer.getMsg().getString("fenceId");
        return gap <= MAX_EVT_TIME_GAP && oldFenceId.equals(newerFenceId);
    }

    @Override
    public List<LinkedBlockingQueue<JSONObject>> getQueue() {
        return messageQueueList;
    }

    @Override
    public String getKey(LocationInfo msg) {
        return BDConst.REDIS_KEY.FENCE_BREAK_IN_KEY + msg.getMsg().getString("deviceId");
    }

    @Override
    public void getBizId(LocationInfo msg) {
        BdFenceVioEvt bdFenceVioEvt = new BdFenceVioEvt();
        bdFenceVioEvt.setFenceId(msg.getMsg().getLong("fenceId"));
        bdFenceVioEvt.setLat(msg.getLatitude());
        bdFenceVioEvt.setLng(msg.getLongitude());
        bdFenceVioEvt.setEvtStatus(EvtStatus.NEW.getCode());
        bdFenceVioEvt.setEvtType(EvtType.FENCE_IN.getCode());
        bdFenceVioEvt.setEvtTime(DateTimeUtil.getDateFromMills(msg.getSrcTimestamp()));
        bdFenceVioEvt.setEvtDesc(String.format("围栏闯禁: %s", msg.getMsg().getString("fenceName")));
        vioEvtService.insertBdFenceVioEvt(bdFenceVioEvt);
        msg.setBizId(bdFenceVioEvt.getId().toString());
    }

    @Override
    public void processOlderData(LocationInfo msg) {
        vioEvtService.updateBdFenceVioEvt(new BdFenceVioEvt() {
            {
                setFenceId(msg.getMsg().getLong("fenceId"));
                setEvtStatus(EvtStatus.DISAPPEAR.getCode());
            }
        });
    }

    @Override
    public void newEvtCallback(LocationInfo msg) {
        fenceVioEvtSocketServer.broadcast(msg);
    }

    public void generateEvt(JSONObject msg, byte[] payload) {
        List<BdFenceInfo> cacheList = redisService.getCacheList(BDConst.REDIS_KEY.FENCE);
        if (CollectionUtils.isEmpty(cacheList)) {
            return;
        }
        Polygon polygon;
        logger.info("fence info size: {}", msg);
        for (BdFenceInfo fenceInfo : cacheList) {
            polygon = GeoUtils.getPolygon(fenceInfo.getPoly());
            if (GeoUtils.isPointInGeoFence(polygon, msg.getString("longitude"), msg.getString("latitude"))) {
                logger.info("?>>>>>fence info size: {}", msg);
                msg.put("fenceId", fenceInfo.getId());
                msg.put("fenceName", fenceInfo.getDefenceName());
                client.publish(BDConst.MQTT_TOPIC.EVT_LOCATION_TOPIC, JSON.toJSONBytes(msg));
                break;
            }
        }
    }
}
