package com.jjt.inventory.controller;

import com.jjt.common.core.controller.BaseController;
import com.jjt.common.core.domain.R;
import com.jjt.common.core.redis.RedisCache;
import com.jjt.common.utils.DateUtils;
import com.jjt.common.utils.StringUtils;
import com.jjt.inventory.domain.TwinCalcDayBp;
import com.jjt.inventory.domain.TwinCalcDayPt;
import com.jjt.inventory.domain.TwinCalcHourBp;
import com.jjt.inventory.domain.TwinCalcHourPt;
import com.jjt.inventory.service.*;
import com.jjt.inventory.vo.*;
import com.jjt.utils.MssqlService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.function.IntFunction;
import java.util.stream.Collectors;

/**
 * @author ruoyi
 */
@Api(tags = "仓库分析数据接口")
@RestController
@Slf4j
@RequestMapping("/inventory")
public class InventoryController extends BaseController {
    @Resource
    private RedisCache redisCache;
    @Resource
    private IInventoryService inventoryService;
    @Resource
    private MssqlService mssqlService;
    @Resource
    private ITwinCalcHourBpService hourBpService;
    @Resource
    private ITwinCalcHourPtService hourPtService;
    @Resource
    private ITwinCalcDayPtService dayPtService;
    @Resource
    private ITwinCalcDayBpService dayBpService;


    @ApiOperation("盘头库库存分析")
    @GetMapping("/pt")
    @CrossOrigin(origins = "*")
    @ResponseBody
    public R<PtVO> pt() {
        PtVO ptVO = new PtVO();
        //当前库TOP10 统计
        String sql = "SELECT TOP 10 SKU,SKU_DESCRC,LOT01 FROM V_WMS_STOCK WHERE WAREHOUSE_ID='WH03-盘头仓库' AND UOM='个' AND LOT01 IS NOT NULL AND LOT01!='' ORDER BY LOT01";
        List<TopVO> tops = topProcess(sql);
        ptVO.setTops(tops);
        //当前库存明细
        sql = "SELECT * FROM V_WMS_STOCK WHERE WAREHOUSE_ID='WH03-盘头仓库' ORDER BY LOT01";
        List<PtStockDetailVO> detailVOList = detailProcess(sql);
        Map<String, List<PtStockDetailVO>> detailMap = detailVOList.stream().collect(Collectors.groupingBy(PtStockDetailVO::getSkuDesc));
        //30天统计
        List<TwinCalcDayPt> calcDayList = dayPtService.selectStartDate(LocalDateTime.now().minusHours(7).minusDays(30).toLocalDate());
        calcDayList = calcDayList.stream().sorted(Comparator.comparing(TwinCalcDayPt::getTime)).collect(Collectors.toList());
        calcDayList.stream()
                .map(day -> {
                    PtVO.Usage usage = new PtVO.Usage();
                    usage.setMax(day.getMaxNum());
                    usage.setRate(day.getRate());
                    usage.setDate(DateUtils.toLocalDate(day.getTime()));
                    Integer hour = Integer.parseInt(day.getData());
                    List<TwinCalcHourPt> hourPtList = hourPtService.selectTwinCalcHourPtByHour(day.getTime(), hour);
                    hourPtList.stream().filter(item -> item.getNum() > 0).sorted(Comparator.comparing(TwinCalcHourPt::getNum).reversed()).forEach(hourPt -> {
                        PtVO.YarnTypeDetail detail = new PtVO.YarnTypeDetail();
                        detail.setName(hourPt.getName());
                        detail.setNum(hourPt.getNum());
                        BigDecimal ratio = new BigDecimal(hourPt.getNum()).divide(new BigDecimal(day.getMaxNum()), 4, RoundingMode.HALF_UP);
                        detail.setRatio(ratio);
                        usage.getDetails().add(detail);
                    });
                    return usage;
                }).forEach(ptVO.getUsages()::add);
        //当日统计
        Map<String, PtVO.Stock> stats = new HashMap<>(16);
        List<TwinCalcHourPt> hourPtList = hourPtService.selectTwinCalcHourPtByDay(LocalDateTime.now().minusHours(7).toLocalDate());
        hourPtList.forEach(pt -> {
            PtVO.Stock stock = stats.computeIfAbsent(pt.getName(), k -> new PtVO.Stock());
            stock.setName(pt.getName());
            stock.addIn(pt.getInNum());
            stock.addOut(pt.getOutNum());
            stock.updateMax(pt.getNum());
            stock.updateMin(pt.getNum());
            stock.total(pt.getNum());
            List<PtStockDetailVO> list = detailMap.getOrDefault(pt.getName(), Collections.emptyList());
            stock.setCurr(list.size());
            Map<PtStockVO, List<PtStockDetailVO>> groupedDetailMap = list.stream()
                    .collect(Collectors.groupingBy(detail ->
                            new PtStockVO(detail.getSkuClass(), detail.getSku(), detail.getSkuDesc(), detail.getLot13(), detail.getLot15())
                    ));
            List<PtStockVO> calcList = new ArrayList<>();
            groupedDetailMap.forEach((key, value) -> {
                if ("容器类".equals(key.getSkuClass())) {
                    stock.setName("(空盘头)" + pt.getName());
                }
                key.setDetails(value);
                key.setNum(value.size());
                calcList.add(key);
            });
            stock.setCalc(calcList);
        });
        long hours = hourPtList.stream().map(TwinCalcHourPt::getHour).distinct().count();
        stats.values().forEach(stock -> stock.calcAvg((int) hours));

        ptVO.getStocks().addAll(stats.values());
        ptVO.getStocks().sort(Comparator.comparing(PtVO.Stock::getCurr).reversed());

        return R.ok(ptVO);
    }

