package com.kym.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import com.github.pagehelper.PageHelper;
import com.kym.common.utils.CommUtil;
import com.kym.entity.DailyStat;
import com.kym.entity.StatSyncLog;
import com.kym.entity.common.PageBean;
import com.kym.entity.queryParams.DailyStatQueryParam;
import com.kym.entity.vo.DailyStatSummaryVo;
import com.kym.entity.vo.DailyStatVo;
import com.kym.mapper.DailyStatMapper;
import com.kym.service.*;
import com.kym.service.cache.KymCache;
import com.kym.service.mybatisplus.MyBaseServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 日统计表 服务实现类
 *
 * @author skyline
 * @since 2025-02-19
 */
@Slf4j
@Service
public class DailyStatServiceImpl extends MyBaseServiceImpl<DailyStatMapper, DailyStat> implements DailyStatService {

    private final WashOrderService washOrderService;
    private final UserService userService;
    private final SplitRecordService splitRecordService;
    private final WashStationService washStationService;
    private final StatSyncLogService statSyncLogService;

    public DailyStatServiceImpl(WashOrderService washOrderService, UserService userService,
                                SplitRecordService splitRecordService, WashStationService washStationService,
                                StatSyncLogService statSyncLogService) {
        this.washOrderService = washOrderService;
        this.userService = userService;
        this.splitRecordService = splitRecordService;
        this.washStationService = washStationService;
        this.statSyncLogService = statSyncLogService;
    }

