package com.sy.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.sy.crossDomain.buildBarCode;
import com.sy.mapper.LandListDao;
import com.sy.model.*;
import com.sy.service.*;
import com.sy.socket.CommandClient;
import com.sy.utils.FileTool;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

import static com.sy.service.impl.ResMessageServiceImpl.toStrArry;

@Slf4j
@Component("GatherInfoHandle")
public class GatherInfoHandle implements GatherInfoService {

    private static GatherInfoHandle gatherInfoHandle;

    //逻辑判断后的返回信息定义
    private static String PERMITTHOUGH =        "直接放行";
    private static String GROWSSEXCETION =      "禁止通行,重量不在可控范围";
    private static String NORECORD =            "车辆未备案或者识别错误,车牌号:";
    private static String NO_CHEPAI =           "未识别到车牌号:";
    private static String INPUTSTATION =        "此车辆未做进站申请";
    private static String ENTERSTATION =        "此车辆未做出站申请";
    private static String ISVALID =             "二维码数据异常,请使用正确的二维码数据";
    private static String ERRORWT =             "出起始场站的重量和进目的场站的重量不一致";
    private static String IEPORSE =             "无相对应进出场申请";
    private static String FANGXING=             "有运单未放行";
    private static String FENBO =               "装载运单的分拨申请舱单未通过校验";
    private static String NOGrossWt =           "过磅重量异常";
    private static String BLACKLIST=            "此车辆已被拉进黑名单,不允许出区";


    @Autowired
    private NmmsService nmmsService;

    /**
     *  车辆信息备案表
     */
    @Autowired
    private LandRoadVeService veService;

    @Autowired
    private RedisService redisService;

    @Autowired
    private BusnesslistinfoService busnesslistinfoService;

    @Resource
    private LandListDao landListDao;

    @Value("${devdebug}")
    private Boolean debug;


    /**
     * 进出场申请表
     */
    @Autowired
    private LandBusListService listService;

    /**
     * 重量校验算法
     */
    @Autowired
    private WeightCheckHandleService weightCheckHandleService;

    /**
     * 查询运单放行表
     */
    @Autowired
    private ResMessageService resMessageService;

    /**
     * 指令日志表
     */
    @Autowired
    private CommandLogService commandLogService;

    /**
     * 车辆过卡信息
     */
    private GatherInfo info;

    /**
     * 车辆流转申请表头信息
     */
    private LandBusinessTypeList landBusinessTypeList;

    private List<LandBusinessTypeList> landBusinessTypeListList;

    /**
     * 车辆申请表体信息
     */
    private List<LAND_BUSINEESTYPE_LIST_INFO> listinfos;



    /**
     *  过磅重量
     */
    private double growssWt=0.0;
    /**
     *  车牌号
     */
    private String vaName;

    /**
     *  车辆备案重量
     */
    private double selfWt=0.0;

    /**
     * 初始化装载货物重量
     */
    private double goodsWt=0.0;
    /**
     * 车辆入场时的重量
     */
    private double inWt=0.0;

    /**
     * 计算后的车辆进出场差值
     */
    private double diffVal=0.0;


    @PostConstruct
    public void init() {
        gatherInfoHandle = this;
    }

    @Override
    public int save(GatherInfo info) {
        return 0;
    }

    @Override
    public void handel(GatherInfo gatherInfo) {
        try {
            this.info = gatherInfo;
            //获取过磅重量
            growssWt = info.getGrosswt().doubleValue();

            //取车牌号,判定卡口是否取到车牌号
            vaName = info.getVename();


                                                //车牌校验
            if (veNameCheck(vaName)
                    && veRecordCheck()          //车辆备案
                    && LandBusinessFormCheck()  //流转申请校验
                    && weightCheck(info)        //过磅重量校验
            ){

                //  场站白名单放行验证
                if (areaWhiteListCheck()){
                    pass();
                    return;
                }
                //入区核放
                if(chanelInCheck()){
                    pass();
                    log.info("[X21-SUCCESS]:[IN]-车辆入区直接放行");
                    return;
                }else{
                    inStationInfo();            //入场信息获取
                    setListinfos();             //流转申请表体获取

                    if (goodsWhiteListCheck()){ //货物类型白名单
                        pass();
                        return;
                    }

                    if(checkRelease()){         //出区核放
                        sendBw(info, true,PERMITTHOUGH,landBusinessTypeList,listinfos);
                        releaseFormCheck();     //流转申请单核销
                    }
                }
            }else{
                log.info("[X21-ERROR]:{}车辆验放失败",info.getVename());
            }


        }catch (Exception e){
            log.error("放行判定异常",e);
            CommandClient.Client(gatherInfo, "放行判定异常");
        }



    }


