package com.steerinfo.meterwork.meterworkmonitor.service.impl;

import com.steerinfo.framework.controller.RESTfulResult;
import com.steerinfo.framework.mapper.IBaseMapper;
import com.steerinfo.framework.service.impl.BaseServiceImpl;
import com.steerinfo.framework.service.pagehelper.PageHelper;
import com.steerinfo.framework.service.pagehelper.PageList;
import com.steerinfo.meterwork.meterworkmonitor.dto.MeterWorkMonitorDto;
import com.steerinfo.meterwork.meterworkmonitor.mapper.MeterWorkMonitorMapper;
import com.steerinfo.meterwork.meterworkmonitor.model.MeterWorkMonitor;
import com.steerinfo.meterwork.meterworkmonitor.rtdb.MeterWorkMonitorRtdbRepository;
import com.steerinfo.meterwork.meterworkmonitor.rtdb.MeterWorkMonitorTupleCodec;
import com.steerinfo.meterwork.meterworkmonitor.service.IMeterWorkMonitorService;
import com.steerinfo.meterwork.meterworkonline.mapper.MeterWorkOnlineMapper;
import com.steerinfo.meterwork.meterworkonline.model.MeterWorkOnline;
import com.steerinfo.meterwork.rtdb.RtdbPoolProperties;
import com.steerinfo.util.FieldsCollector;
import com.steerinfo.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.util.*;

/**
 * MeterWorkMonitor服务实现：
 * @author generator
 * @version 1.0-SNAPSHORT 2020-11-06 03:27
 * 类描述
 * 修订历史：
 * 日期：2020-11-06
 * 作者：generator
 * 参考：
 * 描述：MeterWorkMonitor服务实现
 * @see null
 * @Copyright 湖南视拓信息技术股份有限公司. All rights reserved.
 */
@Service(value = "meterWorkMonitorService")
@Component
@EnableAsync        //开启多线程
public class MeterWorkMonitorServiceImpl extends BaseServiceImpl<MeterWorkMonitor, String> implements IMeterWorkMonitorService {

    private static final Logger log = LoggerFactory.getLogger(MeterWorkMonitorServiceImpl.class);

    @Resource
    private MeterWorkMonitorMapper meterWorkMonitorMapper;

    @Resource
    private MeterWorkOnlineMapper meterWorkOnlineMapper;

    @Autowired(required = false)
    private MeterWorkMonitorRtdbRepository meterWorkMonitorRtdbRepository;

    @Autowired(required = false)
    private RtdbPoolProperties rtdbPoolProperties;

    @Override
    protected IBaseMapper<MeterWorkMonitor, String> getMapper() {
        return meterWorkMonitorMapper;
    }

    @Override
    public RESTfulResult doQueryWf(MeterWorkMonitor oci) {
        RESTfulResult rm = new RESTfulResult();
        try {
            rm.setCode("200");
            PageHelper.startPage(0, 1);
            Map<String, Object> map = FieldsCollector.beanToMap(oci);
            List<MeterWorkMonitor> rows = meterWorkMonitorMapper.selectByParameters(map);
            PageList pageInfo = new PageList(rows);
            rm.setData(pageInfo);
        } catch (Exception ex) {
            rm.setCode("500");
            rm.setMessage("查询失败：" + ex.getMessage());
        }
        return rm;
    }

    @Override
    public String doUpdateOriginl(MeterWorkMonitor model) {
        meterWorkMonitorMapper.updateByPrimaryKeySelective(model);
        return model.getPointNo();
    }

