package com.jjt.biz.service.impl;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.jjt.biz.service.IApiYrService;
import com.jjt.biz.vo.*;
import com.jjt.calc.domain.TwinCalcDayYhj;
import com.jjt.calc.domain.TwinCalcHourEnergy;
import com.jjt.calc.domain.TwinCalcHourYhj;
import com.jjt.calc.service.ITwinCalcDayYhjService;
import com.jjt.calc.service.ITwinCalcHourEnergyService;
import com.jjt.calc.service.ITwinCalcHourYhjService;
import com.jjt.common.constant.CacheConstants;
import com.jjt.common.core.redis.RedisCache;
import com.jjt.common.utils.DateUtils;
import com.jjt.inventory.domain.TwinCalcHourBpOut;
import com.jjt.inventory.service.ITwinCalcHourBpOutService;
import com.jjt.rz.domain.TwinCalcHourRz;
import com.jjt.rz.service.ITwinCalcHourRzService;
import com.jjt.rz.vo.CompareVO;
import com.jjt.ws.domain.TwinRzCalcMonth;
import com.jjt.ws.domain.TwinWorkshop;
import com.jjt.ws.domain.TwinWorkshopEnergy;
import com.jjt.ws.service.ITwinRzCalcMonthService;
import com.jjt.ws.service.ITwinWorkshopService;
import javafx.util.Pair;
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.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * 首页统计数据
 *
 * @author wukai
 * @date 2024/5/4 20:35
 */
@Service
@Slf4j
public class ApiYrServiceImpl implements IApiYrService {
    @Resource
    private RedisCache redisCache;
    @Resource
    private ITwinRzCalcMonthService rzCalcMonthService;
    @Resource
    private ITwinCalcDayYhjService calcDayYhjService;
    @Resource
    private ITwinCalcHourRzService hourRzService;
    @Resource
    private ITwinCalcHourRzService rzService;
    @Resource
    ITwinCalcHourBpOutService bpOutService;
    @Resource
    private ITwinCalcHourEnergyService energyService;
    @Resource
    private ITwinWorkshopService wsService;
    @Resource
    private ITwinCalcHourYhjService yhjService;

    /**
     * 获取产线状态
     *
     * @return 结果
     */
    @Override
    public YrProdLineStatusVO status() {
        YrProdLineStatusVO vo = new YrProdLineStatusVO();
        vo.mock();
        return vo;
    }