    @Override
    public PageBean<DailyStatVo> listDailyStat(DailyStatQueryParam params) {
        var query = lambdaQuery()
                .eq(CommUtil.isNotEmptyAndNull(params.getStationId()),
                        DailyStat::getStationId, params.getStationId())
                .ge(params.getStartDate() != null,
                        DailyStat::getStatDate, params.getStartDate() != null ? params.getStartDate().format(DateTimeFormatter.ISO_LOCAL_DATE) : null)
                .le(params.getEndDate() != null,
                        DailyStat::getStatDate, params.getEndDate() != null ? params.getEndDate().format(DateTimeFormatter.ISO_LOCAL_DATE) : null)
                .orderByDesc(DailyStat::getStatDate);

        var page = PageHelper.startPage(params.getPageNum(), params.getPageSize());
        var list = query.list();

        var voList = list.stream().map(item -> {
            var vo = new DailyStatVo();
            BeanUtils.copyProperties(item, vo);
            vo.setStationName(KymCache.INSTANCE.getStationNameById(item.getStationId()));
            return vo;
        }).toList();

        var bean = new PageBean<>(voList);
        bean.setPages(page.getPages());
        bean.setPageNum(page.getPageNum());
        bean.setPageSize(page.getPageSize());
        bean.setTotal(page.getTotal());
        return bean;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void syncDailyStat(String statDate) {
        LocalDate statDay = LocalDate.parse(statDate, DateTimeFormatter.ISO_LOCAL_DATE);
        log.info("手动同步日统计: {}", statDate);

        // 一次性聚合所有数据
        var amountMap = washOrderService.sumAmountByDate(statDay);
        var registerMap = userService.countDailyRegister(statDay);
        var ordersCountMap = washOrderService.countDailyOrders(statDay);
        var rechargeIncomeMap = splitRecordService.sumDailyRechargeIncome(statDay);
        var crossIncomeMap = splitRecordService.sumDailyCrossIncome(statDay);
        var crossExpendMap = splitRecordService.sumDailyCrossExpend(statDay);
        var refundMap = splitRecordService.sumDailyRefund(statDay);
        var rechargeCountMap = splitRecordService.countDailyRecharge(statDay);
        var refundCountMap = splitRecordService.countDailyRefund(statDay);
        var activeUserMap = washOrderService.countDailyActiveUsers(statDay);

        // 总入账 = 充值收入 + 跨店收入 - 退款 - 跨店支出
        var incomeMap = new HashMap<>(rechargeIncomeMap);
        crossIncomeMap.forEach((k, v) -> incomeMap.merge(k, v, Integer::sum));
        refundMap.forEach((k, v) -> incomeMap.merge(k, -v, Integer::sum));
        crossExpendMap.forEach((k, v) -> incomeMap.merge(k, -v, Integer::sum));

        // 收集所有出现过的站点（过滤掉无归属的null和"未分配"占位符）
        var allStationIds = Stream.of(amountMap, registerMap, ordersCountMap, incomeMap,
                        crossIncomeMap, crossExpendMap, refundMap, rechargeCountMap, refundCountMap, activeUserMap)
                .flatMap(m -> m.keySet().stream())
                .filter(s -> s != null && !"未分配".equals(s))
                .collect(Collectors.toSet());

        long operatorId = 0;
        String operatorName = "系统";
        try {
            operatorId = StpUtil.getLoginIdAsLong();
            operatorName = StpUtil.getLoginIdAsString();
        } catch (Exception ignored) {
        }

        var now = LocalDateTime.now();
        var statList = new ArrayList<DailyStat>();

        for (var stationId : allStationIds) {
            try {
                var stat = new DailyStat();
                stat.setStatDate(statDate);
                stat.setStationId(stationId);
                stat.setConsumption(amountMap.getOrDefault(stationId, 0));
                stat.setRegistrations(registerMap.getOrDefault(stationId, 0));
                stat.setOrdersCount(ordersCountMap.getOrDefault(stationId, 0));
                stat.setTotalIncome(incomeMap.getOrDefault(stationId, 0));
                stat.setCrossIncome(crossIncomeMap.getOrDefault(stationId, 0));
                stat.setCrossExpend(crossExpendMap.getOrDefault(stationId, 0));
                stat.setTotalRefund(refundMap.getOrDefault(stationId, 0));
                stat.setRechargeCount(rechargeCountMap.getOrDefault(stationId, 0));
                stat.setRefundCount(refundCountMap.getOrDefault(stationId, 0));
                stat.setActiveUserCount(activeUserMap.getOrDefault(stationId, 0));
                statList.add(stat);

                var syncLog = new StatSyncLog()
                        .setStatType(StatSyncLog.TYPE_DAILY)
                        .setStatKey(statDate)
                        .setStationId(stationId)
                        .setSyncStatus(StatSyncLog.STATUS_SUCCESS)
                        .setSyncBy(operatorId)
                        .setSyncByName(operatorName)
                        .setStartTime(now)
                        .setEndTime(LocalDateTime.now());
                statSyncLogService.save(syncLog);
            } catch (Exception e) {
                log.error("同步日统计失败: statDate={}, stationId={}", statDate, stationId, e);
                var syncLog = new StatSyncLog()
                        .setStatType(StatSyncLog.TYPE_DAILY)
                        .setStatKey(statDate)
                        .setStationId(stationId)
                        .setSyncStatus(StatSyncLog.STATUS_FAILED)
                        .setSyncBy(operatorId)
                        .setSyncByName(operatorName)
                        .setErrorMsg(e.getMessage())
                        .setStartTime(now)
                        .setEndTime(LocalDateTime.now());
                statSyncLogService.save(syncLog);
            }
        }

        if (!statList.isEmpty()) {
            replaceBatch(statList);
        }
        log.info("日统计同步完成: {}, 站点数: {}", statDate, statList.size());
    }

    @Override
    public void syncDailyStatBatch(String startDate, String endDate) {
        LocalDate start = LocalDate.parse(startDate, DateTimeFormatter.ISO_LOCAL_DATE);
        LocalDate end = LocalDate.parse(endDate, DateTimeFormatter.ISO_LOCAL_DATE);
        if (end.isBefore(start)) {
            throw new IllegalArgumentException("结束日期不能早于开始日期");
        }
        long days = start.until(end).getDays() + 1;
        if (days > 30) {
            throw new IllegalArgumentException("批量同步最多支持30天");
        }
        log.info("批量同步日统计: {} ~ {} (共{}天)", startDate, endDate, days);
        for (LocalDate d = start; !d.isAfter(end); d = d.plusDays(1)) {
            syncDailyStat(d.format(DateTimeFormatter.ISO_LOCAL_DATE));
        }
        log.info("批量同步日统计完成: {} ~ {}", startDate, endDate);
    }

    @Override
    public DailyStatSummaryVo aggregateDailyStat(DailyStatQueryParam params) {
        var query = lambdaQuery()
                .eq(CommUtil.isNotEmptyAndNull(params.getStationId()),
                        DailyStat::getStationId, params.getStationId())
                .ge(params.getStartDate() != null,
                        DailyStat::getStatDate, params.getStartDate() != null ? params.getStartDate().format(DateTimeFormatter.ISO_LOCAL_DATE) : null)
                .le(params.getEndDate() != null,
                        DailyStat::getStatDate, params.getEndDate() != null ? params.getEndDate().format(DateTimeFormatter.ISO_LOCAL_DATE) : null);
        var list = query.list();

        if (list.isEmpty()) {
            return new DailyStatSummaryVo()
                    .setTotalIncome(0)
                    .setTotalConsumption(0)
                    .setOrdersCount(0)
                    .setRegistrations(0)
                    .setActiveUserCount(0)
                    .setRechargeCount(0)
                    .setRefundCount(0);
        }

        var totalIncome = list.stream().filter(s -> s.getTotalIncome() != null).mapToInt(DailyStat::getTotalIncome).sum();
        var totalConsumption = list.stream().filter(s -> s.getConsumption() != null).mapToInt(DailyStat::getConsumption).sum();
        var ordersCount = list.stream().filter(s -> s.getOrdersCount() != null).mapToInt(DailyStat::getOrdersCount).sum();
        var registrations = list.stream().filter(s -> s.getRegistrations() != null).mapToInt(DailyStat::getRegistrations).sum();
        var rechargeCount = list.stream().filter(s -> s.getRechargeCount() != null).mapToInt(DailyStat::getRechargeCount).sum();
        var refundCount = list.stream().filter(s -> s.getRefundCount() != null).mapToInt(DailyStat::getRefundCount).sum();

        // 活跃用户：跨天需要去重，不能简单 SUM 日统计值，改为从订单源表 GROUP BY station_id, user_id 后计数
        var activeStartDate = params.getStartDate() != null ? params.getStartDate() : LocalDate.of(2020, 1, 1);
        var activeEndDate = params.getEndDate() != null ? params.getEndDate() : LocalDate.now();
        var activeUserMap = washOrderService.countDistinctActiveUsers(activeStartDate, activeEndDate);
        var activeUserCount = CommUtil.isNotEmptyAndNull(params.getStationId())
                ? activeUserMap.getOrDefault(params.getStationId(), 0)
                : activeUserMap.values().stream().mapToInt(Integer::intValue).sum();

        return new DailyStatSummaryVo()
                .setTotalIncome(totalIncome)
                .setTotalConsumption(totalConsumption)
                .setOrdersCount(ordersCount)
                .setRegistrations(registrations)
                .setActiveUserCount(activeUserCount)
                .setRechargeCount(rechargeCount)
                .setRefundCount(refundCount);
    }
}