    @Override
    public String doUpdateWf(MeterWorkMonitor model) {
        long start = System.currentTimeMillis();
        try {
            Assert.isTrue(model != null && StringUtils.isNotEmpty(model.getPointNo()), "秤点编号为空");
            boolean rtdbEnabled = isRtdbWriteEnabled();
            log.info("[MonitorRTDB] doUpdateWf service start pointNo={} rtdbEnabled={}",
                    model.getPointNo(), rtdbEnabled);
            if (rtdbEnabled) {
                try {
                    MeterWorkMonitor existing = meterWorkMonitorRtdbRepository.getByPointNo(model.getPointNo());
                    String source;
                    if (existing == null || existing.getPointNo() == null) {
                        existing = meterWorkMonitorMapper.selectByPrimaryKey(model.getPointNo());
                        if (existing == null || existing.getPointNo() == null) {
                            existing = model;
                            source = "request";
                        } else {
                            source = "oracle";
                        }
                    } else {
                        source = "tarantool";
                    }
                    if (!"request".equals(source)) {
                        MeterWorkMonitorTupleCodec.mergeNonNull(existing, model);
                    }
                    log.info("[MonitorRTDB] doUpdateWf replace Tarantool pointNo={} baseSource={} dirty=true",
                            existing.getPointNo(), source);
                    existing.setLastTime(new Date());
                    meterWorkMonitorRtdbRepository.replace(existing, true);
                    log.info("[MonitorRTDB] doUpdateWf Tarantool ok pointNo={} costMs={}",
                            existing.getPointNo(), System.currentTimeMillis() - start);
                    return existing.getPointNo();
                } catch (Exception rtdbEx) {
                    log.error("[MonitorRTDB] doUpdateWf Tarantool failed pointNo={} fallbackOracle={} costMs={}",
                            model.getPointNo(), isFallbackOracle(), System.currentTimeMillis() - start, rtdbEx);
//                    if (isFallbackOracle()) {
//                        meterWorkMonitorMapper.updateByPrimaryKeySelective(model);
//                        log.warn("[MonitorRTDB] doUpdateWf fallback Oracle ok pointNo={} costMs={}",
//                                model.getPointNo(), System.currentTimeMillis() - start);
//                        return model.getPointNo();
//                    }
                    return "";
                }
            }
            log.info("[MonitorRTDB] doUpdateWf write Oracle (rtdb disabled) pointNo={}", model.getPointNo());
            meterWorkMonitorMapper.updateByPrimaryKeySelective(model);
            return model.getPointNo();
        } catch (Exception ex) {
            log.error("[MonitorRTDB] doUpdateWf failed pointNo={} costMs={}",
                    model == null ? null : model.getPointNo(), System.currentTimeMillis() - start, ex);
            return "";
        }
    }

    @Override
    public String updateLastTime(MeterWorkMonitor model) {
        long start = System.currentTimeMillis();
        try {
            Assert.isTrue(Objects.nonNull(model) && StringUtils.isNotEmpty(model.getPointNo()), "秤点编号为空");
            Date now = new Date();
            boolean rtdbEnabled = isRtdbWriteEnabled();
            log.info("[MonitorRTDB] updateLastTime service start pointNo={} rtdbEnabled={} lastTimeMs={}",
                    model.getPointNo(), rtdbEnabled, now.getTime());
            if (rtdbEnabled) {
                try {
                    MeterWorkMonitor existing = meterWorkMonitorRtdbRepository.getByPointNo(model.getPointNo());
                    if (existing == null || existing.getPointNo() == null) {
                        existing = meterWorkMonitorMapper.selectByPrimaryKey(model.getPointNo());
                        if (existing == null || existing.getPointNo() == null) {
                            log.warn("[MonitorRTDB] updateLastTime miss both Tarantool and Oracle pointNo={} costMs={}",
                                    model.getPointNo(), System.currentTimeMillis() - start);
                            return "";
                        }
                        existing.setLastTime(now);
                        log.info("[MonitorRTDB] updateLastTime miss Tarantool, replace from Oracle pointNo={}",
                                model.getPointNo());
                        meterWorkMonitorRtdbRepository.replace(existing, true);
                    } else {
                        log.info("[MonitorRTDB] updateLastTime update Tarantool LAST_TIME pointNo={} lastTimeMs={}",
                                model.getPointNo(), now.getTime());
                        meterWorkMonitorRtdbRepository.updateLastTime(model.getPointNo(), now.getTime());
                    }
                    log.info("[MonitorRTDB] updateLastTime Tarantool ok pointNo={} costMs={}",
                            model.getPointNo(), System.currentTimeMillis() - start);
                    return model.getPointNo();
                } catch (Exception rtdbEx) {
                    log.error("[MonitorRTDB] updateLastTime Tarantool failed pointNo={} fallbackOracle={} costMs={}",
                            model.getPointNo(), isFallbackOracle(), System.currentTimeMillis() - start, rtdbEx);
                    if (isFallbackOracle()) {
                        MeterWorkMonitor tempModel = meterWorkMonitorMapper.selectByPrimaryKey(model.getPointNo());
                        Assert.isTrue(tempModel != null && tempModel.getPointNo() != null, "未查询到秤点信息");
                        tempModel.setLastTime(now);
                        meterWorkMonitorMapper.updateByPrimaryKeySelective(tempModel);
                        log.warn("[MonitorRTDB] updateLastTime fallback Oracle ok pointNo={} costMs={}",
                                tempModel.getPointNo(), System.currentTimeMillis() - start);
                        return tempModel.getPointNo();
                    }
                    return "";
                }
            }
            log.info("[MonitorRTDB] updateLastTime write Oracle (rtdb disabled) pointNo={}", model.getPointNo());
            MeterWorkMonitor tempModel = meterWorkMonitorMapper.selectByPrimaryKey(model.getPointNo());
            Assert.isTrue(tempModel != null && tempModel.getPointNo() != null, "未查询到秤点信息");
            tempModel.setLastTime(now);
            meterWorkMonitorMapper.updateByPrimaryKeySelective(tempModel);
            return tempModel.getPointNo();
        } catch (Exception ex) {
            log.error("[MonitorRTDB] updateLastTime failed pointNo={} costMs={}",
                    model == null ? null : model.getPointNo(), System.currentTimeMillis() - start, ex);
            return "";
        }
    }