    @ApiOperation("白柸布库存分析")
    @GetMapping("/bp")
    @CrossOrigin(origins = "*")
    @ResponseBody
    public R<BpVO> bp() {
        BpVO bpVO = new BpVO();
        //当前库统计
        String sql = "SELECT TOP 10 SKU,SKU_DESCRC,LOT01 FROM V_WMS_STOCK WHERE  WAREHOUSE_ID='WH04-白坯仓库' AND UOM='KG' AND LOT01 IS NOT NULL AND LOT01!='' ORDER BY LOT01";
        List<TopVO> tops = topProcess(sql);
        bpVO.setTops(tops);
        //30天统计
        List<TwinCalcDayBp> calcDayList = dayBpService.selectStartDate(LocalDateTime.now().minusHours(7).minusDays(30).toLocalDate());
        calcDayList.stream().sorted(Comparator.comparing(TwinCalcDayBp::getTime)).forEach(day -> {
            Integer rollMaxHour = Integer.parseInt(day.getRollData().split(",")[0]);
            Integer rollMinHour = Integer.parseInt(day.getRollData().split(",")[1]);
            Integer meterMaxHour = Integer.parseInt(day.getMeterData().split(",")[0]);
            Integer meterMinHour = Integer.parseInt(day.getMeterData().split(",")[1]);
            Integer weightMaxHour = Integer.parseInt(day.getWeightData().split(",")[0]);
            Integer weightMinHour = Integer.parseInt(day.getWeightData().split(",")[1]);
            int[] hours = Arrays.stream(new int[]{rollMaxHour, rollMinHour, meterMaxHour, meterMinHour, weightMaxHour, weightMinHour}).distinct().toArray();
            Map<Integer, List<TwinCalcHourBp>> hourMap = hourBpService.selectTwinCalcHourBpByHours(day.getTime(), hours);
            IntFunction<BpVO.YarnTypeDetail> createDetail = mick -> {
                BpVO.YarnTypeDetail detail = new BpVO.YarnTypeDetail();
                detail.setName("米克重（" + mick + "）");
                return detail;
            };

            // 米数库存
            BpVO.MonthStock meterStock = new BpVO.MonthStock();
            meterStock.setMax(day.getMeterMax());
            meterStock.setMin(day.getMeterMin());
            meterStock.setAvg(day.getMeterAvg());
            meterStock.setDate(DateUtils.toLocalDate(day.getTime()));

            hourMap.get(meterMaxHour).stream()
                    .filter(item -> item.getMeterNum() > 0)
                    .sorted(Comparator.comparing(TwinCalcHourBp::getMeterNum).reversed())
                    .collect(Collectors.groupingBy(TwinCalcHourBp::getMick))
                    .forEach((mick, list) -> {
                        int meterNum = list.stream().mapToInt(TwinCalcHourBp::getMeterNum).sum();
                        BpVO.YarnTypeDetail detail = createDetail.apply(mick);
                        detail.setNum(meterNum);
                        detail.setRatio(new BigDecimal(meterNum).divide(new BigDecimal(day.getMeterMax()), 4, RoundingMode.HALF_UP));
                        meterStock.getDetails().add(detail);
                    });

            hourMap.get(meterMinHour).stream()
                    .filter(item -> item.getMeterNum() > 0)
                    .collect(Collectors.groupingBy(TwinCalcHourBp::getMick))
                    .forEach((mick, list) -> {
                        int meterNum = list.stream().mapToInt(TwinCalcHourBp::getMeterNum).sum();
                        BpVO.YarnTypeDetail detail = createDetail.apply(mick);
                        detail.setNum(meterNum);
                        detail.setRatio(new BigDecimal(meterNum).divide(new BigDecimal(day.getMeterMin()), 4, RoundingMode.HALF_UP));
                        meterStock.getMinDetails().add(detail);
                    });
            meterStock.getDetails().sort(Comparator.comparing(BpVO.YarnTypeDetail::getNum).reversed());
            meterStock.getMinDetails().sort(Comparator.comparing(BpVO.YarnTypeDetail::getNum).reversed());
            bpVO.getMeterMonthStocks().add(meterStock);

            // 重量库存
            BpVO.MonthStock weightStock = new BpVO.MonthStock();
            weightStock.setMax(day.getWeightMax());
            weightStock.setMin(day.getWeightMin());
            weightStock.setAvg(day.getWeightAvg());
            weightStock.setDate(DateUtils.toLocalDate(day.getTime()));
            hourMap.get(weightMaxHour).stream()
                    .filter(item -> item.getWeightNum() > 0)
                    .collect(Collectors.groupingBy(TwinCalcHourBp::getMick))
                    .forEach((mick, list) -> {
                        int weightNum = list.stream().mapToInt(TwinCalcHourBp::getWeightNum).sum();
                        BpVO.YarnTypeDetail detail = createDetail.apply(mick);
                        detail.setNum(weightNum);
                        detail.setRatio(new BigDecimal(weightNum).divide(new BigDecimal(day.getWeightMax()), 4, RoundingMode.HALF_UP));
                        weightStock.getDetails().add(detail);
                    });

            hourMap.get(weightMinHour).stream()
                    .filter(item -> item.getWeightNum() > 0)
                    .collect(Collectors.groupingBy(TwinCalcHourBp::getMick))
                    .forEach((mick, list) -> {
                        int weightNum = list.stream().mapToInt(TwinCalcHourBp::getWeightNum).sum();
                        BpVO.YarnTypeDetail detail = createDetail.apply(mick);
                        detail.setNum(weightNum);
                        detail.setRatio(new BigDecimal(weightNum).divide(new BigDecimal(day.getWeightMin()), 4, RoundingMode.HALF_UP));
                        weightStock.getMinDetails().add(detail);
                    });
            weightStock.getDetails().sort(Comparator.comparing(BpVO.YarnTypeDetail::getNum).reversed());
            weightStock.getMinDetails().sort(Comparator.comparing(BpVO.YarnTypeDetail::getNum).reversed());
            bpVO.getWeightMonthStocks().add(weightStock);

            // 卷数库存
            BpVO.MonthStock rollStock = new BpVO.MonthStock();
            rollStock.setMax(day.getRollMax());
            rollStock.setMin(day.getRollMin());
            rollStock.setAvg(day.getRollAvg());
            rollStock.setDate(DateUtils.toLocalDate(day.getTime()));

            hourMap.get(rollMaxHour).stream()
                    .filter(item -> item.getRollNum() > 0)
                    .sorted(Comparator.comparing(TwinCalcHourBp::getRollNum).reversed())
                    .collect(Collectors.groupingBy(TwinCalcHourBp::getMick))
                    .forEach((mick, list) -> {
                        int rollNum = list.stream().mapToInt(TwinCalcHourBp::getRollNum).sum();
                        BpVO.YarnTypeDetail detail = createDetail.apply(mick);
                        detail.setNum(rollNum);
                        detail.setRatio(new BigDecimal(rollNum).divide(new BigDecimal(day.getRollMax()), 4, RoundingMode.HALF_UP));
                        rollStock.getDetails().add(detail);
                    });
            hourMap.get(rollMinHour).stream()
                    .filter(item -> item.getRollNum() > 0)
                    .sorted(Comparator.comparing(TwinCalcHourBp::getRollNum).reversed())
                    .collect(Collectors.groupingBy(TwinCalcHourBp::getMick))
                    .forEach((mick, list) -> {
                        int rollNum = list.stream().mapToInt(TwinCalcHourBp::getRollNum).sum();
                        BpVO.YarnTypeDetail detail = createDetail.apply(mick);
                        detail.setNum(rollNum);
                        detail.setRatio(new BigDecimal(rollNum).divide(new BigDecimal(day.getRollMin()), 4, RoundingMode.HALF_UP));
                        rollStock.getMinDetails().add(detail);
                    });
            rollStock.getDetails().sort(Comparator.comparing(BpVO.YarnTypeDetail::getNum).reversed());
            rollStock.getMinDetails().sort(Comparator.comparing(BpVO.YarnTypeDetail::getNum).reversed());
            bpVO.getRollMonthStocks().add(rollStock);


//            for (BpVO.MonthStock stock : new BpVO.MonthStock[]{meterStock, weightStock, rollStock}) {
//                int totalNum = 0;
//                int yarnCount = 3 + (int) (Math.random() * 8);
//                // 随机3-10个纱种
//                List<BpVO.YarnTypeDetail> details = new java.util.ArrayList<>();
//
//                // 先设置数量并计算总数
//                for (int j = 1; j <= yarnCount; j++) {
//                    BpVO.YarnTypeDetail detail = new BpVO.YarnTypeDetail();
//                    detail.setName("纱种" + j);
//                    detail.setNum(50 + (int) (Math.random() * 100));
//                    // 随机50-150的数量
//                    totalNum += detail.getNum();
//                    details.add(detail);
//                }
//
//                // 根据数量计算比例
//                for (BpVO.YarnTypeDetail detail : details) {
//                    BigDecimal ratio = new BigDecimal(detail.getNum()).divide(new BigDecimal(totalNum), 2, RoundingMode.HALF_UP);
//                    detail.setRatio(ratio);
//                    stock.getDetails().add(detail);
//                }
//            }
        });

        //当日统计，按米克重统计
        Map<Integer, BpVO.Stock[]> stats = new HashMap<>(16);
        List<TwinCalcHourBp> hourBpList = hourBpService.selectTwinCalcHourPtByDay(LocalDateTime.now().minusHours(7).toLocalDate());
        hourBpList.forEach(bp -> {
            BpVO.Stock[] stock = stats.computeIfAbsent(bp.getMick(), k -> {
                BpVO.Stock[] newStock = new BpVO.Stock[3];
                newStock[0] = new BpVO.Stock(); // 卷数
                newStock[1] = new BpVO.Stock(); // 米数
                newStock[2] = new BpVO.Stock(); // 重量
                return newStock;
            });
            // 处理卷数统计
            processStock(stock[0], bp.getMick(), bp.getRollIn(), bp.getRollOut(), bp.getRollNum());
            // 处理米数统计
            processStock(stock[1], bp.getMick(), bp.getMeterIn(), bp.getMeterOut(), bp.getMeterNum());
            // 处理重量统计
            processStock(stock[2], bp.getMick(), bp.getWeightIn(), bp.getWeightOut(), bp.getWeightNum());
        });

        long hours = hourBpList.stream().map(TwinCalcHourBp::getHour).distinct().count();

        // 使用流式处理优化计算和分类
        List<BpVO.Stock> rollStocks = new ArrayList<>();
        List<BpVO.Stock> meterStocks = new ArrayList<>();
        List<BpVO.Stock> weightStocks = new ArrayList<>();

        stats.values().stream().forEach(stockArray -> {
            stockArray[0].calcAvg((int) hours);
            stockArray[1].calcAvg((int) hours);
            stockArray[2].calcAvg((int) hours);

            rollStocks.add(stockArray[0]);
            meterStocks.add(stockArray[1]);
            weightStocks.add(stockArray[2]);
        });

        rollStocks.sort(Comparator.comparing(BpVO.Stock::getMick).reversed());
        meterStocks.sort(Comparator.comparing(BpVO.Stock::getMick).reversed());
        weightStocks.sort(Comparator.comparing(BpVO.Stock::getMick).reversed());
        bpVO.setRollStocks(rollStocks);
        bpVO.setMeterStocks(meterStocks);
        bpVO.setWeightStocks(weightStocks);
        return R.ok(bpVO);
    }

