package com.zsElectric.boot.system.service.impl;

import cn.hutool.core.util.StrUtil;
import com.zsElectric.boot.system.mapper.DataBoardMapper;
import com.zsElectric.boot.system.model.dto.ChurnUserExportDTO;
import com.zsElectric.boot.system.model.query.HistoryBusinessQuery;
import com.zsElectric.boot.system.model.query.StationRankQuery;
import com.zsElectric.boot.system.model.vo.*;
import com.zsElectric.boot.system.service.DataBoardService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * 数据看板服务实现类
 *
 * @author zsElectric
 * @since 2026-03-09
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class DataBoardServiceImpl implements DataBoardService {

    private final DataBoardMapper dataBoardMapper;

    private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
    private static final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    private static final DateTimeFormatter MM_DD_FORMATTER = DateTimeFormatter.ofPattern("MM-dd");

    @Override
    public DataBoardRealTimeVO getRealTimeData() {
        DataBoardRealTimeVO realTimeVO = dataBoardMapper.selectRealTimeData();
        if (realTimeVO == null) {
            realTimeVO = new DataBoardRealTimeVO();
        }
        // 获取累计退款金额
        BigDecimal totalRefundAmount = dataBoardMapper.selectTotalRefundAmount();
        realTimeVO.setTotalRefundAmount(totalRefundAmount != null ? totalRefundAmount : BigDecimal.ZERO);

        // 获取累计首单金额
        BigDecimal totalFirstOrderAmount = dataBoardMapper.selectTotalFirstOrderAmount();
        realTimeVO.setTotalFirstOrderAmount(totalFirstOrderAmount != null ? totalFirstOrderAmount : BigDecimal.ZERO);

        // 确保所有字段非空
        ensureRealTimeDataNotNull(realTimeVO);
        return realTimeVO;
    }

    @Override
    public DataBoardTodayVO getTodayData() {
        LocalDate today = LocalDate.now();
        String todayStart = today.atStartOfDay().format(DATETIME_FORMATTER);
        String todayEnd = today.atTime(23, 59, 59).format(DATETIME_FORMATTER);

        DataBoardTodayVO todayVO = dataBoardMapper.selectTodayData(todayStart, todayEnd);
        if (todayVO == null) {
            todayVO = new DataBoardTodayVO();
        }

        // 获取今日退款金额
        BigDecimal todayRefundAmount = dataBoardMapper.selectTodayRefundAmount(todayStart, todayEnd);
        todayVO.setTodayRefundAmount(todayRefundAmount != null ? todayRefundAmount : BigDecimal.ZERO);

        // 获取今日首单金额
        BigDecimal todayFirstOrderAmount = dataBoardMapper.selectTodayFirstOrderAmount(todayStart, todayEnd);
        todayVO.setTodayFirstOrderAmount(todayFirstOrderAmount != null ? todayFirstOrderAmount : BigDecimal.ZERO);

        // 确保所有字段非空
        ensureTodayDataNotNull(todayVO);
        return todayVO;
    }

    @Override
    public ChargePowerTrendVO getChargePowerTrend(String compareDate) {
        ChargePowerTrendVO trendVO = new ChargePowerTrendVO();

        // 生成0-23小时列表
        List<Integer> hours = IntStream.rangeClosed(0, 23).boxed().collect(Collectors.toList());
        trendVO.setHours(hours);

        // 今日数据
        LocalDate today = LocalDate.now();
        String todayStart = today.atStartOfDay().format(DATETIME_FORMATTER);
        String todayEnd = today.atTime(23, 59, 59).format(DATETIME_FORMATTER);
        List<Map<String, Object>> todayHourlyData = dataBoardMapper.selectHourlyChargePower(todayStart, todayEnd);
        Map<Integer, BigDecimal> todayMap = convertHourlyDataToMap(todayHourlyData);
        List<BigDecimal> todayData = hours.stream()
                .map(hour -> todayMap.getOrDefault(hour, BigDecimal.ZERO))
                .collect(Collectors.toList());
        trendVO.setTodayData(todayData);

        // 对比日数据
        LocalDate compareDateParsed;
        if (StrUtil.isNotBlank(compareDate)) {
            compareDateParsed = LocalDate.parse(compareDate, DATE_FORMATTER);
        } else {
            // 默认对比昨天
            compareDateParsed = today.minusDays(1);
        }
        String compareDateStart = compareDateParsed.atStartOfDay().format(DATETIME_FORMATTER);
        String compareDateEnd = compareDateParsed.atTime(23, 59, 59).format(DATETIME_FORMATTER);
        List<Map<String, Object>> compareHourlyData = dataBoardMapper.selectHourlyChargePower(compareDateStart, compareDateEnd);
        Map<Integer, BigDecimal> compareMap = convertHourlyDataToMap(compareHourlyData);
        List<BigDecimal> compareData = hours.stream()
                .map(hour -> compareMap.getOrDefault(hour, BigDecimal.ZERO))
                .collect(Collectors.toList());
        trendVO.setCompareData(compareData);
        trendVO.setCompareDate(compareDateParsed.format(DATE_FORMATTER));

        return trendVO;
    }

    @Override
    public HistoryBusinessDataVO getHistoryBusinessData(HistoryBusinessQuery query) {
        HistoryBusinessDataVO dataVO = new HistoryBusinessDataVO();
        dataVO.setDataType(query.getDataType());
        dataVO.setTimeDimension(query.getTimeDimension());

        LocalDate today = LocalDate.now();
        List<String> labels = new ArrayList<>();
        List<BigDecimal> values = new ArrayList<>();

        String dataType = query.getDataType();
        if (StrUtil.isBlank(dataType)) {
            dataType = "chargePower";
        }

        if ("month".equalsIgnoreCase(query.getTimeDimension())) {
            // 月趋势：yearMonth 格式为 yyyy，查询该年每月汇总数据
            String yearStr = query.getYearMonth();
            if (yearStr.contains("-")) {
                throw new IllegalArgumentException("月趋势时yearMonth格式应为yyyy（如2026），当前传入：" + yearStr);
            }
            int year = Integer.parseInt(yearStr);
            int currentYear = today.getYear();
            int endMonth = (year == currentYear) ? today.getMonthValue() : 12;

            for (int month = 1; month <= endMonth; month++) {
                YearMonth ym = YearMonth.of(year, month);
                LocalDate monthStart = ym.atDay(1);
                LocalDate monthEnd = ym.atEndOfMonth();
                // 如果是当月，截止到今天
                if (year == currentYear && month == today.getMonthValue()) {
                    monthEnd = today;
                }

                String startDateStr = monthStart.format(DATE_FORMATTER);
                String endDateStr = monthEnd.format(DATE_FORMATTER);

                BigDecimal monthValue = getMonthlyDataByType(dataType, startDateStr, endDateStr);
                labels.add(String.format("%02d", month));
                values.add(monthValue);
            }
        } else {
            // 日趋势：yearMonth 格式为 yyyy-MM，查询该月每日数据
            String yearMonthStr = query.getYearMonth();
            if (!yearMonthStr.contains("-")) {
                throw new IllegalArgumentException("日趋势时yearMonth格式应为yyyy-MM（如2026-03），当前传入：" + yearMonthStr);
            }
            YearMonth yearMonth = YearMonth.parse(yearMonthStr);
            LocalDate startDate = yearMonth.atDay(1);
            LocalDate endDate;
            // 如果是当月则到今天
            if (yearMonth.equals(YearMonth.from(today))) {
                endDate = today;
            } else {
                endDate = yearMonth.atEndOfMonth();
            }

            String startDateStr = startDate.format(DATE_FORMATTER);
            String endDateStr = endDate.format(DATE_FORMATTER);

            List<Map<String, Object>> rawData = getDailyDataByType(dataType, startDateStr, endDateStr);

            // 转换数据
            Map<String, BigDecimal> dataMap = new LinkedHashMap<>();
            for (Map<String, Object> item : rawData) {
                String label = String.valueOf(item.get("dateLabel"));
                BigDecimal value = item.get("value") != null ?
                        new BigDecimal(String.valueOf(item.get("value"))) : BigDecimal.ZERO;
                dataMap.put(label, value);
            }

            // 填充所有日期
            LocalDate current = startDate;
            DateTimeFormatter labelFormatter = DateTimeFormatter.ofPattern("MM.dd");
            while (!current.isAfter(endDate)) {
                String label = current.format(labelFormatter);
                labels.add(label);
                values.add(dataMap.getOrDefault(label, BigDecimal.ZERO));
                current = current.plusDays(1);
            }
        }

        dataVO.setLabels(labels);
        dataVO.setValues(values);
        return dataVO;
    }

    /**
     * 根据数据类型获取每日数据
     */
    private List<Map<String, Object>> getDailyDataByType(String dataType, String startDateStr, String endDateStr) {
        switch (dataType) {
            case "chargeAmount":
                return dataBoardMapper.selectDailyChargeAmount(startDateStr, endDateStr);
            case "validOrders":
                return dataBoardMapper.selectDailyValidOrders(startDateStr, endDateStr);
            case "registerUsers":
                return dataBoardMapper.selectDailyRegisterUsers(startDateStr, endDateStr);
            case "chargePower":
            default:
                return dataBoardMapper.selectDailyChargePower(startDateStr, endDateStr);
        }
    }

    /**
     * 根据数据类型获取月度汇总数据
     */
    private BigDecimal getMonthlyDataByType(String dataType, String startDateStr, String endDateStr) {
        List<Map<String, Object>> rawData = getDailyDataByType(dataType, startDateStr, endDateStr);
        BigDecimal total = BigDecimal.ZERO;
        for (Map<String, Object> item : rawData) {
            BigDecimal value = item.get("value") != null ?
                    new BigDecimal(String.valueOf(item.get("value"))) : BigDecimal.ZERO;
            total = total.add(value);
        }
        return total;
    }

    @Override
    public StationRankListVO getStationRankData(StationRankQuery query) {
        StationRankListVO rankListVO = new StationRankListVO();

        // 计算时间范围
        LocalDate startDate;
        LocalDate endDate = LocalDate.now();
        String timeRange = query.getTimeRange();
        if (StrUtil.isBlank(timeRange)) {
            timeRange = "week";
        }

        switch (timeRange) {
            case "today":
                startDate = endDate;
                break;
            case "month":
                startDate = endDate.minusDays(29);
                break;
            case "custom":
                if (StrUtil.isNotBlank(query.getStartDate()) && StrUtil.isNotBlank(query.getEndDate())) {
                    startDate = LocalDate.parse(query.getStartDate(), DATE_FORMATTER);
                    endDate = LocalDate.parse(query.getEndDate(), DATE_FORMATTER);
                } else {
                    startDate = endDate.minusDays(6);
                }
                break;
            case "week":
            default:
                startDate = endDate.minusDays(6);
                break;
        }

        String startDateStr = startDate.format(DATE_FORMATTER);
        String endDateStr = endDate.format(DATE_FORMATTER);

        // 计算前一个周期的日期范围（用于波动计算）
        long daysDiff = java.time.temporal.ChronoUnit.DAYS.between(startDate, endDate) + 1;
        LocalDate prevStartDate = startDate.minusDays(daysDiff);
        LocalDate prevEndDate = startDate.minusDays(1);
        String prevStartDateStr = prevStartDate.format(DATE_FORMATTER);
        String prevEndDateStr = prevEndDate.format(DATE_FORMATTER);

        // 获取热门充电站
        List<StationRankVO> hotStations = dataBoardMapper.selectHotStations(startDateStr, endDateStr, 5);
        // 设置排名
        for (int i = 0; i < hotStations.size(); i++) {
            hotStations.get(i).setRank(i + 1);
        }
        rankListVO.setHotStations(hotStations);

        // 计算波动充电站（基于充电度数波动）
        List<StationRankVO> fluctuationStations = calculateFluctuationStations(
                hotStations, prevStartDateStr, prevEndDateStr, startDateStr, endDateStr);
        rankListVO.setFluctuationStations(fluctuationStations);

        // 设置日期范围描述
        rankListVO.setDateRange(startDateStr + " 至 " + endDateStr);

        return rankListVO;
    }

    @Override
    public UserChurnRateVO getUserChurnRate() {
        UserChurnRateVO churnRateVO = new UserChurnRateVO();
        LocalDate today = LocalDate.now();
        LocalDate yesterday = today.minusDays(1);

        // 计算近7天流失率（近7天=昨天之前7天，含昨天，即 today-7 到 today-1）
        // 早期用户：7天之前有充电的用户（today-14 到 today-8）
        // 流失判断：这些用户在近7天内（today-7 到 today-1）没有充电
        BigDecimal sevenDayRate = calculateChurnRate(
                today.minusDays(14), today.minusDays(8),
                today.minusDays(7), yesterday);
        churnRateVO.setSevenDayChurnRate(sevenDayRate);

        // 计算近1个月流失率（近1个月=昨天之前30天，含昨天，即 today-30 到 today-1）
        // 早期用户：30天之前有充电的用户（today-60 到 today-31）
        // 流失判断：这些用户在近30天内（today-30 到 today-1）没有充电
        BigDecimal oneMonthRate = calculateChurnRate(
                today.minusDays(60), today.minusDays(31),
                today.minusDays(30), yesterday);
        churnRateVO.setOneMonthChurnRate(oneMonthRate);

        // 计算近3个月流失率（近3个月=昨天之前90天，含昨天，即 today-90 到 today-1）
        // 早期用户：90天之前有充电的用户（today-180 到 today-91）
        // 流失判断：这些用户在近90天内（today-90 到 today-1）没有充电
        BigDecimal threeMonthRate = calculateChurnRate(
                today.minusDays(180), today.minusDays(91),
                today.minusDays(90), yesterday);
        churnRateVO.setThreeMonthChurnRate(threeMonthRate);

        return churnRateVO;
    }

    /**
     * 计算流失率
     */
    private BigDecimal calculateChurnRate(LocalDate earlyStart, LocalDate earlyEnd,
                                          LocalDate recentStart, LocalDate recentEnd) {
        String earlyStartStr = earlyStart.format(DATE_FORMATTER);
        String earlyEndStr = earlyEnd.format(DATE_FORMATTER);
        String recentStartStr = recentStart.format(DATE_FORMATTER);
        String recentEndStr = recentEnd.format(DATE_FORMATTER);

        Long activeCount = dataBoardMapper.selectActiveUserCount(earlyStartStr, earlyEndStr);
        if (activeCount == null || activeCount == 0) {
            return BigDecimal.ZERO;
        }

        Long churnCount = dataBoardMapper.selectChurnUserCount(earlyStartStr, earlyEndStr, recentStartStr, recentEndStr);
        if (churnCount == null) {
            churnCount = 0L;
        }

        return BigDecimal.valueOf(churnCount)
                .multiply(BigDecimal.valueOf(100))
                .divide(BigDecimal.valueOf(activeCount), 2, RoundingMode.HALF_UP);
    }

    /**
     * 计算波动充电站
     */
    private List<StationRankVO> calculateFluctuationStations(List<StationRankVO> hotStations,
                                                              String prevStartDate, String prevEndDate,
                                                              String currStartDate, String currEndDate) {
        List<StationRankVO> fluctuationStations = new ArrayList<>();

        for (StationRankVO station : hotStations) {
            if (station.getStationId() == null) {
                continue;
            }
            StationRankVO fluctuationStation = new StationRankVO();
            fluctuationStation.setRank(station.getRank());
            fluctuationStation.setStationId(station.getStationId());
            fluctuationStation.setStationName(station.getStationName());
            fluctuationStation.setChargePower(station.getChargePower());

            // 获取前一周期的充电度数
            BigDecimal prevPower = dataBoardMapper.selectStationChargePower(
                    String.valueOf(station.getStationId()), prevStartDate, prevEndDate);
            if (prevPower == null) {
                prevPower = BigDecimal.ZERO;
            }

            BigDecimal currPower = station.getChargePower();
            if (currPower == null) {
                currPower = BigDecimal.ZERO;
            }

            // 计算波动百分比
            if (prevPower.compareTo(BigDecimal.ZERO) == 0) {
                if (currPower.compareTo(BigDecimal.ZERO) > 0) {
                    fluctuationStation.setFluctuation(BigDecimal.valueOf(100));
                    fluctuationStation.setFluctuationDirection("up");
                } else {
                    fluctuationStation.setFluctuation(BigDecimal.ZERO);
                    fluctuationStation.setFluctuationDirection("up");
                }
            } else {
                BigDecimal diff = currPower.subtract(prevPower);
                BigDecimal fluctuation = diff.multiply(BigDecimal.valueOf(100))
                        .divide(prevPower, 2, RoundingMode.HALF_UP).abs();
                fluctuationStation.setFluctuation(fluctuation);
                fluctuationStation.setFluctuationDirection(diff.compareTo(BigDecimal.ZERO) >= 0 ? "up" : "down");
            }

            fluctuationStations.add(fluctuationStation);
        }

        // 按波动幅度排序
        fluctuationStations.sort((a, b) -> b.getFluctuation().compareTo(a.getFluctuation()));

        // 重新设置排名
        for (int i = 0; i < fluctuationStations.size(); i++) {
            fluctuationStations.get(i).setRank(i + 1);
        }

        return fluctuationStations;
    }

    /**
     * 将小时数据转换为Map
     */
    private Map<Integer, BigDecimal> convertHourlyDataToMap(List<Map<String, Object>> hourlyData) {
        Map<Integer, BigDecimal> map = new HashMap<>();
        for (Map<String, Object> item : hourlyData) {
            Integer hour = ((Number) item.get("hour")).intValue();
            BigDecimal power = item.get("chargePower") != null ?
                    new BigDecimal(String.valueOf(item.get("chargePower"))) : BigDecimal.ZERO;
            map.put(hour, power);
        }
        return map;
    }

    /**
     * 确保实时数据所有字段非空
     */
    private void ensureRealTimeDataNotNull(DataBoardRealTimeVO vo) {
        if (vo.getTotalChargePower() == null) vo.setTotalChargePower(BigDecimal.ZERO);
        if (vo.getTotalChargeAmount() == null) vo.setTotalChargeAmount(BigDecimal.ZERO);
        if (vo.getTotalDiscountAmount() == null) vo.setTotalDiscountAmount(BigDecimal.ZERO);
        if (vo.getTotalServiceFeeAmount() == null) vo.setTotalServiceFeeAmount(BigDecimal.ZERO);
        if (vo.getTotalRefundAmount() == null) vo.setTotalRefundAmount(BigDecimal.ZERO);
        if (vo.getTotalActualPayAmount() == null) vo.setTotalActualPayAmount(BigDecimal.ZERO);
        if (vo.getTotalFirstOrderAmount() == null) vo.setTotalFirstOrderAmount(BigDecimal.ZERO);
        if (vo.getTotalCouponAmount() == null) vo.setTotalCouponAmount(BigDecimal.ZERO);
        if (vo.getTotalFirmDiscountAmount() == null) vo.setTotalFirmDiscountAmount(BigDecimal.ZERO);
        if (vo.getTotalCommissionAmount() == null) vo.setTotalCommissionAmount(BigDecimal.ZERO);
    }

    /**
     * 确保今日数据所有字段非空
     */
    private void ensureTodayDataNotNull(DataBoardTodayVO vo) {
        if (vo.getTodayChargePower() == null) vo.setTodayChargePower(BigDecimal.ZERO);
        if (vo.getTodayChargeAmount() == null) vo.setTodayChargeAmount(BigDecimal.ZERO);
        if (vo.getTodayDiscountAmount() == null) vo.setTodayDiscountAmount(BigDecimal.ZERO);
        if (vo.getTodayServiceFeeAmount() == null) vo.setTodayServiceFeeAmount(BigDecimal.ZERO);
        if (vo.getTodayRefundAmount() == null) vo.setTodayRefundAmount(BigDecimal.ZERO);
        if (vo.getTodayActualPayAmount() == null) vo.setTodayActualPayAmount(BigDecimal.ZERO);
        if (vo.getTodayFirstOrderAmount() == null) vo.setTodayFirstOrderAmount(BigDecimal.ZERO);
        if (vo.getTodayCouponAmount() == null) vo.setTodayCouponAmount(BigDecimal.ZERO);
        if (vo.getTodayFirmDiscountAmount() == null) vo.setTodayFirmDiscountAmount(BigDecimal.ZERO);
        if (vo.getTodayCommissionAmount() == null) vo.setTodayCommissionAmount(BigDecimal.ZERO);
    }

    @Override
    public List<ChurnUserExportDTO> getChurnUserExportList(String periodType) {
        LocalDate today = LocalDate.now();
        LocalDate yesterday = today.minusDays(1);

        LocalDate earlyStart;
        LocalDate earlyEnd;
        LocalDate recentStart;
        LocalDate recentEnd = yesterday;

        // 根据时段类型计算日期范围
        switch (periodType) {
            case "7d":
                // 近7天流失用户
                earlyStart = today.minusDays(14);
                earlyEnd = today.minusDays(8);
                recentStart = today.minusDays(7);
                break;
            case "3m":
                // 近3个月流失用户
                earlyStart = today.minusDays(180);
                earlyEnd = today.minusDays(91);
                recentStart = today.minusDays(90);
                break;
            case "1m":
            default:
                // 近1个月流失用户（默认）
                earlyStart = today.minusDays(60);
                earlyEnd = today.minusDays(31);
                recentStart = today.minusDays(30);
                break;
        }

        List<ChurnUserExportDTO> list = dataBoardMapper.selectChurnUserList(
                earlyStart.format(DATE_FORMATTER),
                earlyEnd.format(DATE_FORMATTER),
                recentStart.format(DATE_FORMATTER),
                recentEnd.format(DATE_FORMATTER)
        );

        // 设置序号
        for (int i = 0; i < list.size(); i++) {
            list.get(i).setRowNum(i + 1);
        }
        return list;
    }
}
