package com.kym.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.kym.common.utils.CommUtil;
import com.kym.entity.SplitRecord;
import com.kym.entity.WashOrder;
import com.kym.entity.common.PageBean;
import com.kym.entity.queryParams.SplitRecordQueryParams;
import com.kym.entity.vo.SplitRecordVo;
import com.kym.entity.vo.WashOrderVo;
import com.kym.mapper.SplitRecordMapper;
import com.kym.service.SplitRecordService;
import com.kym.service.cache.KymCache;
import com.kym.service.mybatisplus.MyBaseServiceImpl;
import org.springframework.beans.BeanUtils;
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.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * <p>
 * 平台分账记录表 服务实现类
 * </p>
 *
 * @author skyline
 * @since 2025-02-24
 */
@Service
public class SplitRecordServiceImpl extends MyBaseServiceImpl<SplitRecordMapper, SplitRecord> implements SplitRecordService {

    @Override
    public PageBean<SplitRecordVo> listSplitRecords(SplitRecordQueryParams params) {
        // 站点数据权限
        var adminStationIds = KymCache.INSTANCE.getAdminUserStationIds(StpUtil.getLoginIdAsLong());
        if (CommUtil.isEmptyOrNull(params.getStationId()) &&
                CommUtil.isNotEmptyAndNull(adminStationIds)) {
            params.setStationId(adminStationIds.get(0));
        }
        var page = PageHelper.startPage(params.getPageNum(), params.getPageSize());
        
        var queryWrapper = lambdaQuery()
                .like(CommUtil.isNotEmptyAndNull(params.getTradeNo()), SplitRecord::getTradeNo, params.getTradeNo())
                .eq(CommUtil.isNotEmptyAndNull(params.getType()), SplitRecord::getType,
                    CommUtil.isNotEmptyAndNull(params.getType()) ? Integer.valueOf(params.getType()) : null);
                
        if (CommUtil.isNotEmptyAndNull(params.getStationId())) {
            queryWrapper.and(wrapper -> wrapper.eq(SplitRecord::getFromStationId, params.getStationId())
                    .or()
                    .eq(SplitRecord::getToStationId, params.getStationId()));
        }
        
        var res = queryWrapper.orderByDesc(SplitRecord::getId).list();

        var voList = res.stream().map(item -> {
            var vo = new SplitRecordVo();
            BeanUtils.copyProperties(item, vo);
            vo.setFromStationName(KymCache.INSTANCE.getStationNameById(item.getFromStationId()));
            vo.setToStationName(KymCache.INSTANCE.getStationNameById(item.getToStationId()));
            return vo;
        }).toList();
        PageBean<SplitRecordVo> bean = new PageBean<>(voList);
        bean.setPages(page.getPages());
        bean.setPageNum(page.getPageNum());
        bean.setPageSize(page.getPageSize());
        bean.setTotal(page.getTotal());
        return bean;
    }

    /**
     * 站点日收入
     *
     * @param statDay 统计日期
     * @return 站点ID到金额的映射
     */
    @Override
    public Map<String, Integer> sumDailyIncome(LocalDate statDay, Boolean... isCross) {
        return sumStat(Boolean.TRUE, statDay, SplitRecord::getToStationId, isCross);
    }

    /**
     * 站点日支出
     *
     * @param statDay 统计日期
     * @return 站点ID到金额的映射
     */
    @Override
    public Map<String, Integer> sumDailyExpend(LocalDate statDay, Boolean... isCross) {
        return sumStat(Boolean.TRUE, statDay, SplitRecord::getFromStationId, isCross);
    }

    /**
     * 站点月收入
     *
     * @param statDay 统计日期
     * @return 站点ID到金额的映射
     */
    @Override
    public Map<String, Integer> sumMonthIncome(LocalDate statDay, Boolean... isCross) {
        return sumStat(Boolean.FALSE, statDay, SplitRecord::getToStationId, isCross);
    }

    /**
     * 站点月支出
     *
     * @param statDay 统计日期
     * @return 站点ID到金额的映射
     */
    @Override
    public Map<String, Integer> sumMonthExpend(LocalDate statDay, Boolean... isCross) {
        return sumStat(Boolean.FALSE, statDay, SplitRecord::getFromStationId, isCross);
    }

    @Override
    public Map<String, Integer> sumDailyRefund(LocalDate statDay) {
        return sumByType(Boolean.TRUE, statDay, SplitRecord::getFromStationId, SplitRecord.TYPE_REFUND);
    }

    @Override
    public Map<String, Integer> sumMonthRefund(LocalDate statDay) {
        return sumByType(Boolean.FALSE, statDay, SplitRecord::getFromStationId, SplitRecord.TYPE_REFUND);
    }

    @Override
    public Map<String, Integer> countDailyRecharge(LocalDate statDay) {
        return countByType(Boolean.TRUE, statDay, SplitRecord::getToStationId, SplitRecord.TYPE_RECHARGE);
    }

    @Override
    public Map<String, Integer> countMonthRecharge(LocalDate statDay) {
        return countByType(Boolean.FALSE, statDay, SplitRecord::getToStationId, SplitRecord.TYPE_RECHARGE);
    }