    /**
     * 一.
     * 场站白名单
     * @return 白名单 true. 其他走校验
     */
    private boolean areaWhiteListCheck(){
        if(FileTool.readProperties("stationsf").equals(info.getAreaid())) {
            return true;
        }
        return false;
    }


    /**
     * 1. 车牌信息校验
     */
    private boolean veNameCheck(String veName){
        //1. 若无车牌号,返回未识别错误
        if (StringUtils.isEmpty(vaName)) {
            log.error(NO_CHEPAI+vaName);
            sendBw(info,false,NO_CHEPAI + vaName,null,listinfos);
            //CommandClient.Client(info, NO_CHEPAI + vaName);
            return false;
        }
        return true;
    }

    /**
     * 2. 车辆备案信息校验
     */
    private boolean veRecordCheck(){
        //2. 校验车辆是否备案
        LandRoadVe ve = gatherInfoHandle.veService.selectByFrameNo(vaName);
        //若无备案信息或者黑名单,返回车辆未备案或者识别错误,
        if (ve == null && !veBlackListCheck(ve)) {
            log.error(NORECORD+vaName);
            sendBw(info,false,NORECORD + vaName,null,listinfos);
            //CommandClient.Client(info, NORECORD + vaName);
            return false;
        }

        //车辆备案重量
        selfWt=Double.parseDouble(ve.getSelfWt());
        return true;

    }

    /**
     * 3. 车辆黑名单校验
     */
    private boolean veBlackListCheck(LandRoadVe ve){
        if("Y".equals(ve.getVeState())){
            log.info(BLACKLIST+vaName);
            sendBw(info,false,BLACKLIST + vaName,null,listinfos);
            return false;
        }
        return true;
    }

    /**
     * 4. 车辆进出场流转申请单信息校验
     * 二维码不用校验了,二维码失效会删除redis缓存
     * @return
     */
    private boolean LandBusinessFormCheck(){
        try {
            log.info("车辆-{}核碰缓存",info.getVename());
            landBusinessTypeListList = new ArrayList<>();
            //通过车牌号,二维码,场站,通道号,进出类型查询进出场站申请列表
            if (info==null){
                log.error("info 实体为NULL");
                return false;
            }

            String landBusinessJson = gatherInfoHandle.redisService.get(info.getVename());
            List<LandBusinessTypeList> list = new ArrayList<>();
            if (StringUtils.isNotEmpty(landBusinessJson)){

                 list =  JSONArray.parseArray(landBusinessJson,LandBusinessTypeList.class);

                landBusinessTypeListList  = list;
                //4. 若查询结果为null,返回无相对应进出场申请
                if (list == null || list.isEmpty()) {
                    log.error(vaName+IEPORSE);
                    sendBw(info,false,vaName+IEPORSE,null,listinfos);
                    return false;
                }

                for (LandBusinessTypeList item : list) {

                    if (item.getAisle().equals(info.getChnlno()) && item.getBarcode().equals(info.getBarcode())){
                        landBusinessTypeList = item;
                        //离场装载货物重量
                        goodsWt = Double.parseDouble(landBusinessTypeList.getRemark());
                        log.info("车辆-{}的申请缓存信息核碰成功,通道:{}",info.getVename(),info.getChnlno());
                        return true;
                    }
                }

                //核碰流转申请失败
                log.info("[CACHE]:车辆-{}的流转申请校验失败,无对应流转申请信息",info.getVename());
            }else {
                log.error("[CACHE-ERROR]:未找到车辆-{}的申请缓存信息",info.getVename());
                log.info("[CACHE-ERROR]:未找到车辆-{}的申请缓存信息,或者流转已超期失效",info.getVename());
                sendBw(info,false,info.getVename()+IEPORSE+",或者流转已超期失效",null,listinfos);
                return false;
            }
        }catch (Exception e){
            e.printStackTrace();
            log.error("[CACHE-PARSE-ERROR]:",e);
            return false;
        }

        return false;
    }




