package com.kym.admin.jobs;

import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import com.kym.common.utils.CommUtil;
import com.kym.entity.admin.ConnectorInfo;
import com.kym.entity.admin.StationStatDay;
import com.kym.entity.admin.StationStatMonth;
import com.kym.entity.miniapp.ChargeOrder;
import com.kym.service.admin.ConnectorInfoService;
import com.kym.service.admin.StationStatDayService;
import com.kym.service.admin.StationStatMonthService;
import com.kym.service.miniapp.ChargeOrderService;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 站点统计定时任务
 *
 * @author skyline
 */
@Component
@Slf4j
public class StationStatJob {

    private final ChargeOrderService chargeOrderService;

    private final StationStatDayService dayService;
    private final StationStatMonthService monthService;

    private final ConnectorInfoService connectorInfoService;

    public StationStatJob(ChargeOrderService chargeOrderService, StationStatDayService stationStatDayService, StationStatMonthService stationStatMonthService, ConnectorInfoService connectorInfoService) {
        this.chargeOrderService = chargeOrderService;
        this.dayService = stationStatDayService;
        this.monthService = stationStatMonthService;
        this.connectorInfoService = connectorInfoService;
    }


    // 每天14:30执行一次，通过charge_app.t_charge_order表统计start_time为前一天且充电金额大于100的充电订单，分别统计充电人数、订单数、充电总费用、充电总电量、平均充电电量、平均充电费用、平均订单费用、平均订单电量

    /**
     * 日统计，每日下午14:30启动，统计上日数据
     */
    @Scheduled(cron = "0 30 14 * * ?")
    // 定时每日下午14:30
    private void dayStat() {
        log.info("执行站点日统计定时任务-开始");
        var statDay = LocalDateTime.now().minusDays(1);
        var startTime = LocalDateTime.of(statDay.toLocalDate(), LocalTime.MIN);
        var endTime = LocalDateTime.of(statDay.toLocalDate(), LocalTime.MAX);
        var chargeOrderList = getChargeOrders(startTime, endTime);
        dayService.replaceBatch((Collection<StationStatDay>) getStationStat(chargeOrderList, true, LocalDate.now().minusDays(1)));
        dayService.saveBatch((Collection<StationStatDay>) getStationStat(chargeOrderList, true, LocalDate.now().minusDays(1)));
        log.info("执行站点日统计定时任务-结束");
    }


    /**
     * 月统计，每月5日天下午15:00启动，统计上月数据
     */
    @Scheduled(cron = "0 0 15 5 * ?")
    private void monthStat() {
        log.info("执行站点月统计定时任务-开始");
        var statMonth = LocalDateTime.now().minusMonths(1);
        var startTime = statMonth.with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN);
        var endTime = statMonth.with(TemporalAdjusters.lastDayOfMonth()).with(LocalTime.MAX);
        var chargeOrderList = getChargeOrders(startTime, endTime);
        monthService.replaceBatch((Collection<StationStatMonth>) getStationStat(chargeOrderList, false, LocalDate.now().minusMonths(1)));
//        monthService.saveBatch((Collection<StationStatMonth>) getStationStat(chargeOrderList, false, LocalDate.now().minusMonths(1)));
        log.info("执行站点月统计定时任务-结束");
    }

    /**
     * 手动重新同步指定月份的站点统计数据
     * @param yearMonth 格式: "yyyy-MM"
     * @return 生成的记录数
     */
    public int resyncMonth(String yearMonth) {
        var ym = YearMonth.parse(yearMonth, DateTimeFormatter.ofPattern("yyyy-MM"));
        if (!ym.isBefore(YearMonth.now())) {
            throw new IllegalArgumentException("只允许同步本月之前的数据");
        }
        var startTime = ym.atDay(1).atTime(LocalTime.MIN);
        var endTime = ym.atEndOfMonth().atTime(LocalTime.MAX);
        var targetDate = ym.atDay(1);
        var chargeOrderList = getChargeOrders(startTime, endTime);
        var stats = (Collection<StationStatMonth>) getStationStat(chargeOrderList, false, targetDate);
        monthService.replaceBatch(stats);
        return stats.size();
    }

    // 只执行一次
