package com.njzscloud.supervisory.bs.service;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.Week;
import cn.hutool.core.map.MapUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.njzscloud.common.core.tuple.Tuple2;
import com.njzscloud.common.core.tuple.Tuple3;
import com.njzscloud.common.core.utils.GroupUtil;
import com.njzscloud.supervisory.bs.mapper.StatisticsMapper;
import com.njzscloud.supervisory.bs.pojo.result.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
public class StatisticsService {
    private final StatisticsMapper statisticsMapper;

    private final String[] weeks = {"周一", "周二", "周三", "周四", "周五", "周六", "周日"};
    private final String[] weeks_ = {"周日", "周一", "周二", "周三", "周四", "周五", "周六"};
    private final String[] months = {"1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"};

    public Map<String, Object> obtainData() throws Exception {
        CompletableFuture<List<CarTrends>> carTrends = CompletableFuture.supplyAsync(statisticsMapper::getCarTrends)
                .exceptionally(ex -> {
                    log.error("车辆动态数据获取失败", ex);
                    return Collections.emptyList();
                });
        CompletableFuture<UserSummary> userSummary = CompletableFuture.supplyAsync(statisticsMapper::getUserSummary)
                .exceptionally(ex -> {
                    log.error("用户动态数据获取失败", ex);
                    return null;
                });
        CompletableFuture<List<GarbageDisposeSummary>> garbageDisposeSummary = CompletableFuture.supplyAsync(statisticsMapper::getGarbageDisposeSummary)
                .thenApply(list -> {
                    Double garbageDisposeSummary_totalWeight_1 = list.stream()
                            .filter(it -> it.getDepotType() == 1)
                            .map(GarbageDisposeSummary::getWeight)
                            .reduce(Double::sum).orElse(0D);
                    Double garbageDisposeSummary_totalWeight_2 = list.stream()
                            .filter(it -> it.getDepotType() == 2)
                            .map(GarbageDisposeSummary::getWeight)
                            .reduce(Double::sum).orElse(0D);

                    for (GarbageDisposeSummary disposeSummary : list) {
                        disposeSummary.setPercentage(disposeSummary.getDepotType() == 1 ?
                                garbageDisposeSummary_totalWeight_1 == 0 ? 0 : disposeSummary.getWeight() / garbageDisposeSummary_totalWeight_1 * 100 :
                                garbageDisposeSummary_totalWeight_2 == 0 ? 0 : disposeSummary.getWeight() / garbageDisposeSummary_totalWeight_2 * 100);
                    }
                    return list;
                })
                .exceptionally(ex -> {
                    log.error("垃圾处置动态数据获取失败", ex);
                    return Collections.emptyList();
                });

        CompletableFuture<List<GarbageDisposeSummary>> currentMonthGarbageDisposeSummary = CompletableFuture.supplyAsync(statisticsMapper::getCurrentMonthGarbageDisposeSummary)
                .thenApply(list -> {
                    if (list.size() < 6) {
                        List<String> goodsNames = statisticsMapper.getGoodsNames(Wrappers.query().eq("depot_type", 2)
                                        .notLike("name", "青石")
                                        .notLike("name", "矿石")
                                , 6 - list.size());
                        for (String goodsName : goodsNames) {
                            list.add(new GarbageDisposeSummary()
                                    .setGarbageName(goodsName)
                                    .setWeight(0D));
                        }
                    }
                    Double currentMonthGarbageDisposeSummary_totalWeight = list.stream()
                            .map(GarbageDisposeSummary::getWeight)
                            .reduce(Double::sum).orElse(0D);

                    for (GarbageDisposeSummary disposeSummary : list) {
                        disposeSummary.setPercentage(disposeSummary.getWeight() / currentMonthGarbageDisposeSummary_totalWeight * 100);
                    }
                    return list;
                })
                .exceptionally(ex -> {
                    log.error("当前月垃圾处置动态数据获取失败", ex);
                    return Collections.emptyList();
                });

        CompletableFuture<List<OrderSummary>> summaries = CompletableFuture.supplyAsync(statisticsMapper::getOrderSummary)
                .thenApply(orderSummary -> {
                    List<Long> userIds = orderSummary.stream().map(OrderSummary::getUserId).distinct().collect(Collectors.toList());
                    Map<Long, List<OrderSummary>> map = GroupUtil.k_a(orderSummary, OrderSummary::getUserId);
                    Map<Long, Map<Boolean, OrderSummary>> map_ = new HashMap<>();
                    map.forEach((s, orderSummaries) -> map_.put(s, GroupUtil.k_o(orderSummaries, OrderSummary::getFinish)));
                    return userIds.stream()
                            .map(it -> {
                                Map<Boolean, OrderSummary> map1 = map_.get(it);
                                OrderSummary finish = map1.get(Boolean.TRUE);
                                OrderSummary unFinish = map1.get(Boolean.FALSE);
                                if (finish == null && unFinish == null) return null;

                                Long finishCount = 0L;
                                Long unFinishCount = 0L;
                                String company = null;
                                String groupName = null;

                                if (finish != null) {
                                    company = finish.getCompany();
                                    groupName = finish.getGroupName();
                                    finishCount = finish.getQuantity();
                                }

                                if (unFinish != null) {
                                    company = unFinish.getCompany();
                                    groupName = unFinish.getGroupName();
                                    unFinishCount = unFinish.getQuantity();
                                }

                                return new OrderSummary()
                                        .setUserId(it)
                                        .setFinishCount(finishCount)
                                        .setUnFinishCount(unFinishCount)
                                        .setCompany(company)
                                        .setGroupName(groupName);

                            })
                            .filter(Objects::nonNull)
                            .collect(Collectors.toList());
                })
                .exceptionally(ex -> {
                    log.error("订单动态数据获取失败", ex);
                    return Collections.emptyList();
                });

        DateTime date = DateUtil.date();

        CompletableFuture<Double> today_disposeWeight = CompletableFuture.supplyAsync(() -> {
            long today_start = DateUtil.beginOfDay(date).getTime() / 1000;
            long today_end = DateUtil.endOfDay(date).getTime() / 1000;
            return statisticsMapper.disposeWeight(today_start, today_end);
        }).exceptionally(ex -> {
            log.error("订单动态数据获取失败", ex);
            return 0D;
        });
        CompletableFuture<Double> yesterday_disposeWeight = CompletableFuture.supplyAsync(() -> {
            DateTime yesterday = DateUtil.offsetDay(date, -1);
            long yesterday_start = DateUtil.beginOfDay(yesterday).getTime() / 1000;
            long yesterday_end = DateUtil.endOfDay(yesterday).getTime() / 1000;
            return statisticsMapper.disposeWeight(yesterday_start, yesterday_end);
        }).exceptionally(ex -> {
            log.error("订单动态数据获取失败", ex);
            return 0D;
        });
        CompletableFuture<Double> week_disposeWeight = CompletableFuture.supplyAsync(() -> {
            long week_start = DateUtil.beginOfWeek(date).getTime() / 1000;
            long week_end = DateUtil.endOfWeek(date).getTime() / 1000;
            return statisticsMapper.disposeWeight(week_start, week_end);
        }).exceptionally(ex -> {
            log.error("订单动态数据获取失败", ex);
            return 0D;
        });
        CompletableFuture<Double> month_disposeWeight = CompletableFuture.supplyAsync(() -> {
            long month_start = DateUtil.beginOfMonth(date).getTime() / 1000;
            long month_end = DateUtil.endOfMonth(date).getTime() / 1000;
            return statisticsMapper.disposeWeight(month_start, month_end);
        }).exceptionally(ex -> {
            log.error("订单动态数据获取失败", ex);
            return 0D;
        });
        CompletableFuture<Double> year_disposeWeight = CompletableFuture.supplyAsync(() -> {
            long year_start = DateUtil.beginOfYear(date).getTime() / 1000;
            long year_end = DateUtil.endOfYear(date).getTime() / 1000;
            return statisticsMapper.disposeWeight(year_start, year_end);
        }).exceptionally(ex -> {
            log.error("订单动态数据获取失败", ex);
            return 0D;
        });

        CompletableFuture<Tuple2<List<Double>, List<Double>>> weekAmount = CompletableFuture.supplyAsync(() -> {
            long week_start = DateUtil.beginOfWeek(date).getTime() / 1000;
            long week_end = DateUtil.endOfWeek(date).getTime() / 1000;
            List<OrderAmountSummary> week_orderAmountSummary = statisticsMapper.getOrderAmountSummary(week_start, week_end);
            Map<String, List<OrderAmountSummary>> week_temp_1 = GroupUtil.k_a(week_orderAmountSummary.stream().filter(it -> it.getCd() == 1), it -> {
                Date outTime = it.getOutTime();
                Week week = DateUtil.dayOfWeekEnum(outTime);
                return weeks_[week.getValue() - 1];
            });

            Map<String, List<OrderAmountSummary>> week_temp_2 = GroupUtil.k_a(week_orderAmountSummary.stream().filter(it -> it.getCd() == 2), it -> {
                Date outTime = it.getOutTime();
                Week week = DateUtil.dayOfWeekEnum(outTime);
                return weeks_[week.getValue() - 1];
            });

            List<Double> weekAmountIn = new ArrayList<>(weeks.length);
            for (String week : weeks) {
                List<OrderAmountSummary> orderAmountSummary = week_temp_1.getOrDefault(week, Collections.emptyList());
                Double amount = orderAmountSummary.stream().map(OrderAmountSummary::getAmount).reduce(Integer::sum).orElse(0) / 100.0;
                weekAmountIn.add(amount);
            }

            List<Double> weekAmountOut = new ArrayList<>(weeks.length);
            for (String week : weeks) {
                List<OrderAmountSummary> orderAmountSummary = week_temp_2.getOrDefault(week, Collections.emptyList());
                Double amount = orderAmountSummary.stream().map(OrderAmountSummary::getAmount).reduce(Integer::sum).orElse(0) / 100.0;
                weekAmountOut.add(amount);
            }
            return Tuple2.create(weekAmountIn, weekAmountOut);
        }).exceptionally(ex -> {
            log.error("订单动态数据获取失败", ex);
            return Tuple2.create(Collections.emptyList(), Collections.emptyList());
        });
        CompletableFuture<Tuple3<List<Double>, List<Double>, List<String>>> monthAmount = CompletableFuture.supplyAsync(() -> {
            long month_start = DateUtil.beginOfMonth(date).getTime() / 1000;
            long month_end = DateUtil.endOfMonth(date).getTime() / 1000;
            List<OrderAmountSummary> month_orderAmountSummary = statisticsMapper.getOrderAmountSummary(month_start, month_end);

            int dayOfMonth = DateUtil.endOfMonth(date).dayOfMonth();
            List<String> days = new ArrayList<>(dayOfMonth);

            for (int i = 0; i < dayOfMonth; i++) {
                days.add(i + 1 + "号");
            }
            Map<String, List<OrderAmountSummary>> month_temp_1 = GroupUtil.k_a(month_orderAmountSummary.stream().filter(it -> it.getCd() == 1), it -> {
                Date outTime = it.getOutTime();
                int day = DateUtil.dayOfMonth(outTime);
                return days.get(day - 1);
            });

            Map<String, List<OrderAmountSummary>> month_temp_2 = GroupUtil.k_a(month_orderAmountSummary.stream().filter(it -> it.getCd() == 2), it -> {
                Date outTime = it.getOutTime();
                int day = DateUtil.dayOfMonth(outTime);
                return days.get(day - 1);
            });
            List<Double> monthAmountIn = new ArrayList<>(dayOfMonth);
            List<Double> monthAmountOut = new ArrayList<>(dayOfMonth);
            for (String day : days) {
                List<OrderAmountSummary> orderAmountSummary = month_temp_1.getOrDefault(day, Collections.emptyList());
                Double amount = orderAmountSummary.stream().map(OrderAmountSummary::getAmount).reduce(Integer::sum).orElse(0) / 100.0;
                monthAmountIn.add(amount);
            }
            for (String day : days) {
                List<OrderAmountSummary> orderAmountSummary = month_temp_2.getOrDefault(day, Collections.emptyList());
                Double amount = orderAmountSummary.stream().map(OrderAmountSummary::getAmount).reduce(Integer::sum).orElse(0) / 100.0;
                monthAmountOut.add(amount);
            }
            return Tuple3.create(monthAmountIn, monthAmountOut, days);
        }).exceptionally(ex -> {
            log.error("订单动态数据获取失败", ex);
            return Tuple3.create(Collections.emptyList(), Collections.emptyList(), Collections.emptyList());
        });
        CompletableFuture<Tuple2<List<Double>, List<Double>>> yearAmount = CompletableFuture.supplyAsync(() -> {
            long year_start = DateUtil.beginOfYear(date).getTime() / 1000;
            long year_end = DateUtil.endOfYear(date).getTime() / 1000;
            List<OrderAmountSummary> year_orderAmountSummary = statisticsMapper.getOrderAmountSummary(year_start, year_end);

            Map<String, List<OrderAmountSummary>> year_temp_1 = GroupUtil.k_a(year_orderAmountSummary.stream().filter(it -> it.getCd() == 1), it -> {
                Date outTime = it.getOutTime();
                int month = DateUtil.month(outTime);
                return months[month];
            });

            Map<String, List<OrderAmountSummary>> year_temp_2 = GroupUtil.k_a(year_orderAmountSummary.stream().filter(it -> it.getCd() == 2), it -> {
                Date outTime = it.getOutTime();
                int month = DateUtil.month(outTime);
                return months[month];
            });
            List<Double> yearAmountIn = new ArrayList<>(months.length);
            List<Double> yearAmountOut = new ArrayList<>(months.length);
            for (String month : months) {
                List<OrderAmountSummary> orderAmountSummary = year_temp_1.getOrDefault(month, Collections.emptyList());
                Double amount = orderAmountSummary.stream().map(OrderAmountSummary::getAmount).reduce(Integer::sum).orElse(0) / 100.0;
                yearAmountIn.add(amount);
            }
            for (String month : months) {
                List<OrderAmountSummary> orderAmountSummary = year_temp_2.getOrDefault(month, Collections.emptyList());
                Double amount = orderAmountSummary.stream().map(OrderAmountSummary::getAmount).reduce(Integer::sum).orElse(0) / 100.0;
                yearAmountOut.add(amount);
            }
            return Tuple2.create(yearAmountIn, yearAmountOut);
        }).exceptionally(ex -> {
            log.error("订单动态数据获取失败", ex);
            return Tuple2.create(Collections.emptyList(), Collections.emptyList());
        });

        CompletableFuture<TodayOrderSummary> todayOrderSummary = CompletableFuture.supplyAsync(statisticsMapper::getTodayOrderSummary)
                .exceptionally(ex -> {
                    log.error("订单动态数据获取失败", ex);
                    return new TodayOrderSummary();
                });

        Tuple2<List<Double>, List<Double>> weekAmount_ = weekAmount.get();
        Tuple3<List<Double>, List<Double>, List<String>> monthAmount_ = monthAmount.get();
        Tuple2<List<Double>, List<Double>> yearAmount_ = yearAmount.get();
        return MapUtil.<String, Object>builder()
                .put("carTrends", carTrends.get())
                .put("userSummary", userSummary.get())
                .put("garbageDisposeSummary", garbageDisposeSummary.get())
                .put("currentMonthGarbageDisposeSummary", currentMonthGarbageDisposeSummary.get())
                .put("orderSummary", summaries.get())
                .put("todayDisposeWeight", today_disposeWeight.get())
                .put("yesterdayDisposeWeight", yesterday_disposeWeight.get())
                .put("weekDisposeWeight", week_disposeWeight.get())
                .put("monthDisposeWeight", month_disposeWeight.get())
                .put("yearDisposeWeight", year_disposeWeight.get())
                .put("todayOrderSummary", todayOrderSummary.get())
                .put("weekAmountIn", weekAmount_.get_0())
                .put("weekAmountOut", weekAmount_.get_1())
                .put("monthAmountIn", monthAmount_.get_0())
                .put("monthAmountOut", monthAmount_.get_1())
                .put("days", monthAmount_.get_2())
                .put("yearAmountIn", yearAmount_.get_0())
                .put("yearAmountOut", yearAmount_.get_1())
                .build();
    }

    public List<OrderInfo> obtainOrder(Long userId) {
        return statisticsMapper.obtainOrder(userId);
    }

    public Map<String, Object> biz() {

        List<Map<String, Object>> baseInfo = statisticsMapper.baseInfo();
        List<Map<String, Object>> disposeInfo = statisticsMapper.disposeInfo();
        List<Map<String, Object>> garbageInfo = statisticsMapper.garbageInfo();
        Map<String, Object> planInfo = statisticsMapper.planInfo();
        List<Map<String, Object>> orderInfo = statisticsMapper.orderInfo();
        List<Map<String, Object>> warningInfo = statisticsMapper.warningInfo();

        return MapUtil.<String, Object>builder()
                .put("baseInfo", baseInfo)
                .put("disposeInfo", disposeInfo)
                .put("garbageInfo", garbageInfo)
                .put("planInfo", planInfo)
                .put("orderInfo", orderInfo)
                .put("warningInfo", warningInfo)
                .build();
    }

}
