package com.kym.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.kym.common.utils.CommUtil;
import com.kym.entity.SplitRecord;
import com.kym.entity.User;
import com.kym.entity.WashDevice;
import com.kym.entity.WashOrder;
import com.kym.entity.queryParams.StatQueryParam;
import com.kym.entity.vo.DashboardVo;
import com.kym.entity.vo.StationTrendVo;
import com.kym.service.*;
import com.kym.service.cache.KymCache;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.TemporalAdjusters;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


/**
 * 统计
 */
@Service
public class StatServiceImpl implements StatService {
    private final UserService userService;
    private final WashOrderService washOrderService;
    private final WashDeviceService washDeviceService;
    private final SplitRecordService splitRecordService;
    public StatServiceImpl(UserService userService, WashOrderService washOrderService, WashDeviceService washDeviceService,
                           SplitRecordService splitRecordService) {
        this.userService = userService;
        this.washOrderService = washOrderService;
        this.washDeviceService = washDeviceService;
        this.splitRecordService = splitRecordService;
    }

    @Override
    public Map<String, Integer> getTodayConsumption() {
        return washOrderService.sumAmountByDate(LocalDate.now());
    }

    @Override
    public Map<String, Integer> getMonthConsumption() {
        return washOrderService.sumMonthAmount(LocalDate.now());
    }

    @Override
    public Integer getTotalConsumption() {
        return null;
    }

    @Override
    public Integer getTotalRegistrations() {
        return (int) userService.count();
    }

    /**
     * 当月订单平均消费金额
     *
     * @return
     */
    @Override
    public Integer getAvgPrice() {
//        int totalAmountMap = washOrderService.sumMonthAmount(LocalDate.now());
//        int totalOrdersMap = washOrderService.countMonthOrdersCount(LocalDate.now());
//        // todo 按照站点 分组统计
//        return totalOrders == 0 ? 0 : totalAmount / totalOrders;
        return null;
    }