    /**
     * 5. 车辆过磅重量校验
     * @return true 通过 false 不通过
     */
    private boolean weightCheck(GatherInfo info){
        if(info.getGrosswt().compareTo(BigDecimal.ZERO) > 0){
            log.info("过磅重量校验通过,过磅重量大于0");
        }else{
            sendBw(info,false,NOGrossWt + growssWt,new LandBusinessTypeList(),listinfos);
            return false;
        }
        return true;
    }

    /**
     * 流转申请-货物类型白名单,白名单的货物类型目前直接放行
     * @return true 白名单货物直接放行   false 非白名单 需要验放
     */
    private boolean goodsWhiteListCheck(){
        if(!"普通货物".equals(landBusinessTypeList.getCocode())
                && !"转关货物".equals(landBusinessTypeList.getCocode())
                && !"退库货物".equals(landBusinessTypeList.getCocode()) ) {
            //todo:比如货物类型为快件货物,只走重量校验,不走放行信息等校验.
            //todo:退库货物可以关联货运系统,得到退库信息进行比对
            //todo:查验货物走查验信息比对
            //todo:快件暂不校验直接放行,不过得录单
            //todo:换单货物校验逻辑待定
            //todo:未来取消货物类型判定.
            log.info("车辆装载货物为:" + landBusinessTypeList.getCocode());
            return true;
        }
        return false;
    }

    /**
     * todo:通道及金二路由判定,是金二通道的话,等金二X22
     * 目前宽进严出策略,本地判定
     *
     */
    private boolean chanelInCheck(){
        if ("I".equals(info.getIetype())){
            return true;
        }else {
            return false;
        }
    }

    /**
     * 离场时需要获取的车辆进场信息,入场重量,进出场差值
     */
    private void inStationInfo(){
        List<LandBusinessTypeList> stationInChanleInfo= gatherInfoHandle.listService.selectwt(info.getVename(),info.getBarcode(),info.getAreaid(),"I");
        if (stationInChanleInfo.isEmpty()){
            log.info("未查询到车辆:{}的入场信息",info.getVename());
        }else {
            for(LandBusinessTypeList typeList:stationInChanleInfo){
                if(typeList.getAislewt()!=null){
                    //对应场站进场过磅重量
                    inWt=typeList.getAislewt();
                }
                diffVal = inWt- growssWt;
            }

            //TODO:进场校验 增加 车辆备案重量要 <= 进场过磅重量 ,要有误差判定
        }
    }



    /**
     * 此通道对应的流转申请货物重量.
     */
    private void setGoodsWt(LandBusinessTypeList list){

    }



    /**
     * 设置表体全局变量
     */
    private void setListinfos(){
        //查询运单列表
        listinfos=gatherInfoHandle.busnesslistinfoService.selectmanilist(info.getBarcode());
    }