    /**
     * 盘头库存TOP排名处理
     *
     * @return 结果集
     */
    private List<TopVO> topProcess(String sql) {
        List<Map<String, Object>> tops = mssqlService.executeQuery(sql);
        return tops.stream().map(map -> {
            TopVO top = new TopVO();
            String sku = (String) map.get("SKU");
            String skuDesc = (String) map.get("SKU_DESCRC");
            String dateStr = (String) map.get("LOT01");
            top.setName(skuDesc + "（" + sku + "）");
            LocalDate date = LocalDate.parse(dateStr);
            top.setDay((int) ChronoUnit.DAYS.between(date, LocalDate.now()));
            top.setDate(date);
            return top;
        }).collect(Collectors.toList());
    }

    /**
     * 盘头库存TOP排名处理
     *
     * @return 结果集
     */
    private List<PtStockDetailVO> detailProcess(String sql) {
        List<Map<String, Object>> tops = mssqlService.executeQuery(sql);
        return tops.stream().map(map -> {
            PtStockDetailVO vo = new PtStockDetailVO();
            String sku = (String) map.get("SKU");
            String skuClass = (String) map.get("SKU_CLASS");
            String skuDesc = (String) map.get("SKU_DESCRC");
            String dateStr = (String) map.get("LOT01");
            String lot12 = (String) map.get("LOT12");
            String lot13 = (String) map.get("LOT13");
            String lot15 = (String) map.get("LOT15");
            String lot17 = (String) map.get("LOT17");
            vo.setSku(sku);
            vo.setSkuClass(skuClass);
            vo.setSkuDesc(skuDesc);
            if (StringUtils.isNotEmpty(dateStr)) {
                LocalDate date = LocalDate.parse(dateStr);
                vo.setDate(date);
            }
            vo.setLot12(lot12);
            vo.setLot13(lot13);
            vo.setLot15(lot15);
            vo.setLot17(lot17);
            return vo;
        }).collect(Collectors.toList());
    }


    /**
     * 处理库存数据
     *
     * @param stock 库存数据
     * @param mick  米克重
     * @param in    入库数量
     * @param out   出库数量
     * @param num   数量
     */
    private void processStock(BpVO.Stock stock, Integer mick, Integer in, Integer out, Integer num) {
        stock.setName("米克重（" + mick + "）");
        stock.setMick(mick);
        stock.addIn(in);
        stock.addOut(out);
        stock.updateMax(num);
        stock.updateMin(num);
        stock.total(num);
    }


}
