package org.dromara.util;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSON;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONUtil;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import lombok.extern.slf4j.Slf4j;
import org.apache.camel.CamelContext;
import org.apache.camel.Exchange;
import org.apache.camel.ProducerTemplate;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.dromara.common.core.constant.Constants;
import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.json.utils.JsonUtils;
import org.dromara.common.redis.utils.CacheUtils;
import org.dromara.common.redis.utils.RedisUtils;
import org.dromara.netty.NettyChannelHandlerContextMapCache;
import org.dromara.netty.pojo.NettyChannelHandlerContext;
import org.dromara.sms4j.api.universal.RedisUtil;
import org.dromara.system.domain.CountData;
import org.dromara.system.domain.GpsData;
import org.dromara.system.domain.SysDictData;
import org.dromara.system.domain.TblCarInfo;
import org.dromara.system.domain.bo.CountDataBo;
import org.dromara.system.domain.bo.SysDictDataBo;
import org.dromara.system.domain.bo.TblCarInfoBo;
import org.dromara.system.domain.bo.TblCarScheduleBo;
import org.dromara.system.domain.vo.SysDictDataVo;
import org.dromara.system.domain.vo.TblCarInfoVo;
import org.dromara.system.domain.vo.TblCarScheduleVo;
import org.dromara.system.service.*;
import org.springframework.beans.factory.annotation.Autowired;
import cn.hutool.json.JSONObject;
import org.springframework.stereotype.Component;

import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.time.Duration;
import java.util.*;

import static org.dromara.netty.NettyChannelHandlerContextMapCache.ChannelMapCache;
import static org.dromara.netty.NettyChannelHandlerContextMapCache.ChannelMapCache;

@Slf4j
@Component("camelDataUtil")
public class CamelDataUtil {
    @Autowired
    private CamelContext camelContext;

    @Autowired
    ITblGpsDataService gpsDataService;

    @Autowired
    ITblCountDataService countDataService;


    @Autowired
    ITblCarInfoService tblCarInfoService;

    @Autowired
    ITblCarScheduleService tblCarScheduleService;

    @Autowired
    ISysDictDataService sysDictDataService;


    private StandardThreadExecutor threadPoolExecutor=new StandardThreadExecutor();

    public void SendData(String endrouter,Object payload,String headerkey,Object headervalue){
        ProducerTemplate template = camelContext.createProducerTemplate();
        template.sendBodyAndHeader(endrouter,payload,headerkey,headervalue);
    }

    public void processdata(String body, Exchange exchange) throws UnsupportedEncodingException {
        Map<String,String> o = exchange.getMessage().getHeader("mqheader", Map.class);
        exchange.getMessage().setHeader("CamelPahoOverrideTopic","/dev/"+o.get("devtype")+"/0001/");
        exchange.getMessage().setBody(body.getBytes(Charset.forName("utf8")));
//        exchange.getMessage().setBody(body);
    }

    public static void setPlayList(String carNum,Object data)
    {
        try{
            CacheUtils.put("playlist",carNum,data);
        }catch (Exception e){}

    }