    private boolean isRtdbWriteEnabled() {
        return meterWorkMonitorRtdbRepository != null
                && rtdbPoolProperties != null
                && rtdbPoolProperties.getMonitor().isEnabled()
                && !rtdbPoolProperties.getMonitor().isFallbackOracle();
    }

    private boolean isFallbackOracle() {
        return rtdbPoolProperties != null && rtdbPoolProperties.getMonitor().isFallbackOracle();
    }

    @Autowired
    DataSource dataSource;

    @Async
//    @Scheduled(cron = "0/1 * * * * ?") //派位程序定时任务，业务代码打包时启用
    @Override
    public void AutomaticAllocation() {
        long startTime = System.currentTimeMillis();
        //1、随机派位
        RandomAssign();
        //update METER_WORK_MONITOR set IS_TAKE_OVER = '0',USER_NO= ''  where 在线时间>= 40)
        meterWorkMonitorMapper.batchUpdateForNotOnLine();

        //update METER_WORK_STORAGE_ONLINE set WORK_FLAG= '0' where 在线时间>= 40
        meterWorkOnlineMapper.batchUpdateForNotOnLine();

        meterWorkOnlineMapper.batchUpdateForNotWork();

        //4、再次随机派位
        RandomAssign();
        long endTime = System.currentTimeMillis();
        float excTime = (float) (endTime - startTime) / 1000;
        //system.out.println("====================================>执行时间：" + excTime + "s");
        //system.out.println("DATASOURCE = " + dataSource  );
    }

    @Override
    public RESTfulResult weightByCarNo(String rfidCar) {
        RESTfulResult rm = new RESTfulResult();
        rm.setFailed();
        try{
            HashMap<String, Object> map = new HashMap();
            map.put("setCar", rfidCar);
            map.put("rfidCar", rfidCar);
            map.put("photoCar", rfidCar);
            map.put("photoCar2", rfidCar);
            map.put("scanCar", rfidCar);
            map.put("editCar", rfidCar);
            List<MeterWorkMonitor> models = meterWorkMonitorMapper.weightByCarNo(map);
            if(models.size() >= 1) {
                rm.setSucceed();
                rm.setData(models.get(0));
                rm.setMessage("操作成功！！");
                rm.setCode("200");
            }else {
                rm.setMessage("根据车号未查询到相应重量信息,请确认！！");
            }
            return rm;
        }catch(Exception e) {
            e.printStackTrace();
            rm.setMessage("操作异常，请确认！！>>>" + e.getMessage());
            return rm;
        }
    }