    /**
     * 获取生产效率
     *
     * @return 结果
     */
    @Override
    public List<YrProdEfficiencyVO> eff() {
        List<YrProdEfficiencyVO> list = new ArrayList<>();
        YrProdEfficiencyVO[] arr = new YrProdEfficiencyVO[9];
        for (int i = 0; i < 9; i++) {
            YrProdEfficiencyVO vo = new YrProdEfficiencyVO(i);
            vo.mock();
            arr[i] = vo;
        }
        YrProdEfficiencyVO line0 = new YrProdEfficiencyVO(0);
        //处理设备开机数量
        Map<Integer, Pair<Integer, Integer>> deviceStatus = rzService.curr();
        AtomicInteger total = new AtomicInteger();
        AtomicInteger open = new AtomicInteger();
        //处理印花机运行数据
        List<TwinCalcHourYhj> calcList = redisCache.getCacheObject(CacheConstants.YHJ_TODAY);
        Map<Integer, Map<String, Integer>> sumByDeviceAndTeam = calcList.stream()
                .collect(Collectors.groupingBy(
                        TwinCalcHourYhj::getDeviceId,
                        Collectors.groupingBy(
                                TwinCalcHourYhj::getTeam,
                                Collectors.summingInt(TwinCalcHourYhj::getLength)
                        )
                ));
        AtomicInteger openTimes = new AtomicInteger(0);
        sumByDeviceAndTeam.forEach((deviceId, teamMap) -> {
            Pair<Integer, Integer> pair = deviceStatus.get(deviceId);

            Integer lengthA = teamMap.get("A");
            Integer lengthB = teamMap.get("B");
            if (lengthB == null) {
                lengthB = 0;
            }
            Integer length = lengthA + lengthB;
            YrProdEfficiencyVO vo = arr[deviceId];
            if (pair != null) {
                vo.setTotal(pair.getKey());
                vo.setOpen(pair.getValue());
                vo.setRatio(BigDecimal.valueOf(pair.getValue()).multiply(BigDecimal.valueOf(100)).divide(BigDecimal.valueOf(pair.getKey()), 2, RoundingMode.HALF_UP));

                total.addAndGet(pair.getKey());
                // 将当前设备的开机数量累加到总开机数量中
                open.addAndGet(pair.getValue());
            } else {
                vo.setTotal(1);
                vo.setOpen(0);
            }
            if (length > 0) {
                openTimes.incrementAndGet();
            } else {
                vo.setRatio(BigDecimal.ZERO);
                vo.setTimeA(BigDecimal.ZERO);
                vo.setTimeB(BigDecimal.ZERO);
                vo.setEffA(BigDecimal.ZERO);
                vo.setEffB(BigDecimal.ZERO);
            }
            BigDecimal weightA = BigDecimal.valueOf(lengthA).divide(BigDecimal.valueOf(1000), 2, RoundingMode.HALF_UP);
            BigDecimal weightB = BigDecimal.valueOf(lengthB).divide(BigDecimal.valueOf(1000), 2, RoundingMode.HALF_UP);
            BigDecimal weight = BigDecimal.valueOf(length).divide(BigDecimal.valueOf(1000), 2, RoundingMode.HALF_UP);
            vo.setLengthA(BigDecimal.valueOf(lengthA));
            vo.setLengthB(BigDecimal.valueOf(lengthB));
            vo.setWeightA(weightA);
            vo.setWeightB(weightB);
            vo.setWeight(weight);
            vo.setLength(BigDecimal.valueOf(length));

            line0.setEffA(line0.getEffA().add(vo.getEffA()));
            line0.setEffB(line0.getEffB().add(vo.getEffB()));
            line0.setTimeA(line0.getTimeA().add(vo.getTimeA()));
            line0.setTimeB(line0.getTimeB().add(vo.getTimeB()));
            line0.setLengthA(line0.getLengthA().add(vo.getLengthA()));
            line0.setLengthB(line0.getLengthB().add(vo.getLengthB()));
            line0.setWeightA(line0.getWeightA().add(vo.getWeightA()));
            line0.setWeightB(line0.getWeightB().add(vo.getWeightB()));
            line0.setWeight(line0.getWeight().add(vo.getWeight()));
            line0.setLength(line0.getLength().add(vo.getLength()));
        });

        line0.setEffA(line0.getEffA().divide(BigDecimal.valueOf(8), 2, RoundingMode.HALF_UP));
        line0.setEffB(line0.getEffB().divide(BigDecimal.valueOf(8), 2, RoundingMode.HALF_UP));
        line0.setTotal(total.get());
        line0.setOpen(open.get());
        line0.setRatio(BigDecimal.valueOf(open.get()).multiply(BigDecimal.valueOf(100)).divide(BigDecimal.valueOf(total.get()), 2, RoundingMode.HALF_UP));
        list.add(line0);
        for (int i = 1; i < 9; i++) {
            YrProdEfficiencyVO vo = arr[i];
            list.add(vo);
        }
        return list;
    }

    /**
     * 获取能耗趋势
     *
     * @return 结果
     */
    public List<YrEnergyTradeVO> energyTrade(List<TwinRzCalcMonth> list) {
        List<YrEnergyTradeVO> result = new ArrayList<>();
        for (TwinRzCalcMonth cm : list) {
            YrEnergyTradeVO vo = new YrEnergyTradeVO();
            LocalDate ldt = DateUtils.toLocalDate(cm.getDataDate());
            vo.setDate(ldt.getDayOfMonth() + "");
            vo.setTips(ldt.toString());
            vo.split(cm.getLengthPrice(), cm.getDUse(), cm.getQLowUse(), cm.getSUse());
            result.add(vo);
        }
        return result;
    }

    /**
     * 获取生产趋势
     *
     * @return 结果
     */
    public List<YrProdTradeVO> prodTrade(List<TwinRzCalcMonth> list, List<TwinCalcDayYhj> yhjList) {
        Map<Date, List<TwinCalcDayYhj>> yhjMap = yhjList.stream().collect(Collectors.groupingBy(TwinCalcDayYhj::getTime));
        List<YrProdTradeVO> result = new ArrayList<>();
        for (TwinRzCalcMonth cm : list) {
            YrProdTradeVO vo = new YrProdTradeVO();
            LocalDate ldt = DateUtils.toLocalDate(cm.getDataDate());
            List<TwinCalcDayYhj> yhjs = yhjMap.get(cm.getDataDate());
            vo.setDate(ldt.getDayOfMonth() + "");
            vo.setTips(ldt.toString());
            vo.split(yhjs, cm.getLength(), cm.getWeight());
            result.add(vo);
        }
        return result;
    }