    @Override
    public Map<String, Integer> countDailyRefund(LocalDate statDay) {
        return countByType(Boolean.TRUE, statDay, SplitRecord::getFromStationId, SplitRecord.TYPE_REFUND);
    }

    @Override
    public Map<String, Integer> countMonthRefund(LocalDate statDay) {
        return countByType(Boolean.FALSE, statDay, SplitRecord::getFromStationId, SplitRecord.TYPE_REFUND);
    }

    @Override
    public Map<String, Integer> sumDailyRechargeIncome(LocalDate statDay) {
        return sumByType(Boolean.TRUE, statDay, SplitRecord::getToStationId, SplitRecord.TYPE_RECHARGE);
    }

    @Override
    public Map<String, Integer> sumMonthRechargeIncome(LocalDate statDay) {
        return sumByType(Boolean.FALSE, statDay, SplitRecord::getToStationId, SplitRecord.TYPE_RECHARGE);
    }

    @Override
    public Map<String, Integer> sumDailyCrossIncome(LocalDate statDay) {
        return sumByType(Boolean.TRUE, statDay, SplitRecord::getToStationId, SplitRecord.TYPE_CROSS_INCOME);
    }

    @Override
    public Map<String, Integer> sumMonthCrossIncome(LocalDate statDay) {
        return sumByType(Boolean.FALSE, statDay, SplitRecord::getToStationId, SplitRecord.TYPE_CROSS_INCOME);
    }

    @Override
    public Map<String, Integer> sumDailyCrossExpend(LocalDate statDay) {
        return sumByType(Boolean.TRUE, statDay, SplitRecord::getFromStationId, SplitRecord.TYPE_CROSS_EXPEND);
    }

    @Override
    public Map<String, Integer> sumMonthCrossExpend(LocalDate statDay) {
        return sumByType(Boolean.FALSE, statDay, SplitRecord::getFromStationId, SplitRecord.TYPE_CROSS_EXPEND);
    }

    private Map<String, Integer> sumByType(Boolean isDaily, LocalDate statDay, Function<SplitRecord, String> classifier, Integer type) {
        LocalDateTime startTime = isDaily ? getStartOfDay(statDay) : getStartOfMonth(statDay);
        LocalDateTime endTime = isDaily ? getEndOfDay(statDay) : getEndOfMonth(statDay);
        return lambdaQuery()
                .ge(SplitRecord::getCreateTime, startTime)
                .lt(SplitRecord::getCreateTime, endTime)
                .eq(SplitRecord::getType, type)
                .list()
                .stream()
                .filter(r -> r.getAmount() != null && classifier.apply(r) != null)
                .collect(Collectors.groupingBy(classifier, Collectors.summingInt(SplitRecord::getAmount)));
    }

    private Map<String, Integer> countByType(Boolean isDaily, LocalDate statDay, Function<SplitRecord, String> classifier, Integer type) {
        LocalDateTime startTime = isDaily ? getStartOfDay(statDay) : getStartOfMonth(statDay);
        LocalDateTime endTime = isDaily ? getEndOfDay(statDay) : getEndOfMonth(statDay);
        return lambdaQuery()
                .eq(SplitRecord::getType, type)
                .ge(SplitRecord::getCreateTime, startTime)
                .lt(SplitRecord::getCreateTime, endTime)
                .list()
                .stream()
                .collect(Collectors.groupingBy(classifier, Collectors.summingInt(o -> 1)));
    }

    /**
     * 公共统计方法
     *
     * @param statDay    统计日期
     * @param classifier 分组依据函数
     * @return 统计结果映射
     */
    public Map<String, Integer> sumStat(Boolean isDaily, LocalDate statDay, Function<SplitRecord, String> classifier, Boolean... isCross) {
        LocalDateTime startTime = isDaily ? getStartOfDay(statDay) : getStartOfMonth(statDay);
        LocalDateTime endTime = isDaily ? getEndOfDay(statDay) : getEndOfMonth(statDay);
        LambdaQueryWrapper<SplitRecord> wrapper = new LambdaQueryWrapper<>();
        wrapper.ge(SplitRecord::getCreateTime, startTime);
        wrapper.lt(SplitRecord::getCreateTime, endTime);
        if (isCross.length > 0 && isCross[0]) {
            wrapper.apply("from_station_id != to_station_id");
        }
        return list(wrapper)
                .stream()
                .collect(Collectors.toMap(
                        classifier,
                        SplitRecord::getAmount,
                        Integer::sum
                ));
    }

    /**
     * 获取日期起始时间
     */
    private LocalDateTime getStartOfDay(LocalDate date) {
        return date.atStartOfDay();
    }

    /**
     * 获取日期结束时间（次日零点，配合 lt 使用）
     */
    private LocalDateTime getEndOfDay(LocalDate date) {
        return date.plusDays(1).atStartOfDay();
    }

    /**
     * 获取月份起始时间
     */
    private LocalDateTime getStartOfMonth(LocalDate date) {
        return date.with(TemporalAdjusters.firstDayOfMonth()).atStartOfDay();
    }

    /**
     * 获取月份结束时间（下月首日零点，配合 lt 使用）
     */
    private LocalDateTime getEndOfMonth(LocalDate date) {
        return date.with(TemporalAdjusters.firstDayOfMonth()).plusMonths(1).atStartOfDay();
    }

}