    /**
     * todo:与金二指令一起核碰
     * 出区抬杆放行判定
     * @return true 抬杆放行
     */
    private boolean checkRelease(){
        log.info("[进出场申请]-业务类型为:"+landBusinessTypeList.getCocode()+landBusinessTypeList.getBusinesstype());
        switch (landBusinessTypeList.getBusinesstype()){
            case "空车业务":
                if(gatherInfoHandle.weightCheckHandleService.checkEmpty(growssWt,selfWt)){
                    return true;
                }else{
                    log.error("[空车业务]-出场重量未通过校验:"+GROWSSEXCETION);
                    sendBw(info,false,GROWSSEXCETION,landBusinessTypeList,listinfos);
                    return false;
                }
            case "出口转关":
            case "出口送货":
                if (gatherInfoHandle.weightCheckHandleService.checkExportDownLoading(growssWt, selfWt, goodsWt,inWt)){
                    return true;
                }else{
                    log.error("[出口送货]-出场重量未通过校验:"+GROWSSEXCETION);
                    sendBw(info,false,GROWSSEXCETION,landBusinessTypeList,listinfos);
                    return false;
                }
            case "进口转关":
            case "进口提货":
                if (gatherInfoHandle.weightCheckHandleService.checkImportDlv(growssWt, selfWt, goodsWt,inWt)){
                    if ("退库货物".equals(landBusinessTypeList.getCocode())){
                        /**
                         * 退库货物不校验运单放行.
                         */
                        log.info("[退库业务]-重量核验通过.");
                    }else{
                        //检查运单放行
                        if (gatherInfoHandle.resMessageService.checkManifestRelease(info,listinfos)){
                            return true;
                        }else {
                            //有运单未放行
                            log.error("[进口提货]-出场未通过校验:"+FANGXING+landBusinessTypeList.getMasterList());
                            sendBw(info,false,FANGXING+landBusinessTypeList.getMasterList(),landBusinessTypeList,listinfos);
                            return false;
                        }
                    }
                    //todo:装载运单历史数据累加超重判定
                }else{
                    log.error("[进口提货]-出场重量未通过校验:"+GROWSSEXCETION);
                    sendBw(info,false,GROWSSEXCETION,landBusinessTypeList,listinfos);
                    return false;
                }
                break;
            case "分拨业务":
                if (gatherInfoHandle.weightCheckHandleService.checkAllocateOrDispatch(growssWt, selfWt, goodsWt,inWt)){
                    //todo:检查分拨申请
                    boolean allocatCheck = checkNmmsAllocate(landBusinessTypeList.getMasterList());
                    if(!allocatCheck){
                        log.error("[分拨业务]-分拨申请舱单未通过校验:"+FENBO);
                        sendBw(info,false,FENBO,landBusinessTypeList,listinfos);
                        return false;
                    }
                    return true;
                    //todo:分拨运抵通知,重车入场视为运抵
                }else{
                    log.error("[分拨业务]-出场重量未通过校验:"+GROWSSEXCETION);
                    sendBw(info,false,GROWSSEXCETION,landBusinessTypeList,listinfos);
                    return false;
                }
            case "调拨业务":
                if (gatherInfoHandle.weightCheckHandleService.checkAllocateOrDispatch(growssWt, selfWt, goodsWt,inWt)){
                    //检查运单放行
                    if (gatherInfoHandle.resMessageService.checkManifestRelease(info,listinfos)){
                        return true;
                    }else {
                        //有运单未放行
                        log.error("[调拨业务]-出场未通过校验:"+FANGXING+landBusinessTypeList.getMasterList());
                        sendBw(info,false,FANGXING,landBusinessTypeList,listinfos);
                        return false;
                    }
                    //todo:检查ULD放行.
                }else{
                    log.error("[调拨业务]-出场未通过校验:"+GROWSSEXCETION);
                    sendBw(info,false,GROWSSEXCETION,landBusinessTypeList,listinfos);
                    return false;
                }
            default:
                log.error("[未知业务类型]-出场未通过校验");
                break;
        }

        return false;
    }

    /**
     * 记录进出区信息
     */
    private void record(){
        if ("I".equals(info.getIetype())){
            landBusinessTypeList.setAislewt(info.getGrosswt().doubleValue());
            landBusinessTypeList.setUpdateDate(new Date());
            landBusinessTypeList.setRemark(String.format("%.1f", goodsWt));
            //车辆备案重量
            landBusinessTypeList.setRemark2(String.valueOf(selfWt));
            landBusinessTypeList.setContrastflag("已进站");
        }else {
            landBusinessTypeList.setAislewt(info.getGrosswt().doubleValue());
            landBusinessTypeList.setUpdateDate(new Date());
            //装载货物总重量
            landBusinessTypeList.setRemark(String.format("%.1f", goodsWt));
            //进出差值
            landBusinessTypeList.setRemark1(String.format("%.1f", diffVal));
            landBusinessTypeList.setRemark2(String.valueOf(selfWt));
            landBusinessTypeList.setContrastflag("已出站");
        }

        //todo:判定放行后,插入数据库,出入区记录
        landBusinessTypeList.setId(UUID.randomUUID().toString());
        landBusinessTypeList.setIsvalid("1");
        //todo:这里SEQN也要入库
        gatherInfoHandle.listService.saveList(landBusinessTypeList);
    }