    /**
     * 模拟数据定时任务
     */
    @Override
    public void mock() {
        YrTwinVO vo = new YrTwinVO();
        YrProdLineStatusVO status = status();
        vo.setStatus(status);
        List<YrProdEfficiencyVO> effList = eff();
        int open = 0;
        for (YrProdEfficiencyVO eff : effList) {
            if (eff.getLine() == 0) {
                status.setWeight(eff.getWeight().setScale(0, RoundingMode.HALF_UP));
                status.setLength(eff.getLength().setScale(0, RoundingMode.HALF_UP));
                status.setTotal(eff.getTotal());
                status.setOpen(eff.getOpen());
                status.setRatio(eff.getRatio().setScale(2, RoundingMode.HALF_UP));
            } else {
                if (eff.getLength().compareTo(BigDecimal.ZERO) > 0) {
                    open++;
                }
            }
        }
        status.setOpenProd(open);

        //白坯投放量处理--start
        Map<String, Object> result = redisCache.getCacheObject(CacheConstants.VMS_STOCK);
        Map<String, Long> lineMap = (Map<String, Long>) result.get("out-rz-line");
        lineMap.remove("@type"); // 手动移除多余字段
        AtomicReference<Long> baiPei = new AtomicReference<>(0L);
        lineMap.forEach((key, value) -> {
            baiPei.updateAndGet(v -> v + value);
        });
        status.setBaiPei(BigDecimal.valueOf(baiPei.get()));
        //白坯投放量处理--end

        vo.setEff(effList);
        TwinRzCalcMonth calcMonth = new TwinRzCalcMonth();
        Map<String, Object> params = new HashMap<>(16);
        params.put("start", LocalDate.now().minusDays(30).toString());
        calcMonth.setParams(params);
        List<TwinRzCalcMonth> list = rzCalcMonthService.selectTwinRzCalcMonthList(calcMonth);
        TwinCalcDayYhj yhj = new TwinCalcDayYhj();
        yhj.setParams(params);
        List<TwinCalcDayYhj> yhjList = calcDayYhjService.selectTwinCalcDayYhjList(yhj);
        vo.setProdTrade(prodTrade(list, yhjList));
        List<YrEnergyTradeVO> energyList = energyTrade(list);
        vo.setEnergyTrade(energyList);

        List<YrEnergyTradeVO.Line> lines = energyList.get(energyList.size() - 1).getLine();
        for (int i = 0; i < lines.size(); i++) {
            if (i < effList.size()) {
                effList.get(i).setEnergy(lines.get(i).getPrice());
            }
        }
        redisCache.setCacheObject(CacheConstants.RZ_MOCK, vo);
    }


    /**
     * 工艺对比
     *
     * @param reqs 输入参数
     * @return 结果集
     */
    @Override
    public List<CompareVO> compare(List<YrCompareBackReq> reqs, boolean flag) {
        List<CompareVO> result = new ArrayList<>();
        for (YrCompareBackReq req : reqs) {
            CompareVO vo = itemProcessor(req, flag);
            vo.setLength(getTotalLength(req));
            vo.setBp(getBpLength(req));
            vo.setOil(getOil(req));
            result.add(vo);
        }
        return result;
    }


    private Map<String, Object> getParams(YrCompareBackReq req) {
        Map<String, Object> params = new HashMap<>(16);
        params.put("sTime", DateUtils.toDate(req.getStart()));
        params.put("eTime", DateUtils.toDate(req.getEnd()));
        return params;
    }

    /**
     * 获取用油情况
     *
     * @param req 输入参数
     * @return 结果
     */
    private BigDecimal getOil(YrCompareBackReq req) {
        TwinCalcHourEnergy energySearch = new TwinCalcHourEnergy();
        energySearch.setParams(getParams(req));
        List<TwinCalcHourEnergy> list = energyService.selectTwinCalcHourEnergyList(energySearch);
        List<TwinWorkshop> wsList = wsService.selectTwinWorkshopList(new TwinWorkshop());
        //存储能源ID和车间ID关系
        Map<Long, TwinWorkshop> wsMap = new HashMap<>(16);
        wsList.forEach(ws -> {
            List<TwinWorkshopEnergy> energyList = wsService.selectTwinWorkshopByWsId(ws.getWsId()).getTwinWorkshopEnergyList();
            energyList.forEach(o -> wsMap.put(o.getEnergyId(), ws));
        });
        BigDecimal total = BigDecimal.ZERO;
        for (TwinCalcHourEnergy obj : list) {
            obj.setTeam();
            TwinWorkshop ws = wsMap.get(obj.getEnergyId());
            if (ws == null) {
                continue;
            }
            if (ws.getWsCode().contains("WS03-Y")) {
                total = total.add(obj.getDataValue());
            }
        }
        //先直接除以8条产线
        return total.divide(BigDecimal.valueOf(8), 2, RoundingMode.HALF_UP);
    }