    public void processGpsData(GpsData gpsData){

        try{
            this.SendData("direct:senddev", JSONUtil.toJsonStr(gpsData), "devtype","-");

        }catch (Exception e){

        }
        gpsDataService.insertData(gpsData);

        gpsData.setTs(DateUtils.getNowDate());
        //缓存最新数据到redis
        RedisUtils.setCacheObject(StringUtils.format("gps_{}", gpsData.getDevicecode()), gpsData);


        ThreadUtil.execAsync(new Runnable() {
            @Override
            public void run() {

                TblCarInfoBo carInfoBo = new TblCarInfoBo();
                carInfoBo.setDeviceInfos(gpsData.getDevicecode());
                List<TblCarInfoVo> carInfoVos =  tblCarInfoService.queryList(carInfoBo);
                if(carInfoVos.size()>0){
                    TblCarInfoVo carInfoVo = carInfoVos.get(0);
                    if(StringUtils.isNotEmpty(carInfoVo.getCarNum())){
                        //查询车辆信息
                        TblCarInfoBo carInfoBo1 = new TblCarInfoBo();
                        carInfoBo1.setCarNum(carInfoVo.getCarNum());
                        List<TblCarInfoVo> carInfoVos1 =  tblCarInfoService.queryList(carInfoBo1);
                        if(carInfoVos1.size()>0){
                            // 查询车辆信息
                            TblCarInfoVo carInfoVo1 = carInfoVos1.get(0);
                            RedisUtils.setCacheObject(StringUtils.format("gps_{}", carInfoVo1.getId()), gpsData);
                            TblCarScheduleBo tblCarScheduleBo = new TblCarScheduleBo();
                            tblCarScheduleBo.setCarNum(carInfoVo1.getCarNum());
                            Map<String, Object> map = Map.of("scheduleDate", DateUtils.getDate() );
                            tblCarScheduleBo.setParams(map);
                            List<TblCarScheduleVo>  list =  tblCarScheduleService.queryList(tblCarScheduleBo);
                            if(list.size()>0){
                                long t0 = 10;
                                TblCarScheduleVo scheduleVo = null;
                                for (TblCarScheduleVo vo : list) {
                                    if(vo.getScheduleDate().getTime()-DateUtils.getNowDate().getTime()<0){
                                        if(t0>0){
                                            t0 = vo.getScheduleDate().getTime()-DateUtils.getNowDate().getTime();
                                            scheduleVo = vo;
                                        }
                                        if(t0<vo.getScheduleDate().getTime()-DateUtils.getNowDate().getTime()){
                                            t0 = vo.getScheduleDate().getTime()-DateUtils.getNowDate().getTime();
                                            scheduleVo = vo;
                                        }
                                    }
                                }
                                //  查询路线信息
                                if(scheduleVo!=null){
                                    try{
                                        JSONArray lines = JSONUtil.parseObj(scheduleVo.getPathInfo()).getJSONArray("pathLine");
                                        List<JSONObject> lines1 = new ArrayList<>();
                                        String text = SpringUtils.getBean(ISysConfigService.class).selectConfigByKey("sys.car.text");
                                        RedisUtils.setCacheObject(StringUtils.format("car_{}_{}_current_station",carInfoVo1.getCarNum(),DateFormatUtils.format( scheduleVo.getScheduleDate(),DateUtils.YYYYMMDDHHMMSS)),"");
                                        for (JSONObject jsonObject : lines.toList(JSONObject.class)) {
                                            double distance =  DistanceCalculator.calculateDistance(jsonObject.getStr("location"),StringUtils.format("{},{}", gpsData.getLng(),gpsData.getLat()));
                                            String configdistance = SpringUtils.getBean(ISysConfigService.class).selectConfigByKey("sys.car.distance");

                                            if(Double.parseDouble(configdistance)>distance){
                                                String stations = RedisUtils.getCacheObject(StringUtils.format("car_{}_{}_current_station",carInfoVo1.getCarNum(),DateFormatUtils.format( scheduleVo.getScheduleDate(),DateUtils.YYYYMMDDHHMMSS)));
                                                RedisUtils.setCacheObject(StringUtils.format("car_{}_{}_current_station",carInfoVo1.getCarNum(),DateFormatUtils.format( scheduleVo.getScheduleDate(),DateUtils.YYYYMMDDHHMMSS)),stations+","+jsonObject.getStr("stationName"));
                                                //靠近站点 还得计算当前是否跨站点
                                                String stationName =  RedisUtils.getCacheObject(StringUtils.format("car_{}_{}_tip",carInfoVo1.getCarNum(),DateFormatUtils.format( scheduleVo.getScheduleDate(),DateUtils.YYYYMMDDHHMMSS) ));
                                                if(stationName!=null && stationName.contains(jsonObject.getStr("stationName"))){
                                                    //提报过的不再提报  //离开站记录时间

                                                    try {
                                                        if (scheduleVo.getExt1() != null) {
                                                            //进站点 记录时间
                                                            JSONObject datainout = new JSONObject();
                                                            datainout = JSONUtil.parseObj(scheduleVo.getExt1());
                                                            if (datainout.get(jsonObject.getStr("stationName")) != null) {
                                                                JSONObject inout = datainout.getJSONObject(jsonObject.getStr("stationName"));
                                                                if (inout.getStr("outtime") == null) {
                                                                    inout.set("outtime", DateUtil.now());
                                                                }
                                                                datainout.set(jsonObject.getStr("stationName"), inout);
                                                                scheduleVo.setExt1(JSONUtil.toJsonStr(datainout));
                                                                TblCarScheduleBo scheduleVo1 = new TblCarScheduleBo();
                                                                scheduleVo1.setId(scheduleVo.getId());
                                                                scheduleVo1.setExt1(scheduleVo.getExt1());
                                                                tblCarScheduleService.updateByBo(scheduleVo1);
                                                            }
                                                        }
                                                    }catch (Exception e){}
                                                }else{
                                                   lines1.add(jsonObject);
                                                }
                                            }
                                        }

                                        if(lines1.size()>0){
                                            //先判断是否跨站点
                                            if(lines1.size()>1){
                                                lines1.sort(Comparator.comparingInt((JSONObject o) -> o.getInt("order")));
                                            }

                                            JSONObject jsonObject = lines1.get(0);
                                            String stationName =  RedisUtils.getCacheObject(StringUtils.format("car_{}_{}_tip",carInfoVo1.getCarNum(),DateFormatUtils.format( scheduleVo.getScheduleDate(),DateUtils.YYYYMMDDHHMMSS) ));
                                            stationName=stationName==null?"":stationName;
                                            if(!stationName.contains(jsonObject.getStr("stationName"))){
                                                RedisUtils.setCacheObject(StringUtils.format("car_{}_{}_tip",carInfoVo1.getCarNum(), DateFormatUtils.format( scheduleVo.getScheduleDate(),DateUtils.YYYYMMDDHHMMSS) ), stationName+","+jsonObject.getStr("stationName"),Duration.ofSeconds(3600*24));
                                            }
                                           //开始播报
                                            //获取站点设备信息
                                            SysDictDataBo sysDictData =  new SysDictDataBo();
                                            sysDictData.setDictLabel(jsonObject.getStr("stationName"));
                                            sysDictData.setDictType("tbl_sites");
                                            List<SysDictDataVo> stations = sysDictDataService.selectDictDataList(sysDictData);
                                            if(stations.size()>0){
                                                try{
                                                    //进站点 记录时间
                                                    JSONObject datainout = new JSONObject();
                                                    if(scheduleVo.getExt1()!=null){
                                                        datainout = JSONUtil.parseObj(scheduleVo.getExt1());
                                                    }
                                                    JSONObject inout = new JSONObject();
                                                    inout.set("intime", DateUtil.now());
                                                    datainout.set(stations.get(0).getDictLabel(),inout);
                                                    scheduleVo.setExt1(JSONUtil.toJsonStr(datainout));
                                                    TblCarScheduleBo scheduleVo1 = new TblCarScheduleBo();
                                                    scheduleVo1.setId(scheduleVo.getId());
                                                    scheduleVo1.setExt1(scheduleVo.getExt1());
                                                    tblCarScheduleService.updateByBo(scheduleVo1);
                                                }catch (Exception e){}


                                                //满足条件播报（led or 广播）
                                                String message = StringUtils.format(text, carInfoVo1.getCarNum(),stations.get(0).getDictLabel());
                                                if(StringUtils.isNotEmpty(stations.get(0).getRemark())){
                                                    List<JSONObject> devicedatas = JSONUtil.parseArray( stations.get(0).getRemark()).toList(JSONObject.class);
                                                    Map<String,String> o = new HashMap<>();
                                                    o.put("station",stations.get(0).getDictLabel());
                                                    o.put("time",DateUtil.now());
                                                    o.put("type",carInfoVo1.getCarType());
                                                    o.put("carNum",carInfoVo1.getCarNum());
                                                    o.put("msg",message);
                                                    CamelDataUtil.setPlayList(carInfoVo1.getCarNum(),o);
                                                    for (JSONObject i :devicedatas) {
                                                        if(i.getStr("type").equals("led")){
                                                            RedisUtils.setCacheMapValue("station_led",stations.get(0).getDictLabel(),  DateUtil.current());
                                                            MessageUtils.sendLed(i.getStr("code"), message);
                                                        }
                                                        if(i.getStr("type").equals("tts")){
                                                            MessageUtils.sendTTs(i.getStr("code"), message);
                                                        }
                                                    }
                                                }

                                            }

                                        }else{
                                            RedisUtils.setCacheObject(StringUtils.format("car_{}_{}_current_station",carInfoVo1.getCarNum(),DateFormatUtils.format( scheduleVo.getScheduleDate(),DateUtils.YYYYMMDDHHMMSS)),"");
                                        }


                                    }catch (Exception e){
                                        log.error("---------------------");
                                        log.error(e.getMessage());

                                        log.error("---------------------");
                                    }
                                }
                            }else{
                                //无路线无排班车辆
                                if(carInfoVo1.getCarType().equals("1")){
                                    //五座车报站
                                    String configdistance = SpringUtils.getBean(ISysConfigService.class).selectConfigByKey("sys.car5.distance");
                                    String configtime = SpringUtils.getBean(ISysConfigService.class).selectConfigByKey("sys.car5.time");
                                    String text = SpringUtils.getBean(ISysConfigService.class).selectConfigByKey("sys.car5.text");
                                    if(StrUtil.isEmptyIfStr(configdistance)) configdistance="500";
                                    if(StrUtil.isEmptyIfStr(configtime)) configtime="180000";
                                    //查询所有站点经纬
                                    SysDictDataBo bo =new SysDictDataBo();
                                    bo.setDictType("tbl_sites");
                                    List<SysDictDataVo> allstations = sysDictDataService.selectDictDataList(bo);
                                    //循环计算站点与车的距离
                                    for (SysDictDataVo station : allstations){
                                        if(station.getDictValue()!=null && StrUtil.isNotEmpty(station.getDictValue()) && station.getDictValue().contains(",")){
                                            try{
                                                if(DistanceCalculator.calculateDistance(station.getDictValue(),StringUtils.format("{},{}", gpsData.getLng(),gpsData.getLat()))<Integer.parseInt(configdistance)){
                                                    //在范围内
                                                    //查看是否在单位时间内播报过
                                                    Long time = RedisUtils.getCacheObject(StringUtils.format("car_{}_{}_station",carInfoVo1.getCarNum(),station.getDictCode()));
                                                    boolean canbobao = false;
                                                    if(time==null){
                                                        //未报过
                                                        canbobao = true;
                                                    }else{
                                                        if(System.currentTimeMillis()-time<Integer.parseInt(configtime)){
                                                            //在单位时间内播报过
                                                            continue;
                                                        }else{
                                                            //在单位时间内未播报过
                                                            canbobao = true;
                                                        }
                                                    }
                                                    if(canbobao){
                                                        String lastStationCode =  RedisUtils.getCacheObject(StringUtils.format("car_{}_last_bb_station",carInfoVo1.getCarNum()));
                                                        if(lastStationCode!=null && lastStationCode.equals(station.getDictCode().toString())){
                                                            canbobao = false;
                                                        }
                                                    }
                                                    if(canbobao){

                                                        //设置播报站播报时间
                                                        RedisUtils.setCacheObject(StringUtils.format("car_{}_{}_station",carInfoVo1.getCarNum(),station.getDictCode()),DateUtil.current());
                                                        //设置播报站
                                                        RedisUtils.setCacheObject(StringUtils.format("car_{}_last_bb_station",carInfoVo1.getCarNum()),station.getDictCode().toString());

                                                        //满足条件播报（led or 广播）
                                                        String message = StringUtils.format(text, carInfoVo1.getCarNum(),station.getDictLabel());
                                                        if(StringUtils.isNotEmpty(station.getRemark())){
                                                            List<JSONObject> devicedatas = JSONUtil.parseArray( station.getRemark()).toList(JSONObject.class);
                                                            Map<String,String> o = new HashMap<>();
                                                            o.put("station",station.getDictLabel());
                                                            o.put("time",DateUtil.now());
                                                            o.put("carNum",carInfoVo1.getCarNum());
                                                            o.put("type",carInfoVo1.getCarType());
                                                            o.put("msg",message);
                                                            CamelDataUtil.setPlayList(carInfoVo1.getCarNum(),o);
                                                            for (JSONObject i :devicedatas) {
                                                                if(i.getStr("type").equals("led")){
                                                                    RedisUtils.setCacheMapValue("station_led",station.getDictLabel(), DateUtil.current());
                                                                    MessageUtils.sendLed(i.getStr("code"), message);
                                                                }
                                                                if(i.getStr("type").equals("tts")){
                                                                    MessageUtils.sendTTs(i.getStr("code"), message);
                                                                }
                                                            }
                                                        }
                                                    }

                                                }else{
                                                    continue;
                                                }

                                            }catch (Exception e){
                                                log.error("--------5座车计算经纬错误-------------");
                                                log.error(e.getMessage());
                                                log.error("-------5座车计算经纬错误--------------");
                                            }
                                        }

                                    }
                                }

                            }
                        }
                    }
                }
            }
        });

    }
    public void ReciveData(String body, Exchange exchange){
        if(exchange.getMessage().getHeaders().get("CamelMqttTopic").toString().contains("gps")){
            String devicecode = exchange.getMessage().getHeaders().get("CamelMqttTopic").toString().split("/")[3];
            GpsData gpsData = new GpsData();
            gpsData.setDevicecode(devicecode);
            gpsData.setLat(JSONUtil.parseObj(body).getDouble("LAT"));
            gpsData.setLng(JSONUtil.parseObj(body).getDouble("LNG"));
            gpsData.setAlt(JSONUtil.parseObj(body).getDouble("ALT"));
            gpsData.setSpeed(JSONUtil.parseObj(body).getDouble("SPD"));
            if(JSONUtil.parseObj(body).getDouble("SPD")>0){
                processGpsData(gpsData);
            }
        }

        log.info("body:{}",body);
    }