    /**
     * 流转申请是否进行核销判定
     */
    private void releaseFormCheck(){
        List<LandBusinessTypeList> temp =  landBusinessTypeListList;
        //二维码已出区的记录
        List<LandBusinessTypeList> havenCrossList = gatherInfoHandle.listService.selectByBarcodeWithE(info.getBarcode());

        if (landBusinessTypeListList.isEmpty() && havenCrossList.isEmpty()){
            return;
        }

        //申请核销判定,已出区的记录与流转申请数据核碰.如果流转申请列表核碰完,还剩下的需要出区的场站则不核销.
        for (LandBusinessTypeList businessTypeList : havenCrossList) {
            List<LandBusinessTypeList> r =  landBusinessTypeListList.stream().filter(item ->{
                //判断还有没有没有出区的场站,还有没有出区的场站就不核销
                 if(item.getEndstation().equals(businessTypeList.getEndstation())) {
                     return true;
                 }else {
                     return false;
                 }
            })
            .collect(Collectors.toList());
            temp.removeAll(r);
        }

        if (temp.isEmpty()){

            //todo:流转申请状态核销
            log.info("[进出场申请]-流转申请已核销:"+vaName);
            //二维码核销
            releaseBarCode();
        }

    }

    /**
     * 二维码失效核销->判定及失效通知.
     */
    private void releaseBarCode(){
        //二维码判定
        int count=gatherInfoHandle.listService.selectlaststation(info.getVename(),info.getBarcode());
        //二维码失效通知,已进入场站的与缓存比较.
        if(count==0){
            //todo:测试注释掉,二维码释放
            log.info("[进出场申请]-二维码释放:"+vaName);
            if (!gatherInfoHandle.debug){
                buildBarCode.cancleBarCode(vaName);
            }
            int rc = gatherInfoHandle.landListDao.releaseBarcode(info.getBarcode());
            //车辆流转申请缓存删除
           releaseCache();
            log.info("[流转申请]-车辆{}二维码已核销:{}",vaName,info.getBarcode());
        }
    }

    private void releaseCache(){
        //车辆流转申请缓存删除
        gatherInfoHandle.redisService.del(info.getVename());
        //流转申请时生成的临时核碰场站代码列表
        gatherInfoHandle.redisService.del(info.getVename()+"_endstationList");
        //车辆过卡信息缓存删除-X22金二判定时候生成的这个缓存
        gatherInfoHandle.redisService.del(info.getSeqno());
    }

    /**
     * todo:车单关系绑定报文
     */

    /**
     * 发送X22指令
     * @param info          过卡信息
     * @param check         true 抬杆,false 不抬杆
     * @param reason        原因
     * @param land          流转申请表头信息
     * @param list_infos    流转申请表体信息
     * @return              调试模式 直接返回true,生产模式按业务走
     */
    private boolean sendBw(GatherInfo info, boolean check, String reason, LandBusinessTypeList land, List<LAND_BUSINEESTYPE_LIST_INFO> list_infos) {
        //调试模式 直接返回true
        if (gatherInfoHandle.debug){
            if (check) {
                record();
            }
            commandlog(info,check,reason,land,list_infos);
            return check;
        }

        log.info(String.format("开始发送指令:车牌%s,场站%s,通道%s,重量%s",info.getVename(),info.getAreaid(),info.getChnlno(),info.getGrosswt()));
        boolean flag = false;
        if (check) {
            CommandClient.Client(info, PERMITTHOUGH);
            log.info("=============>>>>>>>>放行报文发送成功<<<<<<<<<==============");
            flag = true;
            record();
        } else {
            CommandClient.Client(info, reason);
            log.info("=============>>>>>>>>重量异常报文发送成功<<<<<<<<<==============");
        }
        commandlog(info,check,reason,land,list_infos);
        return flag;
    }