    /**
     * 获取白坯投放量
     *
     * @param req 输入参数
     * @return 结果
     */
    private Integer getBpLength(YrCompareBackReq req) {
        //获取印花产量数据
        TwinCalcHourBpOut bpOut = new TwinCalcHourBpOut();
        bpOut.setDeviceId(req.getLine());
        bpOut.setParams(getParams(req));
        List<TwinCalcHourBpOut> yhjList = bpOutService.selectTwinCalcHourBpOutList(bpOut);
        return yhjList.stream()
                .mapToInt(TwinCalcHourBpOut::getLength)
                .sum();
    }

    /**
     * 获取印花产量数据
     *
     * @param req 请求
     * @return 结果
     */
    private Integer getTotalLength(YrCompareBackReq req) {
        //获取印花产量数据
        TwinCalcHourYhj yhjSearch = new TwinCalcHourYhj();
        yhjSearch.setDeviceId(req.getLine());
        yhjSearch.setParams(getParams(req));
        List<TwinCalcHourYhj> yhjList = yhjService.selectTwinCalcHourYhjList(yhjSearch);
        return yhjList.stream()
                .mapToInt(TwinCalcHourYhj::getLength)
                .sum();
    }

    private CompareVO itemProcessor(YrCompareBackReq req, boolean flag) {
        CompareVO vo = new CompareVO();
        vo.setLine(req.getLine());
        vo.setStart(req.getStart());
        vo.setEnd(req.getEnd());
        CompareVO.Qz qz = vo.getQz();
        CompareVO.Hz hz = vo.getHz();
        CompareVO.Yr yr = vo.getYr();
        yr.setYh(10);
        LocalDateTime ldt = req.getEnd().minusHours(1);
        Date date = Date.from(ldt.toLocalDate().atStartOfDay(ZoneOffset.of("+8")).toInstant());
        TwinCalcHourRz search = new TwinCalcHourRz();
        search.setDataDate(date);
        search.setHour(ldt.getHour());
        search.setLine(req.getLine());
        List<TwinCalcHourRz> list = hourRzService.selectTwinCalcHourRzList(search);
        list.forEach(item -> {
            Map<String, Object> map = JSON.parseObject(item.getData(), Map.class);
            map.put("device", item.getDeviceName());
            map.put("online", item.getOnline());
            CompareVO.Para para = new CompareVO.Para();
            para.setName(item.getDeviceName());
            para.setPara(map);
            if ("Forward".equals(item.getWsName())) {
                qzProcessor(qz, item, para);
            } else if ("Back".equals(item.getWsName())) {
                hzProcessor(hz, item, para);
            } else {
                //留给烘固机
                yrProcessor(yr, item, para);
            }
        });
        calcAvg(vo, flag);
        return vo;
    }