    @Override
    public PageList<MeterWorkMonitorDto> queryForPage2(HashMap parmas, Integer pageNum, Integer pageSize) {
        PageHelper.startPage(pageNum, pageSize);
        List<MeterWorkMonitor> rows = meterWorkMonitorMapper.monitorLeftJoinUserSpotConfig(parmas);

        List<MeterWorkMonitorDto> list = new ArrayList<>();
        for (MeterWorkMonitor meterWorkMonitor : rows){
            long time = 45*1000;//45秒
            Date now = new Date();
            Date beforeDate = new Date(now.getTime() - time);//30秒前的时间
            MeterWorkMonitorDto meterWorkMonitorDto = null;
            if (meterWorkMonitor.getLastTime() != null){
                boolean before = meterWorkMonitor.getLastTime().before(beforeDate);
                if (before){
                    meterWorkMonitorDto =  new MeterWorkMonitorDto(meterWorkMonitor,"N");
                }else{
                    meterWorkMonitorDto =  new MeterWorkMonitorDto(meterWorkMonitor,"Y");
                }

            }else {
                meterWorkMonitorDto = new MeterWorkMonitorDto(meterWorkMonitor,"N");

            }
            list.add(meterWorkMonitorDto);
        }

        PageList pageInfo = new PageList(list);
        return pageInfo;
    }

    @Override
    public PageList<MeterWorkMonitorDto> listByLonxi(HashMap parmas, Integer pageNum, Integer pageSize) {
        PageHelper.startPage(pageNum, pageSize);
        List<MeterWorkMonitor> rows = this.getMapper().selectByParameters(parmas);

        List<MeterWorkMonitor> rowsTemp = new ArrayList<>();
        if(rows.size() >= 1) {
            for (MeterWorkMonitor model: rows) {
                if(!model.getPointName().contains("陇西")) {
                    rowsTemp.add(model);
                }
            }
        }

        List<MeterWorkMonitorDto> list = new ArrayList<>();
        for (MeterWorkMonitor meterWorkMonitor : rowsTemp){
            long time = 45*1000;//45秒
            Date now = new Date();
            Date beforeDate = new Date(now.getTime() - time);//30秒前的时间
            MeterWorkMonitorDto meterWorkMonitorDto = null;
            if (meterWorkMonitor.getLastTime() != null){
                boolean before = meterWorkMonitor.getLastTime().before(beforeDate);
                if (before){
                    meterWorkMonitorDto =  new MeterWorkMonitorDto(meterWorkMonitor,"N");
                }else{
                    meterWorkMonitorDto =  new MeterWorkMonitorDto(meterWorkMonitor,"Y");
                }

            }else {
                meterWorkMonitorDto = new MeterWorkMonitorDto(meterWorkMonitor,"N");

            }
            list.add(meterWorkMonitorDto);
        }

        PageList pageInfo = new PageList(list);
        return pageInfo;
    }

    @Override
    public List<MeterWorkMonitor> queryForList() {
        return meterWorkMonitorMapper.selectForList();
    }




    private void RandomAssign() {
        List<MeterWorkMonitor> list2 = meterWorkMonitorMapper.selectForAss();
        if (list2.size() > 0) {
            //2、取最后在线时间与当前时间差不超过10s且状态为未接管的数据
            HashMap<String, Object> obj1 = new HashMap<>();
            obj1.put("duration", 20);//时长
            obj1.put("workFlag", "0");
            List<MeterWorkOnline> list1 = meterWorkOnlineMapper.selectFor(obj1);
            if (list1.size() > 0){
                ArrayList<String> ids = new ArrayList<>();
                for (MeterWorkMonitor list : list2) {
                    Random rand = new Random();
                    int randNum = rand.nextInt(list1.size());
                    //为监控表赋值
                    list.setUserNo(list1.get(randNum).getUserId());
                    //
                    ids.add(list1.get(randNum).getUserId());
                    list1.remove(list1.get(randNum));
                    //
                    list.setIsTakeOver("2");
                }
                meterWorkMonitorMapper.UpdateUserNo(list2);
                meterWorkOnlineMapper.batchUpdateForId(ids);
            }
        }
    }
}