    /**
     * 直接放行
     */
    private void pass(){
        sendBw(info,true,PERMITTHOUGH,landBusinessTypeList,listinfos);
    }

    /**
     * 放行日志记录
     * @param info
     * @param check
     * @param reason
     * @param land
     * @param list_infos
     */
    private void commandlog(GatherInfo info,boolean check,String reason,LandBusinessTypeList land,List<LAND_BUSINEESTYPE_LIST_INFO> list_infos){
        String flag="",type="";
        commandLog command=new commandLog();
        command.setId(UUID.randomUUID().toString());
        command.setBarcode(info.getBarcode());

        if(land!=null){
            command.setBarcode(land.getBarcode());
            command.setBusnessType(land.getBusinesstype());
        }
        command.setAreaId(info.getAreaid());
        command.setChnlNo(info.getChnlno());
        if (check){
            flag = "00";
        } else{
            flag = "11";
        }
        if("I".equals(info.getIetype())){
            type="000000200000000000";
        }else{
            type="000000100000000000";
        }
        command.setReasonCode(flag+type);
        command.setReasonText(reason);
        command.setVeName(info.getVename());
        command.setVeWeight(selfWt);
        command.setIeType(info.getIetype());
        command.setExitGrossWeight(info.getGrosswt().doubleValue());
        command.setInGrossWeight(inWt);
        command.setGoodsWeight(goodsWt);
        command.setActualGoodsWeight(diffVal);
        if(list_infos !=null && list_infos.size()>0){
            command.setMasterList(Arrays.toString(toStrArry(list_infos)));
        }
        gatherInfoHandle.commandLogService.insert(command);
    }

    /**
     * 新舱单分拨申请数据查询
     * @param waybill 查询的运单号
     * @return
     */
    private Map nmmsAllocate(@NotNull String waybill){
        log.info("新舱单查询分拨申请数据开始");
        Feign_Allocate_Search feignAllocateSearch = new Feign_Allocate_Search(waybill,0,10);
        Map map = gatherInfoHandle.nmmsService.getAllocate(feignAllocateSearch);
        log.info(""+map);
        return map;
    }


    /**
     * 分拨申请查询
     * @param waybill
     * @return
     */
    private boolean checkNmmsAllocate(String waybill){
        if (waybill.length() < 1) {
            return false;
        }
        waybill = waybill.replace("-", "");
        //中文逗号替换
        waybill = waybill.replace(",", ",");
        String[] maifest = waybill.split(",");
        log.info("运单列表:" + waybill);
        boolean flag = false;

        for (String awb : maifest) {
            Map map= nmmsAllocate(awb);
            if (map.containsKey("reslut")){
                String result = map.get("reslut").toString();
                JSONObject jsonObject = JSON.parseObject(result);
                if (jsonObject.containsKey("ds")) {
                    JSONArray ds = jsonObject.getJSONArray("ds");
                    JSONObject awbinfo = ds.getJSONObject(0);
                    if (awbinfo.containsKey("RECEIPTINFORMATION")){
                        if (awbinfo.getString("RECEIPTINFORMATION").contains("39301") || awbinfo.getString("RECEIPTINFORMATION").contains("39103")){
                            log.info("运单:{},分拨回执查询结果:{}",awb,awbinfo.getString("RECEIPTINFORMATION"));
                        }else {
                            log.info("运单{}分拨申请回执不正常:{}",awb,awbinfo.getString("RECEIPTINFORMATION"));
                            return false;
                        }
                    }
                }else {
                    log.info("运单:{}分拨申请回执未查询到,或未进行分拨申请",awb);
                    return false;
                }
            }else{
                log.info("运单:{}分拨申请回执未查询到,或未进行分拨申请",awb);
                return false;
            }

        }
        return true;
    }
}