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.MonthStat;
import com.kym.entity.SettlementRecord;
import com.kym.entity.StatSyncLog;
import com.kym.entity.common.PageBean;
import com.kym.entity.queryParams.MonthStatQueryParam;
import com.kym.entity.vo.MonthStatVo;
import com.kym.mapper.MonthStatMapper;
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.YearMonth;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 月统计表 服务实现类
 *
 * @author skyline
 * @since 2025-04-03
 */
@Slf4j
@Service
public class MonthStatServiceImpl extends MyBaseServiceImpl<MonthStatMapper, MonthStat> implements MonthStatService {

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

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

    @Override
    public PageBean<MonthStatVo> listMonthStat(MonthStatQueryParam params) {
        var query = lambdaQuery()
                .eq(CommUtil.isNotEmptyAndNull(params.getStationId()),
                        MonthStat::getStationId, params.getStationId())
                .eq(CommUtil.isNotEmptyAndNull(params.getStatMonth()),
                        MonthStat::getStatMonth, params.getStatMonth())
                .orderByDesc(MonthStat::getStatMonth);

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

        var voList = list.stream().map(item -> {
            var vo = new MonthStatVo();
            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 syncMonthlyStat(String statMonth) {
        var yearMonth = YearMonth.parse(statMonth);
        var statDay = yearMonth.atDay(1);
        log.info("手动同步月统计: {}", statMonth);

        Map<String, Integer> amountMap = washOrderService.sumMonthAmount(statDay);
        Map<String, Integer> registerMap = userService.countMonthRegister(statDay);
        Map<String, Integer> ordersCountMap = washOrderService.countMonthOrders(statDay);
        var rechargeIncomeMap = splitRecordService.sumMonthRechargeIncome(statDay);
        var crossIncomeMap = splitRecordService.sumMonthCrossIncome(statDay);
        var crossExpendMap = splitRecordService.sumMonthCrossExpend(statDay);
        var refundMap = splitRecordService.sumMonthRefund(statDay);
        var rechargeCountMap = splitRecordService.countMonthRecharge(statDay);
        var refundCountMap = splitRecordService.countMonthRefund(statDay);
        var activeUserMap = washOrderService.countMonthActiveUsers(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));

        var platformFeeMap = settlementService.lambdaQuery()
                .eq(SettlementRecord::getSettlementPeriod, statMonth)
                .list()
                .stream()
                .collect(Collectors.toMap(
                        SettlementRecord::getStationId,
                        SettlementRecord::getPlatformFee,
                        Integer::sum));

        var allStationIds = Stream.of(amountMap, registerMap, ordersCountMap, incomeMap,
                        crossIncomeMap, crossExpendMap, refundMap, rechargeCountMap, refundCountMap,
                        activeUserMap, platformFeeMap)
                .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<MonthStat>();

        for (var stationId : allStationIds) {
            try {
                var stat = new MonthStat();
                stat.setStatMonth(statMonth);
                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.setPlatformFee(platformFeeMap.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_MONTHLY)
                        .setStatKey(statMonth)
                        .setStationId(stationId)
                        .setSyncStatus(StatSyncLog.STATUS_SUCCESS)
                        .setSyncBy(operatorId)
                        .setSyncByName(operatorName)
                        .setStartTime(now)
                        .setEndTime(LocalDateTime.now());
                statSyncLogService.save(syncLog);
            } catch (Exception e) {
                log.error("同步月统计失败: statMonth={}, stationId={}", statMonth, stationId, e);
                var syncLog = new StatSyncLog()
                        .setStatType(StatSyncLog.TYPE_MONTHLY)
                        .setStatKey(statMonth)
                        .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("月统计同步完成: {}, 站点数: {}", statMonth, statList.size());
    }
}
