package com.njzscloud.supervisory.bs.service;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.NumberUtil;
import com.njzscloud.common.core.ex.Exceptions;
import com.njzscloud.common.core.utils.GroupUtil;
import com.njzscloud.supervisory.bs.mapper.SupervisionStatisticsMapper;
import com.njzscloud.supervisory.bs.pojo.result.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
@RequiredArgsConstructor
public class SupervisionStatisticsService {
    private final SupervisionStatisticsMapper supervisionStatisticsMapper;

    private final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
            50,
            200,
            0L,
            TimeUnit.MILLISECONDS,
            new ArrayBlockingQueue<>(1000),
            (r, executor) -> {
                throw Exceptions.exception("服务器繁忙");
            }
    );

    public Map<String, Object> obtainData() {

        DispatchSummary dispatchSummary = supervisionStatisticsMapper.getDispatchSummary();

        YuYueSummary yuYueSummary = supervisionStatisticsMapper.getYuYueSummary();

        Integer disposeTotalWeight = supervisionStatisticsMapper.getDisposeTotalWeight();

        CarSummary carSummary = supervisionStatisticsMapper.getCarSummary();
        Integer todayCarCount = supervisionStatisticsMapper.getTodayCarCount();
        Integer totalCarCount = supervisionStatisticsMapper.getTotalCarCount();
        List<RegionSummary> regionSummary = supervisionStatisticsMapper.getRegionSummary();
        List<CompanySummary> companySummary = supervisionStatisticsMapper.getCompanySummary();
        List<StationInfo> stationInfo = supervisionStatisticsMapper.getStationInfo();
        List<Order> orders = supervisionStatisticsMapper.getOrders();
        List<Project> projects = supervisionStatisticsMapper.getProjects();
        List<Map<String, Object>> kehu = supervisionStatisticsMapper.getKehuList();

        return MapUtil.<String, Object>builder()
                .put("dispatchSummary", dispatchSummary == null ? new DispatchSummary() : dispatchSummary)
                .put("yuYueSummary", yuYueSummary == null ? new YuYueSummary() : yuYueSummary)
                .put("disposeTotalWeight", disposeTotalWeight == null ? 0 : disposeTotalWeight)
                .put("carSummary", carSummary == null ? new CarSummary() : carSummary)
                .put("todayCarCount", todayCarCount == null ? 0 : todayCarCount)
                .put("totalCarCount", totalCarCount == null ? 0 : totalCarCount)
                .put("regionSummary", regionSummary == null ? new ArrayList<>() : regionSummary)
                .put("companySummary", companySummary == null ? new ArrayList<>() : companySummary)
                .put("stationInfo", stationInfo == null ? new ArrayList<>() : stationInfo)
                .put("orders", orders == null ? new ArrayList<>() : orders)
                .put("projects", projects == null ? new ArrayList<>() : projects)
                .put("kehu", kehu == null ? new ArrayList<>() : kehu)
                .build();
    }

    public Map<String, Object> firstPage(Integer trendStatisticsType) {
        CompletableFuture<Map<String, Object>> dataStatisticsFuture = CompletableFuture.supplyAsync(supervisionStatisticsMapper::dataStatistics, threadPoolExecutor)
                .thenApply(dataStatistics -> {
                    Double todayTotalWeight = MapUtil.getDouble(dataStatistics, "todayTotalWeight", 0.0);
                    Double yesterdayTotalWeight = MapUtil.getDouble(dataStatistics, "yesterdayTotalWeight", 0.0);
                    double dayRate = yesterdayTotalWeight == 0 ? 0 : (todayTotalWeight - yesterdayTotalWeight) / yesterdayTotalWeight * 100;

                    Double currentMonthTotalWeight = MapUtil.getDouble(dataStatistics, "currentMonthTotalWeight", 0.0);
                    Double lastMonthTotalWeight = MapUtil.getDouble(dataStatistics, "lastMonthTotalWeight", 0.0);
                    double monthRate = lastMonthTotalWeight == 0 ? 0 : (currentMonthTotalWeight - lastMonthTotalWeight) / lastMonthTotalWeight * 100;

                    Double todayFinishCount = MapUtil.getDouble(dataStatistics, "todayFinishCount", 0.0);
                    Double todayTotalCount = MapUtil.getDouble(dataStatistics, "todayTotalCount", 0.0);
                    double todayFinishRate = todayTotalCount == 0 ? 0 : todayFinishCount / todayTotalCount * 100;

                    Double yesterdayFinishCount = MapUtil.getDouble(dataStatistics, "yesterdayFinishCount", 0.0);
                    // Double yesterdayTotalCount = MapUtil.getDouble(dataStatistics, "yesterdayTotalCount", 0.0);
                    // Double yesterdayFinishRate = yesterdayFinishCount == 0 ? 100 : yesterdayFinishCount / yesterdayTotalCount * 100;
                    double incFinishRate = yesterdayFinishCount == 0 ? 0 : (todayFinishCount - yesterdayFinishCount) / yesterdayFinishCount * 100;

                    Double todayWarnCount = MapUtil.getDouble(dataStatistics, "todayWarnCount", 0.0);
                    Double yesterdayWarnCount = MapUtil.getDouble(dataStatistics, "yesterdayWarnCount", 0.0);
                    double warnRate = todayTotalCount == 0 ? 0 : todayWarnCount / todayTotalCount * 100;
                    double incWarnRate = yesterdayWarnCount == 0 ? 0 : (todayWarnCount - yesterdayWarnCount) / yesterdayWarnCount * 100;


                    Double yesterdayTotalCount = MapUtil.getDouble(dataStatistics, "yesterdayTotalCount", 0.0);
                    double incDayCountRate = yesterdayTotalCount == 0 ? 0 : (todayTotalCount - yesterdayTotalCount) / yesterdayTotalCount * 100;

                    Double currentMonthTotalCount = MapUtil.getDouble(dataStatistics, "currentMonthTotalCount", 0.0);
                    Double lastMonthTotalCount = MapUtil.getDouble(dataStatistics, "lastMonthTotalCount", 0.0);
                    double incMonthCountRate = lastMonthTotalCount == 0 ? 0 : (currentMonthTotalCount - lastMonthTotalCount) / lastMonthTotalCount * 100;


                    dataStatistics.put("dayRate", NumberUtil.decimalFormat("#.##", dayRate));
                    dataStatistics.put("monthRate", NumberUtil.decimalFormat("#.##", monthRate));
                    dataStatistics.put("finishRate", NumberUtil.decimalFormat("#.##", todayFinishRate));
                    dataStatistics.put("incFinishRate", NumberUtil.decimalFormat("#.##", incFinishRate));
                    dataStatistics.put("warnRate", NumberUtil.decimalFormat("#.##", warnRate));
                    dataStatistics.put("incWarnRate", NumberUtil.decimalFormat("#.##", incWarnRate));
                    dataStatistics.put("incDayCountRate", NumberUtil.decimalFormat("#.##", incDayCountRate));
                    dataStatistics.put("incMonthCountRate", NumberUtil.decimalFormat("#.##", incMonthCountRate));
                    return dataStatistics;
                })
                .exceptionally(ex -> {
                    log.error("数据获取失败", ex);
                    return Collections.emptyMap();
                });


        CompletableFuture<List<Map<String, Object>>> warnTruckFuture = CompletableFuture.supplyAsync(supervisionStatisticsMapper::warnTruck, threadPoolExecutor)
                .exceptionally(ex -> {
                    log.error("数据获取失败", ex);
                    return Collections.emptyList();
                });

        CompletableFuture<Map<String, Object>> truckStatisticsFuture = CompletableFuture.supplyAsync(supervisionStatisticsMapper::truckStatistics, threadPoolExecutor)
                .thenApply(truckStatistics -> {
                    Integer offlineCount = MapUtil.getInt(truckStatistics, "offlineCount", 0);
                    Integer totalCount = MapUtil.getInt(truckStatistics, "totalCount", 0);
                    truckStatistics.put("onlineCount", totalCount - offlineCount);
                    return truckStatistics;
                })
                .exceptionally(ex -> {
                    log.error("数据获取失败", ex);
                    return Collections.emptyMap();
                });

        CompletableFuture<List<Map<String, Object>>> projectStatisticsFuture = CompletableFuture.supplyAsync(supervisionStatisticsMapper::projectStatistics, threadPoolExecutor)
                .exceptionally(ex -> {
                    log.error("数据获取失败", ex);
                    return Collections.emptyList();
                });

        CompletableFuture<Map<String, Double>> trendStatisticsFuture = CompletableFuture.supplyAsync(() -> {
                    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
                    LocalDateTime now = LocalDateTime.now();
                    LocalDateTime startTime = now.minusDays(7);
                    LocalDateTime endTime = LocalDateTime.now();

                    if (trendStatisticsType == 2) {
                        startTime = now.minusMonths(12);
                    } else if (trendStatisticsType == 3) {
                        startTime = now.minusYears(5);
                    }
                    List<Map<String, Object>> maps = supervisionStatisticsMapper.trendStatistics(startTime.format(formatter), endTime.format(formatter));
                    Map<String, Double> res = new HashMap<>();
                    if (trendStatisticsType == 2) {
                        long months = ChronoUnit.MONTHS.between(startTime, endTime);
                        Map<String, List<Map<String, Object>>> timepointMap = GroupUtil.k_a(maps, it -> {
                            String timepoint = MapUtil.getStr(it, "timepoint", "");
                            return DateUtil.format(DateUtil.parseDate(timepoint), "yyyy-MM");
                        });

                        for (long l = 0; l < months; l++) {
                            LocalDateTime localDateTime = startTime.plusMonths(l);
                            String month = localDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM"));
                            List<Map<String, Object>> monthData = timepointMap.getOrDefault(month, Collections.emptyList());
                            Optional<Integer> weight = monthData.stream()
                                    .map(it -> MapUtil.getInt(it, "weight", 0))
                                    .reduce(Integer::sum);

                            res.put(month, weight.orElse(0) / 1000.0);
                        }

                    } else if (trendStatisticsType == 3) {
                        long years = ChronoUnit.YEARS.between(startTime, endTime);
                        Map<String, List<Map<String, Object>>> timepointMap = GroupUtil.k_a(maps, it -> {
                            String timepoint = MapUtil.getStr(it, "timepoint", "");
                            return DateUtil.format(DateUtil.parseDate(timepoint), "yyyy");
                        });

                        for (long l = 0; l < years; l++) {
                            LocalDateTime localDateTime = startTime.plusYears(l);
                            String year = localDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM"));
                            List<Map<String, Object>> yearData = timepointMap.getOrDefault(year, Collections.emptyList());
                            Optional<Integer> weight = yearData.stream()
                                    .map(it -> MapUtil.getInt(it, "weight", 0))
                                    .reduce(Integer::sum);

                            res.put(year, weight.orElse(0) / 1000.0);
                        }
                    } else {
                        long days = ChronoUnit.DAYS.between(startTime, endTime);
                        Map<String, List<Map<String, Object>>> timepointMap = GroupUtil.k_a(maps, it -> {
                            String timepoint = MapUtil.getStr(it, "timepoint", "");
                            return DateUtil.format(DateUtil.parseDate(timepoint), "yyyy-MM-dd");
                        });

                        for (long l = 0; l < days; l++) {
                            LocalDateTime localDateTime = startTime.plusDays(l);
                            String day = localDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
                            List<Map<String, Object>> dayData = timepointMap.getOrDefault(day, Collections.emptyList());
                            Optional<Integer> weight = dayData.stream()
                                    .map(it -> MapUtil.getInt(it, "weight", 0))
                                    .reduce(Integer::sum);

                            res.put(day, weight.orElse(0) / 1000.0);
                        }
                    }
                    return res;
                }, threadPoolExecutor)
                .exceptionally(ex -> {
                    log.error("数据获取失败", ex);
                    return Collections.emptyMap();
                });
        CompletableFuture<List<Map<String, Object>>> categoryStatisticsFuture = CompletableFuture.supplyAsync(supervisionStatisticsMapper::categoryStatistics, threadPoolExecutor)
                .exceptionally(ex -> {
                    log.error("数据获取失败", ex);
                    return Collections.emptyList();
                });

        CompletableFuture<Map<String, Object>> orderStatisticsFuture = CompletableFuture.supplyAsync(supervisionStatisticsMapper::orderStatistics, threadPoolExecutor)
                .exceptionally(ex -> {
                    log.error("数据获取失败", ex);
                    return Collections.emptyMap();
                });

        CompletableFuture<Map<String, Object>> weightStatisticsFuture = CompletableFuture.supplyAsync(supervisionStatisticsMapper::weightStatistics, threadPoolExecutor)
                .exceptionally(ex -> {
                    log.error("数据获取失败", ex);
                    return Collections.emptyMap();
                });
        CompletableFuture<Map<String, Object>> activeProjectStatisticsFuture = CompletableFuture.supplyAsync(supervisionStatisticsMapper::activeProjectStatistics, threadPoolExecutor)
                .exceptionally(ex -> {
                    log.error("数据获取失败", ex);
                    return Collections.emptyMap();
                });
        CompletableFuture<Map<String, Object>> transStatisticsFuture = CompletableFuture.supplyAsync(supervisionStatisticsMapper::transStatistics, threadPoolExecutor)
                .exceptionally(ex -> {
                    log.error("数据获取失败", ex);
                    return Collections.emptyMap();
                });
        CompletableFuture<Map<String, Object>> userStatisticsFuture = CompletableFuture.supplyAsync(supervisionStatisticsMapper::userStatistics, threadPoolExecutor)
                .exceptionally(ex -> {
                    log.error("数据获取失败", ex);
                    return Collections.emptyMap();
                });
        CompletableFuture<List<Map<String, Object>>> dataTableFuture = CompletableFuture.supplyAsync(supervisionStatisticsMapper::dataTable, threadPoolExecutor)
                .exceptionally(ex -> {
                    log.error("数据获取失败", ex);
                    return Collections.emptyList();
                });
        CompletableFuture<Map<String, Object>> todayAuditPassStatisticsFuture = CompletableFuture.supplyAsync(supervisionStatisticsMapper::todayAuditPassStatistics, threadPoolExecutor)
                .exceptionally(ex -> {
                    log.error("数据获取失败", ex);
                    return Collections.emptyMap();
                });
        CompletableFuture<List<Map<String, Object>>> stationStatisticsFuture = CompletableFuture.supplyAsync(supervisionStatisticsMapper::stationStatistics, threadPoolExecutor)
                .exceptionally(ex -> {
                    log.error("数据获取失败", ex);
                    return Collections.emptyList();
                });

        try {
            Map<String, Object> dataStatistics = dataStatisticsFuture.get();
            List<Map<String, Object>> warnTruck = warnTruckFuture.get();
            Map<String, Object> truckStatistics = truckStatisticsFuture.get();
            List<Map<String, Object>> projectStatistics = projectStatisticsFuture.get();
            Map<String, Double> trendStatistics = trendStatisticsFuture.get();
            List<Map<String, Object>> categoryStatistics = categoryStatisticsFuture.get();
            Map<String, Object> orderStatistics = orderStatisticsFuture.get();
            Map<String, Object> activeProjectStatistics = activeProjectStatisticsFuture.get();
            Map<String, Object> userStatistics = userStatisticsFuture.get();
            Map<String, Object> transStatistics = transStatisticsFuture.get();
            Map<String, Object> weightStatistics = weightStatisticsFuture.get();
            List<Map<String, Object>> dataTable = dataTableFuture.get();
            Map<String, Object> todayAuditPassStatistics = todayAuditPassStatisticsFuture.get();
            List<Map<String, Object>> stationStatistics = stationStatisticsFuture.get();
            return MapUtil.<String, Object>builder()
                    .put("dataStatistics", dataStatistics)
                    .put("warnTruck", warnTruck)
                    .put("truckStatistics", truckStatistics)
                    .put("projectStatistics", projectStatistics)
                    .put("trendStatistics", trendStatistics)
                    .put("categoryStatistics", categoryStatistics)
                    .put("orderStatistics", orderStatistics)
                    .put("activeProjectStatistics", activeProjectStatistics)
                    .put("transStatistics", transStatistics)
                    .put("userStatistics", userStatistics)
                    .put("weightStatistics", weightStatistics)
                    .put("dataTable", dataTable)
                    .put("todayAuditPassStatistics", todayAuditPassStatistics)
                    .put("stationStatistics", stationStatistics)
                    .build();
        } catch (Exception e) {
            throw Exceptions.exception(e, "数据获取失败");
        }
    }
}