    public void ReciveTcpData(String body, Exchange exchange) throws UnsupportedEncodingException {
        StringBuilder countdatastr= new StringBuilder("");
        JSONArray data= JSONUtil.parseArray(body);
        for (int i = 0; i < data.size(); i++) {
            if( data.get(i) instanceof Integer){
                countdatastr.append((char)(((Integer) data.get(i)).byteValue()&0xff));
            }
        }
        if(StrUtil.isNotEmpty(countdatastr.toString())){
            try{
                this.SendData("direct:senddev", countdatastr.toString(), "devtype","-");
                ProducerTemplate template  = exchange.getContext().createProducerTemplate();
                template.sendBody("direct:reSendTCp",JsonUtils.toJsonString(countdatastr));
            }catch (Exception e){

            }
            try{
                JSONObject countdata = JsonUtils.parseObject(countdatastr.toString(), JSONObject.class);

                String sn = countdata.getStr("sn");
                String type = countdata.getStr("type");
                Long time = countdata.getLong("time");
                try {
                    NettyChannelHandlerContext topic = NettyChannelHandlerContextMapCache.ChannelMapCache.get(((ChannelHandlerContext) (exchange.getIn().getHeader("CamelNettyChannelHandlerContext"))).channel().toString());
                    if (topic != null) {
                        topic.setTag(sn);
                    }
                    RedisUtils.setCacheObject(StringUtils.format("count_{}", sn), DateUtil.now());

                }catch (Exception e){

                }
                Integer in = 0;
                Integer out = 0;
                Long iCreatedTime = 0L;
                if(type.equals("count")){
                    //获取数据in out
                    JSONArray inoutarry =  ((JSONObject)(countdata.getJSONArray("data").get(0))).getJSONArray("count");
                    for (int i = 0; i < inoutarry.size(); i++) {
                        if(((JSONObject)inoutarry.get(i)).getStr("name").equals("d1")){
                            in = ((JSONObject)inoutarry.get(i)).getInt("in");
                            out = ((JSONObject)inoutarry.get(i)).getInt("out");
                            break;
                        }
                    }
                    iCreatedTime = ((JSONObject)(countdata.getJSONArray("data").get(0))).getLong("iCreatedTime");
                    if(iCreatedTime*1000>DateUtil.current()-24*3600*1000  && iCreatedTime*1000<DateUtil.current()+24*3600*1000){
                        //正确的时间
                        CountData countData = new CountData();
                        countData.setIncount(in);
                        countData.setOutcount(out);
                        countData.setDevicecode(sn);
                        countData.setTslong(iCreatedTime*1000);
                        countDataService.insertData(countData);
                        log.info("{}--{}:",sn,JSONUtil.toJsonStr(countData));
                    }else{
                        log.error("{}--{}:时间错误",sn,iCreatedTime);
                        //断开
//                        NettyChannelHandlerContext ctx = ChannelMapCache.get(((ChannelHandlerContext)exchange.getIn().getHeader("CamelNettyChannelHandlerContext")).channel().toString());
//                        ctx.getChannelHandlerContext().close();
//                        return;
                    }
                    //存储数据
//                log.info("{},{},{},{},{}",sn,type,in,out,iCreatedTime);
                }
                //组织返回数据
                JSONObject result = new JSONObject();
                result.put( "sn" , sn) ;
                result.put("type", type) ;
                if(type.equals("heart")){
                    JSONObject sub = new JSONObject() ;
                    sub.put("time", DateUtil.currentSeconds()) ;
                    result.put("data", sub ) ;
                }else{
                    result.put("time", time  ) ;
                }

                NettyChannelHandlerContext ctx = ChannelMapCache.get(((ChannelHandlerContext)exchange.getIn().getHeader("CamelNettyChannelHandlerContext")).channel().toString());
                ByteBuf buf = Unpooled.copiedBuffer(JsonUtils.toJsonString(result).getBytes(CharsetUtil.UTF_8));
                ctx.getChannelHandlerContext().writeAndFlush(buf);

                ProducerTemplate template  = exchange.getContext().createProducerTemplate();
                template.sendBody("direct:reSendTCpO",JsonUtils.toJsonString(result));

            }catch (Exception e){}




        }


    }
//    public static void main(String[] args) {
//        String ss = "{\"pathLine\": [{\"order\": 1, \"location\": \"119.807462,26.958413\", \"planInTime\": \"00:15:00\", \"planOutTime\": \"01:15:00\", \"stationName\": \"松罗乡\"}, {\"order\": 2, \"location\": \"119.723653,26.841206\", \"planInTime\": \"00:30:00\", \"planOutTime\": \"01:15:00\", \"stationName\": \"福安客运站\"}, {\"order\": 3, \"location\": \"119.852881,26.817088\", \"planInTime\": \"01:15:00\", \"planOutTime\": \"04:15:00\", \"stationName\": \"姚澳村\"}], \"driverInfo\": 2, \"attendantInfo\": \"1763485590079639554\"}";
//        JSONArray lines = JSONUtil.parseObj(ss).getJSONArray("pathLine");
//        List<JSONObject> lines1 = lines.toList(JSONObject.class);
//        lines1.sort(Comparator.comparingInt((JSONObject o) -> o.getInt("order")));
//        System.out.println(lines1.get(0).toStringPretty());
//    }
}