//    @PostConstruct
    private void init() {
        log.info("执行站点初始化定时任务-开始");
        // 2023-08-01 00:00:00
//        var statMonth = LocalDateTime.of(2023, 12, 1, 0, 0);
//        var endMonth = LocalDateTime.of(2023, 12, 1, 0, 0);
//        var startTime = statMonth.with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN);
//        var endTime = endMonth.with(TemporalAdjusters.lastDayOfMonth()).with(LocalTime.MAX);
//        var chargeOrderList = getChargeOrders(startTime, endTime);
//        monthService.saveBatch((Collection<StationStatMonth>) getStationStat(chargeOrderList, false));
//
//        // 将订单按照日为单位分组
//        var chargeOrderMap = chargeOrderList.stream().collect(Collectors.groupingBy(order ->
//                order.getStartTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))
//        ));
//        // chargeOrderMap按照key升序排序
//        var chargeOrderMapSort = chargeOrderMap.entrySet().stream().sorted(Map.Entry.comparingByKey()).collect(
//                Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (oldValue, newValue) -> oldValue, LinkedHashMap::new));
//
//        chargeOrderMapSort.forEach((k, v) -> dayService.saveBatch(((Collection<StationStatDay>) getStationStat(v, true))));

        log.info("执行站点初始化定时任务-结束");
    }

    List<ChargeOrder> getChargeOrders(LocalDateTime startTime, LocalDateTime endTime) {
        // 通过charge_app.t_charge_order表统计start_time为前一天且充电金额大于0的有效充电订单
        DynamicDataSourceContextHolder.push("db-miniapp");
        var res = chargeOrderService.lambdaQuery()
                .ge(ChargeOrder::getStartTime, startTime)
                .le(ChargeOrder::getStartTime, endTime)
                .gt(ChargeOrder::getTotalMoney, 0)
                .list();
        DynamicDataSourceContextHolder.poll();
        return res;
    }

    Collection<?> getStationStat(List<ChargeOrder> chargeOrderList, boolean isDayStat, LocalDate targetDate) {
        // 统计每个站点的connector_id数量
        var stationConnectorCountMap = connectorInfoService.list().stream().collect(Collectors.groupingBy(ConnectorInfo::getStationId, Collectors.counting()));
        // 将chargeOrderList按照stationId分组
        var chargeOrderMap = chargeOrderList.stream().collect(Collectors.groupingBy(ChargeOrder::getStationId));
        // 根据分组计算每个站点的充电人数、订单数、充电总费用、充电总电量、平均充电电量、平均充电费用、平均订单费用、平均订单电量
        return chargeOrderMap.entrySet().stream().collect(Collectors.toMap(
                Map.Entry::getKey,
                entry -> {
                    var chargeOrders = entry.getValue();
                    var chargeUsers = chargeOrders.stream().collect(Collectors.collectingAndThen(
                            Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(ChargeOrder::getUserId))), ArrayList<ChargeOrder>::new)).size();
                    var totalMoney = chargeOrders.stream().mapToInt(ChargeOrder::getTotalMoney).sum();
                    var elecMoney = chargeOrders.stream().mapToInt(ChargeOrder::getElecMoney).sum();
                    var serviceMoney = chargeOrders.stream().mapToInt(ChargeOrder::getServiceMoney).sum();
                    var serviceMoneyDiscount = chargeOrders.stream().mapToInt(ChargeOrder::getServiceMoneyDiscount).sum();
                    var discountAmount = chargeOrders.stream().mapToInt(ChargeOrder::getDiscountAmount).sum();
                    var totalPower = chargeOrders.stream().mapToDouble(ChargeOrder::getTotalPower).sum();
                    var avgPower = chargeOrders.stream().mapToDouble(ChargeOrder::getTotalPower).average().orElse(0.0);
                    var avgOrderMoney = chargeOrders.stream().mapToInt(ChargeOrder::getTotalMoney).average().orElse(0);
                    // 充电桩使用率 = 有订单的充电桩数量 / 总充电桩数
                    var connectorUsageRate = (double) chargeOrders.stream().filter(CommUtil.distinctByKey(ChargeOrder::getConnectorId)).count() / stationConnectorCountMap.get(entry.getKey());
                    ;
                    return isDayStat
                            ? new StationStatDay()
                            .setStationId(entry.getKey())
                            .setStatDay(targetDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")))
                            .setChargeUsers(chargeUsers)
                            .setValidOrders(chargeOrders.size())
                            .setTotalMoney(totalMoney)
                            .setElecMoney(elecMoney)
                            .setServiceMoney(serviceMoney)
                            .setServiceMoneyDiscount(serviceMoneyDiscount)
                            .setDiscountAmount(discountAmount)

                            .setTotalPower(totalPower)
                            .setAvgOrderElec(avgPower)
                            .setAvgOrderMoney((int) avgOrderMoney)
                            .setAvgConnectorElec(totalPower / (stationConnectorCountMap.get(entry.getKey())))
                            .setConnectorUsageRate(connectorUsageRate)

                            : new StationStatMonth()
                            .setStationId(entry.getKey())
                            .setStatMonth(targetDate.format(DateTimeFormatter.ofPattern("yyyy-MM")))
                            .setChargeUsers(chargeUsers)
                            .setValidOrders(chargeOrders.size())
                            .setTotalMoney(totalMoney)
                            .setElecMoney(elecMoney)
                            .setServiceMoney(serviceMoney)
                            .setServiceMoneyDiscount(serviceMoneyDiscount)
                            .setDiscountAmount(discountAmount)
                            .setTotalPower(totalPower)
                            .setAvgOrderElec(avgPower)
                            .setAvgOrderMoney((int) avgOrderMoney)
                            .setAvgConnectorElec(totalPower / (stationConnectorCountMap.get(entry.getKey()) * (targetDate.lengthOfMonth())));
                })).values();
    }
}
