package com.jjt.biz.service.impl;

import com.jjt.biz.domain.TwinDevice;
import com.jjt.biz.service.IApiService;
import com.jjt.biz.service.ITwinDeviceService;
import com.jjt.biz.vo.*;
import com.jjt.calc.domain.TwinCalcDay;
import com.jjt.calc.domain.TwinFormulaInfo;
import com.jjt.calc.service.ITwinCalcDayService;
import com.jjt.calc.service.ITwinFormulaInfoService;
import com.jjt.calc.service.ITwinPanHeadInfoService;
import com.jjt.common.constant.CacheConstants;
import com.jjt.common.core.redis.RedisCache;
import com.jjt.utils.AsyncService;
import com.jjt.utils.IotService;
import com.jjt.utils.Tools;
import com.jjt.ws.domain.TwinWorkshopCalc;
import com.jjt.ws.service.ITwinWorkshopCalcService;
import com.jjt.ws.service.ITwinWorkshopService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.stream.Collectors;

/**
 * 首页统计数据
 *
 * @author wukai
 * @date 2024/5/4 20:35
 */
@Service
@Slf4j
public class ApiServiceImpl implements IApiService {
    @Resource
    private ITwinCalcDayService twinCalcDayService;

    @Resource
    private ITwinPanHeadInfoService panHeadInfoService;
    @Resource
    private ITwinDeviceService deviceService;
    @Resource
    private IotService iotService;
    @Resource
    private AsyncService asyncService;
    @Resource
    private ITwinFormulaInfoService twinFormulaInfoService;
    @Resource
    private RedisCache redisCache;
    @Resource
    private ITwinWorkshopService workshopService;
    @Resource
    private ITwinWorkshopCalcService wsCalcService;

    /**
     * 首页统计数据
     */
    @Override
    public void today() {
        List<WeekData> weekDataList = new ArrayList<>();
        IndexData indexData = new IndexData();
        IndexEfficiency efficiency = new IndexEfficiency();
        TwinCalcDay calcDay = twinCalcDayService.today();
        /*
            获取当天产量数据
         */
        efficiency.setAEfficiency(calcDay.getEfficiencyA().floatValue());
        efficiency.setBEfficiency(calcDay.getEfficiencyB().floatValue());
        efficiency.setTotalLength(calcDay.getLength().floatValue());
        efficiency.setALength(calcDay.getLengthA().floatValue());
        efficiency.setBLength(calcDay.getLengthB().floatValue());
        //界面上重新计算重量 2024-12-25 用总长度除以373
        BigDecimal tw = calcDay.getLength().divide(BigDecimal.valueOf(373), 2, BigDecimal.ROUND_HALF_UP);
        BigDecimal aw = calcDay.getLengthA().divide(BigDecimal.valueOf(373), 2, BigDecimal.ROUND_HALF_UP);
        BigDecimal bw = calcDay.getLengthB().divide(BigDecimal.valueOf(373), 2, BigDecimal.ROUND_HALF_UP);
        efficiency.setTotalWeight(tw.floatValue());
        efficiency.setAWeight(aw.floatValue());
        efficiency.setBWeight(bw.floatValue());
        efficiency.setATime(calcDay.getOpenTimeA().divide(BigDecimal.valueOf(3600), 2, BigDecimal.ROUND_HALF_UP).floatValue());
        efficiency.setBTime(calcDay.getOpenTimeB().divide(BigDecimal.valueOf(3600), 2, BigDecimal.ROUND_HALF_UP).floatValue());
        //如果产量累计产量为0，则稼动和生产时间都为0
        if (efficiency.getALength() == 0f) {
            efficiency.setAEfficiency(0f);
            efficiency.setATime(0f);
        }
        //如果产量累计产量为0，则稼动和生产时间都为0
        if (efficiency.getBLength() == 0f) {
            efficiency.setBEfficiency(0f);
            efficiency.setBTime(0f);
        }
        indexData.setEfficiency(efficiency);

        //7点前是属于前一生产天,所以这里直接减7小时即可
        LocalDateTime ldt = LocalDateTime.now().minusHours(7);
        //获取前面7天的数据
        LocalDate localDate = ldt.toLocalDate().minusDays(7);
//        TwinWorkshop ws = workshopService.selectTwinWorkshopByWsCode("WS02-D");
        Date date = Date.from(localDate.atStartOfDay(ZoneOffset.of("+8")).toInstant());
        List<TwinWorkshopCalc> wsCalcList = wsCalcService.listByWsCodeDate("WS02-D", date);
        //按日期统计用量
        Map<Date, TwinWorkshopCalc> resultMap = wsCalcList.stream().collect(Collectors.toMap(TwinWorkshopCalc::getDataDate, obj -> obj));
        List<TwinCalcDay> list = twinCalcDayService.selectTwinCalcDayListByTime(date);
        Map<Date, List<TwinCalcDay>> dayGroup = list.stream().collect(Collectors.groupingBy(TwinCalcDay::getTime, LinkedHashMap::new, Collectors.toList()));
        for (Map.Entry<Date, List<TwinCalcDay>> entry : dayGroup.entrySet()) {
            TwinCalcDay day = new TwinCalcDay(entry.getKey());
            List<TwinCalcDay> days = entry.getValue();
            day.calcDays(days);
            WeekData weekData = new WeekData();
            weekData.convert(day);
            TwinWorkshopCalc ws = resultMap.get(entry.getKey());
            if (ws != null) {
                weekData.setAKwh(ws.getAValue());
                weekData.setBKwh(ws.getBValue());
                weekData.setKwh(ws.getTotalValue());
            } else {
                weekData.setAKwh(BigDecimal.ZERO);
                weekData.setBKwh(BigDecimal.ZERO);
                weekData.setKwh(BigDecimal.ZERO);
            }
            weekDataList.add(weekData);
        }
        indexData.setWeekData(weekDataList);
        IndexDevice device = new IndexDevice();
        device.setRunningRatio(calcDay.getRunningRatio());
        indexData.setDevice(device);

        redisCache.setCacheObject(CacheConstants.INDEX_CALC, indexData);
    }