    private void calcAvg(CompareVO vo, boolean flag) {
        // 提取公用方法
        Function<Map.Entry<String, Object>, BigDecimal> valueExtractor = entry -> {
            JSONObject jsonObject = JSONObject.parseObject(entry.getValue().toString());
            JSONArray array = jsonObject.getJSONArray("act");
            return array != null && !array.isEmpty() ? array.getJSONObject(0).getBigDecimal("value") : BigDecimal.ZERO;
        };

        // 计算平均值的方法
        BiFunction<List<CompareVO.Para>, Predicate<Map.Entry<String, Object>>, BigDecimal> calculateAvg = (list, filter) ->
                list.stream()
                        .flatMap(item -> item.getPara().entrySet().stream())
                        .filter(filter)
                        .map(valueExtractor)
                        .reduce(BigDecimal.ZERO, BigDecimal::add);

        // 前整车间计算
        List<CompareVO.Para> qzDx = vo.getQz().getDxPara();
        List<CompareVO.Para> qzTg = vo.getQz().getTgPara();

        vo.getQz().setHxAvgTemp(qzDx.isEmpty() ? BigDecimal.ZERO :
                calculateAvg.apply(qzDx, entry -> entry.getKey().contains("烘箱温度"))
                        .divide(new BigDecimal(qzDx.size() * 4), 2, RoundingMode.HALF_UP));

        vo.getQz().setTgAvgTemp(qzTg.isEmpty() ? BigDecimal.ZERO :
                calculateAvg.apply(qzTg, entry -> entry.getKey().contains("烫辊") && entry.getKey().contains("温度"))
                        .divide(new BigDecimal(qzTg.size() * 2), 2, RoundingMode.HALF_UP));

        vo.getQz().setTgAvgSpeed(qzTg.isEmpty() ? BigDecimal.ZERO :
                calculateAvg.apply(qzTg, entry -> entry.getKey().contains("走布速度"))
                        .divide(new BigDecimal(qzTg.size()), 2, RoundingMode.HALF_UP));

        // 后整车间计算
        List<CompareVO.Para> hzDx = vo.getHz().getDxPara();
        List<CompareVO.Para> hzTg = vo.getHz().getTgPara();

        vo.getHz().setHxAvgTemp(hzDx.isEmpty() ? BigDecimal.ZERO :
                calculateAvg.apply(hzDx, entry -> entry.getKey().contains("烘箱温度"))
                        .divide(new BigDecimal(hzDx.size() * 4), 2, RoundingMode.HALF_UP));

        vo.getHz().setTgAvgTemp(hzTg.isEmpty() ? BigDecimal.ZERO :
                calculateAvg.apply(hzTg, entry -> entry.getKey().contains("烫辊") && entry.getKey().contains("温度"))
                        .divide(new BigDecimal(hzTg.size() * 2), 2, RoundingMode.HALF_UP));

        vo.getHz().setTgAvgSpeed(hzTg.isEmpty() ? BigDecimal.ZERO :
                calculateAvg.apply(hzTg, entry -> entry.getKey().contains("走布速度"))
                        .divide(new BigDecimal(hzTg.size()), 2, RoundingMode.HALF_UP));

        if (!flag) {
            vo.getQz().setDxPara(null);
            vo.getQz().setGsPara(null);
            vo.getQz().setSmPara(null);
            vo.getQz().setTgPara(null);
            vo.getHz().setDxPara(null);
            vo.getHz().setGsPara(null);
            vo.getHz().setQmPara(null);
            vo.getHz().setSmPara(null);
            vo.getHz().setTgPara(null);
            vo.getHz().setTjPara(null);
            vo.getYr().setHgPara(null);
        }
    }

    private void qzProcessor(CompareVO.Qz qz, TwinCalcHourRz item, CompareVO.Para para) {
        //前整
        switch (item.getTypeId()) {
            case 2:
                //四节定型机
                if ("Y".equals(item.getOnline())) {
                    qz.setDx(qz.getDx() + 1);
                }
                qz.addDxPara(para);
                break;
            case 3:
                //双棍烫光机
                if ("Y".equals(item.getOnline())) {
                    qz.setTg(qz.getTg() + 1);
                }
                qz.addTgPara(para);
                break;
            case 4:
                //高梳机
                if ("Y".equals(item.getOnline())) {
                    qz.setGs(qz.getGs() + 1);
                }
                qz.addGsPara(para);
                break;
            case 5:
                //双棍刷毛
                if ("Y".equals(item.getOnline())) {
                    qz.setSm(qz.getSm() + 1);
                }
                qz.addSmPara(para);
                break;
            default:
                break;
        }
    }

    private void yrProcessor(CompareVO.Yr yr, TwinCalcHourRz item, CompareVO.Para para) {
        if (8 == item.getTypeId()) {
            yr.addHgPara(para);
        }
    }

    private void hzProcessor(CompareVO.Hz hz, TwinCalcHourRz item, CompareVO.Para para) {
        //后整
        switch (item.getTypeId()) {
            case 6:
                //六节定型机
                if ("Y".equals(item.getOnline())) {
                    hz.setDx(hz.getDx() + 1);
                }
                hz.addDxPara(para);
                break;
            case 3:
                //双棍烫光机
                if ("Y".equals(item.getOnline())) {
                    hz.setTg(hz.getTg() + 1);
                }
                hz.addTgPara(para);
                break;
            case 4:
                //高梳机
                if ("Y".equals(item.getOnline())) {
                    hz.setGs(hz.getGs() + 1);
                }
                hz.addGsPara(para);
                break;
            case 5:
                //双棍刷毛
                if ("Y".equals(item.getOnline())) {
                    hz.setSm(hz.getSm() + 1);
                }
                hz.addSmPara(para);
                break;
            case 7:
                //起毛机
                if ("Y".equals(item.getOnline())) {
                    hz.setQm(hz.getQm() + 1);
                }
                hz.addQmPara(para);
                break;
            case 9:
                //烫剪机
                if ("Y".equals(item.getOnline())) {
                    hz.setTj(hz.getTj() + 1);
                }
                hz.addTjPara(para);
                break;
            default:
                break;
        }
    }

}