    /**
     * 平均洗车时长
     *
     * @return
     */
    @Override
    public Integer getAvgDuration() {
        var startTime = LocalDate.now().with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN);
        var endTime = LocalDate.now().with(TemporalAdjusters.lastDayOfMonth()).with(LocalTime.MAX);
        LambdaQueryWrapper<WashOrder> wrapper = new LambdaQueryWrapper<>();
        wrapper.ge(WashOrder::getStartTime, startTime);
        wrapper.lt(WashOrder::getStartTime, endTime);
        var list = washOrderService.list(wrapper);
        if (!list.isEmpty()) {
            var totalSeconds = list.stream().mapToLong(WashOrder::getTotalSeconds).sum();
            return (int) (totalSeconds / list.size());
        }
        return null;
    }


    /**
     * 当月用户平均洗车频率
     *
     * @return
     */
    @Override
    public Double getAvgFrequency() {
        var startTime = LocalDate.now().with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN);
        var endTime = LocalDate.now().with(TemporalAdjusters.lastDayOfMonth()).with(LocalTime.MAX);
        LambdaQueryWrapper<WashOrder> wrapper = new LambdaQueryWrapper<>();
        wrapper.ge(WashOrder::getStartTime, startTime);
        wrapper.lt(WashOrder::getStartTime, endTime);
        var list = washOrderService.list(wrapper);
        // 洗车人数
        var users = list.stream().map(WashOrder::getUserId).distinct().count();
        return users == 0 ? 0 : (double) Math.round((double) list.size() / users * 100) / 100;
    }

    /**
     * 今日概要
     *
     * @return
     */
    @Override
    public DashboardVo dashboard(String stationId) {
        // 截止到现在今日的所有洗车订单
        var todayOrders = washOrderService.lambdaQuery()
                .eq(WashOrder::getStationId, stationId)
                .ge(WashOrder::getStartTime, LocalDateTime.of(LocalDate.now(), LocalTime.MIN))
                .list();

        // 今日收入：充值收入 + 跨店收入 - 退款 - 跨店支出
        var todayRechargeAndCrossIncome = splitRecordService.lambdaQuery()
                .eq(SplitRecord::getToStationId, stationId)
                .in(SplitRecord::getType, SplitRecord.TYPE_RECHARGE, SplitRecord.TYPE_CROSS_INCOME)
                .ge(SplitRecord::getCreateTime, LocalDateTime.of(LocalDate.now(), LocalTime.MIN))
                .lt(SplitRecord::getCreateTime, LocalDateTime.of(LocalDate.now().plusDays(1), LocalTime.MIN))
                .list().stream().filter(r -> r.getAmount() != null).mapToInt(SplitRecord::getAmount).sum();
        var todayRefund = splitRecordService.lambdaQuery()
                .eq(SplitRecord::getFromStationId, stationId)
                .eq(SplitRecord::getType, SplitRecord.TYPE_REFUND)
                .ge(SplitRecord::getCreateTime, LocalDateTime.of(LocalDate.now(), LocalTime.MIN))
                .lt(SplitRecord::getCreateTime, LocalDateTime.of(LocalDate.now().plusDays(1), LocalTime.MIN))
                .list().stream().filter(r -> r.getAmount() != null).mapToInt(SplitRecord::getAmount).sum();
        var todayCrossExpend = splitRecordService.lambdaQuery()
                .eq(SplitRecord::getFromStationId, stationId)
                .eq(SplitRecord::getType, SplitRecord.TYPE_CROSS_EXPEND)
                .ge(SplitRecord::getCreateTime, LocalDateTime.of(LocalDate.now(), LocalTime.MIN))
                .lt(SplitRecord::getCreateTime, LocalDateTime.of(LocalDate.now().plusDays(1), LocalTime.MIN))
                .list().stream().filter(r -> r.getAmount() != null).mapToInt(SplitRecord::getAmount).sum();
        var todayIncome = todayRechargeAndCrossIncome - todayRefund - todayCrossExpend;

        // 今日洗车人数（去重用户）
        var todayWashUsers = (int) todayOrders.stream()
                .map(WashOrder::getUserId)
                .filter(java.util.Objects::nonNull)
                .distinct()
                .count();

        // 站点今日注册人数
        var todayRegistrations = userService.countDailyRegister(LocalDate.now(), stationId);

        // 平均洗车消费单价（分）
        var todayConsumptionAmount = todayOrders.stream().filter(o -> o.getAmount() != null).mapToInt(WashOrder::getAmount).sum();
        var avgOrderPrice = todayOrders.isEmpty() ? 0 : todayConsumptionAmount / todayOrders.size();
       
        // 平均洗车时长（秒）
        var ordersWithDuration = todayOrders.stream().filter(o -> o.getTotalSeconds() != null).toList();
       var avgOrderDuration = ordersWithDuration.isEmpty() ? 0 : (int) (ordersWithDuration.stream().mapToLong(WashOrder::getTotalSeconds).sum() / ordersWithDuration.size());

        // 月度数据
        var monthOrders = washOrderService.lambdaQuery()
                .eq(WashOrder::getStationId, stationId)
                .ge(WashOrder::getStartTime, LocalDate.now().with(TemporalAdjusters.firstDayOfMonth()).atTime(LocalTime.MIN))
                .le(WashOrder::getStartTime, LocalDate.now().with(TemporalAdjusters.lastDayOfMonth()).atTime(LocalTime.MAX))
                .list();
        var monthConsumptionAmount = monthOrders.stream().filter(o -> o.getAmount() != null).mapToInt(WashOrder::getAmount).sum();
        var monthRechargeAndCrossIncome = splitRecordService.lambdaQuery()
                .eq(SplitRecord::getToStationId, stationId)
                .in(SplitRecord::getType, SplitRecord.TYPE_RECHARGE, SplitRecord.TYPE_CROSS_INCOME)
                .ge(SplitRecord::getCreateTime, LocalDate.now().with(TemporalAdjusters.firstDayOfMonth()).atTime(LocalTime.MIN))
                .lt(SplitRecord::getCreateTime, LocalDate.now().with(TemporalAdjusters.firstDayOfMonth()).plusMonths(1).atTime(LocalTime.MIN))
                .list().stream().filter(r -> r.getAmount() != null).mapToInt(SplitRecord::getAmount).sum();
        var monthRefund = splitRecordService.lambdaQuery()
                .eq(SplitRecord::getFromStationId, stationId)
                .eq(SplitRecord::getType, SplitRecord.TYPE_REFUND)
                .ge(SplitRecord::getCreateTime, LocalDate.now().with(TemporalAdjusters.firstDayOfMonth()).atTime(LocalTime.MIN))
                .lt(SplitRecord::getCreateTime, LocalDate.now().with(TemporalAdjusters.firstDayOfMonth()).plusMonths(1).atTime(LocalTime.MIN))
                .list().stream().filter(r -> r.getAmount() != null).mapToInt(SplitRecord::getAmount).sum();
        var monthCrossExpend = splitRecordService.lambdaQuery()
                .eq(SplitRecord::getFromStationId, stationId)
                .eq(SplitRecord::getType, SplitRecord.TYPE_CROSS_EXPEND)
                .ge(SplitRecord::getCreateTime, LocalDate.now().with(TemporalAdjusters.firstDayOfMonth()).atTime(LocalTime.MIN))
                .lt(SplitRecord::getCreateTime, LocalDate.now().with(TemporalAdjusters.firstDayOfMonth()).plusMonths(1).atTime(LocalTime.MIN))
                .list().stream().filter(r -> r.getAmount() != null).mapToInt(SplitRecord::getAmount).sum();
        var monthIncome = monthRechargeAndCrossIncome - monthRefund - monthCrossExpend;
        var monthRegistrations = userService.countMonthRegister(LocalDate.now());
        var monthOrdersMap = washOrderService.countMonthOrders(LocalDate.now());

        // 累计数据
        var allOrders = washOrderService.lambdaQuery()
                .eq(WashOrder::getStationId, stationId)
                .list();
        var totalRechargeAndCrossIncome = splitRecordService.lambdaQuery()
                .eq(SplitRecord::getToStationId, stationId)
                .in(SplitRecord::getType, SplitRecord.TYPE_RECHARGE, SplitRecord.TYPE_CROSS_INCOME)
                .list().stream().filter(r -> r.getAmount() != null).mapToInt(SplitRecord::getAmount).sum();
        var totalRefund = splitRecordService.lambdaQuery()
                .eq(SplitRecord::getFromStationId, stationId)
                .eq(SplitRecord::getType, SplitRecord.TYPE_REFUND)
                .list().stream().filter(r -> r.getAmount() != null).mapToInt(SplitRecord::getAmount).sum();
        var totalCrossExpend = splitRecordService.lambdaQuery()
                .eq(SplitRecord::getFromStationId, stationId)
                .eq(SplitRecord::getType, SplitRecord.TYPE_CROSS_EXPEND)
                .list().stream().filter(r -> r.getAmount() != null).mapToInt(SplitRecord::getAmount).sum();
        var totalIncome = totalRechargeAndCrossIncome - totalRefund - totalCrossExpend;
        var totalConsumptionAmount = allOrders.stream().filter(o -> o.getAmount() != null).mapToInt(WashOrder::getAmount).sum();
        var totalMembers = userService.lambdaQuery().eq(User::getStationId, stationId).count().intValue();

        DashboardVo dashboardVo = new DashboardVo();
        dashboardVo.setTodayIncome(todayIncome);
        dashboardVo.setTodayConsumptionAmount(todayConsumptionAmount);
        dashboardVo.setTodayWashOrders(todayOrders.size());
        dashboardVo.setTodayWashUsers(todayWashUsers);
        dashboardVo.setTodayRefundAmount(todayRefund);
        dashboardVo.setTodayRegisteredMembers(CommUtil.isEmptyOrNull(todayRegistrations) ? 0 : todayRegistrations.getOrDefault(stationId, 0));

        dashboardVo.setMonthIncome(monthIncome);
        dashboardVo.setMonthConsumptionAmount(monthConsumptionAmount);
        dashboardVo.setMonthRegisteredMembers(monthRegistrations.getOrDefault(stationId, 0));
        dashboardVo.setMonthWashOrders(monthOrdersMap.getOrDefault(stationId, 0));

        dashboardVo.setTotalIncome(totalIncome);
        dashboardVo.setTotalConsumptionAmount(totalConsumptionAmount);
        dashboardVo.setTotalWashOrders(allOrders.size());
        dashboardVo.setTotalRegisteredMembers(totalMembers);

        
        dashboardVo.setAvgOrderPrice(avgOrderPrice); 
        dashboardVo.setAvgOrderDuration(avgOrderDuration); 
        // 平均洗车频率（次）
        dashboardVo.setAvgWashFrequency(0.0); // todo

        return dashboardVo;
    }


    /**
     * 统计站点的设备状态情况
     *
     * @param stationId
     * @return
     */
    @Override
    public Map<String, Long> washDeviceStatus(String stationId) {
        return washDeviceService.lambdaQuery().eq(WashDevice::getStationId, stationId).list()
                .stream().collect((Collectors.groupingBy(WashDevice::getState, Collectors.counting())));
    }

    @Override
    public List<StationTrendVo> trend(StatQueryParam params) {
        // 判断数据权限
        var adminStationIds = KymCache.INSTANCE.getAdminUserStationIds(StpUtil.getLoginIdAsLong());
        if (CommUtil.isEmptyOrNull(params.getStationId()) && CommUtil.isNotEmptyAndNull(adminStationIds)) {
            params.setStationId(adminStationIds.get(0));
        }
        if (CommUtil.isNotEmptyAndNull(params.getStationId()) && CommUtil.isNotEmptyAndNull(adminStationIds) && !adminStationIds.contains(params.getStationId())) {
            params.setStationId(adminStationIds.get(0));
        }


        return washOrderService.stationTrend(params);
    }
}