    /**
     * 统计当前数据
     */
    @Override
    public void curr() {
        IndexData indexData = new IndexData();
        List<IndexAlarm> alarmList = new ArrayList<>();
        //配方统计数据
        List<FormulaTotal> formulaTotal = new ArrayList<>();
        //配方明细数据
        List<FormulaDetail> formulaDetail = new ArrayList<>();
        //送经量
        List<WarpRunIn> warpList = new ArrayList<>();
        //平方米克重统计数据
        List<GramMass> gramMasses = new ArrayList<>();
        //平方米克重明细数据
        List<GramMassDetail> gramMassDetails = new ArrayList<>();
        iotService.getToken();
        TwinDevice searchDevice = new TwinDevice();
        searchDevice.setOnline("1");
        List<TwinDevice> list = deviceService.selectTwinDeviceList(searchDevice);
        List<Future<Map<String, Object>>> futureList = new ArrayList<>();
        for (int i = 0; i < list.size(); i++) {
            TwinDevice twinDevice = list.get(i);
            futureList.add(asyncService.currData(twinDevice));
        }
        int stop1 = 0, stop2 = 0, stop6 = 0, stop8 = 0, onlineNum = 0, otherStop = 0, stopPan = 0;
        //停机明细
        List<StopDetailVO> stopList = new ArrayList<>();
        //所有设备当前数据
        Map<String, Map<String, Object>> currMap = new HashMap<>(16);
        try {
            for (Future<Map<String, Object>> future : futureList) {
                Map<String, Object> map = future.get();
                TwinDevice device = (TwinDevice) map.get("device");
                //判断是否小经编
                boolean isSmall = device.getDeviceCode().startsWith("C_");
                currMap.put(device.getDeviceCode(), map);
                StopDetailVO sdVO = new StopDetailVO();
                sdVO.setDeviceId(device.getDeviceId());
                sdVO.setDeviceName(device.getDeviceName());
                sdVO.setHasData(true);
                int total = (int) map.get("total");
                if (total == 0) {
                    //可能会出现接口返回无数据的情况,需要设置设备状态为离线
                    sdVO.setHasData(false);
                    stopList.add(sdVO);
                    otherStop++;
                    continue;
                }
                float speed = (float) map.get("Capacity_data_1");
                int stopStatus = (int) map.get("Capacity_data_48");
                Object data4 = map.get("Capacity_data_4");
                if (device.getDeviceCode().startsWith("D_")) {
                    speed = (float) map.get("Capacity_data_117");
                    stopStatus = (int) map.get("Capacity_data_118");
                    data4 = map.get("Capacity_data_110");
                }
                sdVO.setStop(stopStatus);
                sdVO.setSpeed(speed);
                stopList.add(sdVO);

                if (data4 instanceof Integer && (Integer) data4 == 0 || data4 instanceof Float && (Float) data4 == 0f) {
                    //Capacity_data_4,如果设定落布米数为0，则证明当前数据无效
                    //不能跟上面判断合并，不然报空指针
                    continue;
                }

                //停机统计--start
                IndexAlarm stopCalc = new IndexAlarm();
                stopCalc.setCode(device.getDeviceCode());
                stopCalc.setName(device.getDeviceName());
                int alarmType = 0;
                if (speed > 0f || stopStatus == 0) {
                    onlineNum++;
                } else {
                    switch (stopStatus) {
                        case 1:
                            stop1++;
                            alarmType = 10001;
                            break;
                        case 2:
                            stop2++;
                            alarmType = 10002;
                            break;
                        case 6:
                            stop6++;
                            alarmType = 10006;
                            break;
                        case 7:
                            //7-盘头剩余圈数达到停机,表示停机叫料
                            stop8++;
                            alarmType = 10008;
                            break;
                        default:
                            otherStop++;

                    }
                }

                if (alarmType != 0) {
                    stopCalc.setType(alarmType);
                    alarmList.add(stopCalc);
                }
                //停机统计--end

                IndexPan pan = new IndexPan();
                pan.setCode(device.getDeviceCode());
                pan.setName(device.getDeviceName());

                //处理配方明细数据
                FormulaDetail detail = new FormulaDetail(isSmall, map);
                formulaDetail.add(detail);
                //处理平方米克重明细数据
                GramMassDetail gramMassDetail = new GramMassDetail(isSmall, map);
                gramMassDetails.add(gramMassDetail);
                //处理送经量数据
                WarpRunIn warpRunIn = new WarpRunIn(isSmall, map);
                warpList.add(warpRunIn);

                //盘头小于200圈的统计--start
                int len = 5;
                if (!isSmall) {
                    len = 10;
                }
                boolean panFlag = false;
                for (int i = 0; i < len; i++) {
                    int pos = 15 + i;
                    int v = (int) map.get("Capacity_data_" + pos);
                    if (v < 200) {
                        //盘头小于200圈
                        panFlag = true;
                        break;
                    }
                }
                if (panFlag) {
                    stopPan++;
                }
                //盘头小于200圈的统计--end

                if (isSmall) {
                    for (int i = 0; i < Tools.ALLOW_ALARM.length; i++) {
                        //允许的告警编号
                        boolean flag = (boolean) map.get("Alarm_unit_" + Tools.ALLOW_ALARM[i]);
                        if (flag) {
                            IndexAlarm indexAlarm = new IndexAlarm();
                            indexAlarm.setCode(device.getDeviceCode());
                            indexAlarm.setName(device.getDeviceName());
                            indexAlarm.setType(i);
                            alarmList.add(indexAlarm);
                        }
                    }
                }
            }
        } catch (InterruptedException | ExecutionException e) {
            throw new RuntimeException(e);
        }

        indexData.setAlarm(alarmList);
        IndexDevice device = new IndexDevice();
        device.setTotal(list.size());
        device.setOnline(onlineNum);
        device.setStop1(stop1);
        device.setStop2(stop2);
        device.setStop6(stop6);
        device.setStop8(stop8);
        device.setStopPan(stopPan);
//        2024-11-18 告警数改为其他停机数
//        device.setAlarm(alarmList.size());
        device.setAlarm(otherStop);
        BigDecimal ratio = BigDecimal.valueOf(onlineNum * 100L).divide(BigDecimal.valueOf(list.size()), 2, RoundingMode.HALF_UP);
        device.setRunningRatio(ratio.doubleValue());
        indexData.setDevice(device);


        Map<Float, Long> temp = formulaDetail.stream().collect(Collectors.groupingBy(FormulaDetail::getFormula_data_15, Collectors.counting()));
        for (Float v : temp.keySet()) {
            FormulaTotal total = new FormulaTotal();
            total.setHeight(v);
            int num = Math.toIntExact(temp.get(v));
            total.setNum(num);
            float percent = BigDecimal.valueOf(num).divide(BigDecimal.valueOf(list.size()), 2, RoundingMode.HALF_UP).floatValue();
            total.setPercent(percent);
            formulaTotal.add(total);
        }

        temp = gramMassDetails.stream().collect(Collectors.groupingBy(GramMassDetail::getGramMass, Collectors.counting()));
        for (Float v : temp.keySet()) {
            GramMass total = new GramMass();
            total.setGramMass(v);
            int num = Math.toIntExact(temp.get(v));
            total.setNum(num);
            float percent = BigDecimal.valueOf(num).divide(BigDecimal.valueOf(list.size()), 2, RoundingMode.HALF_UP).floatValue();
            total.setPercent(percent);
            gramMasses.add(total);
        }

        redisCache.setCacheObject(CacheConstants.DEVICE_CURRENT, currMap);
        redisCache.setCacheObject(CacheConstants.INDEX_ALARM, indexData);
        redisCache.setCacheObject(CacheConstants.INDEX_FORMULA_TOTAL, formulaTotal);
        redisCache.setCacheObject(CacheConstants.INDEX_FORMULA_DETAIL, formulaDetail);
        redisCache.setCacheObject(CacheConstants.INDEX_WARP_RUN_IN, warpList);
        redisCache.setCacheObject(CacheConstants.INDEX_GRAM_MASS, gramMasses);
        redisCache.setCacheObject(CacheConstants.INDEX_GRAM_MASS_DETAIL, gramMassDetails);
        redisCache.setCacheObject(CacheConstants.STOP_DETAIL, stopList);
    }
}
