package com.ruoyi.web.modules.trading.support;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.web.modules.trading.domain.dto.AppDistributorAmountBucketDto;
import com.ruoyi.web.modules.trading.domain.dto.AppDistributorTradeSummaryDto;
import com.ruoyi.web.modules.trading.domain.vo.AppDistributorAmountTrendPointVo;
import com.ruoyi.web.modules.trading.domain.vo.AppDistributorHomeDashboardVo;
import com.ruoyi.web.modules.trading.domain.vo.AppDistributorTradeSummaryVo;

/**
 * 承销商首页统计：时段解析、指标与趋势序列组装。
 */
public final class AppDistributorHomeSupport
{
    private static final ZoneId ZONE = ZoneId.systemDefault();

    private static final DateTimeFormatter DATE_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd");

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

    private static final DateTimeFormatter CHART_DAY_LABEL = DateTimeFormatter.ofPattern("MM-dd");

    private AppDistributorHomeSupport()
    {
    }

    public static String normalizeRange(String range)
    {
        if (StringUtils.isEmpty(range))
        {
            return AppDistributorHomeRules.RANGE_TODAY;
        }
        String key = range.trim().toUpperCase();
        switch (key)
        {
            case AppDistributorHomeRules.RANGE_TODAY:
            case AppDistributorHomeRules.RANGE_YESTERDAY:
            case AppDistributorHomeRules.RANGE_DAYS_7:
            case AppDistributorHomeRules.RANGE_DAYS_30:
                return key;
            default:
                throw new ServiceException(AppDistributorHomeRules.MSG_RANGE_INVALID);
        }
    }

    public static String rangeName(String range)
    {
        switch (range)
        {
            case AppDistributorHomeRules.RANGE_TODAY:
                return "今日";
            case AppDistributorHomeRules.RANGE_YESTERDAY:
                return "昨日";
            case AppDistributorHomeRules.RANGE_DAYS_7:
                return "7日";
            case AppDistributorHomeRules.RANGE_DAYS_30:
                return "30日";
            default:
                return "";
        }
    }

    public static DateRange resolveRange(String range)
    {
        LocalDate today = LocalDate.now(ZONE);
        switch (range)
        {
            case AppDistributorHomeRules.RANGE_TODAY:
                return new DateRange(today, today, AppDistributorHomeRules.TREND_GRANULARITY_HOUR);
            case AppDistributorHomeRules.RANGE_YESTERDAY:
                LocalDate yesterday = today.minusDays(1);
                return new DateRange(yesterday, yesterday, AppDistributorHomeRules.TREND_GRANULARITY_HOUR);
            case AppDistributorHomeRules.RANGE_DAYS_7:
                return new DateRange(
                        today.minusDays(AppDistributorHomeRules.DAYS_7_WINDOW - 1L),
                        today,
                        AppDistributorHomeRules.TREND_GRANULARITY_DAY);
            case AppDistributorHomeRules.RANGE_DAYS_30:
                return new DateRange(
                        today.minusDays(AppDistributorHomeRules.DAYS_30_WINDOW - 1L),
                        today,
                        AppDistributorHomeRules.TREND_GRANULARITY_DAY);
            default:
                throw new ServiceException(AppDistributorHomeRules.MSG_RANGE_INVALID);
        }
    }

    public static AppDistributorHomeDashboardVo buildDashboard(
            String range,
            AppDistributorTradeSummaryDto summaryRaw,
            List<AppDistributorAmountBucketDto> bucketRows,
            DateRange dateRange)
    {
        AppDistributorHomeDashboardVo vo = new AppDistributorHomeDashboardVo();
        vo.setRange(range);
        vo.setRangeName(rangeName(range));
        vo.setUpdatedAt(LocalDateTime.now(ZONE).format(DATETIME_FMT));
        vo.setTrendGranularity(dateRange.trendGranularity);
        vo.setSummary(buildSummary(summaryRaw));
        if (AppDistributorHomeRules.TREND_GRANULARITY_HOUR.equals(dateRange.trendGranularity))
        {
            vo.setAmountTrend(buildHourlyTrend(bucketRows));
        }
        else
        {
            vo.setAmountTrend(buildDailyTrend(bucketRows, dateRange.startDay, dateRange.endDay));
        }
        return vo;
    }

    public static AppDistributorTradeSummaryVo buildSummary(AppDistributorTradeSummaryDto raw)
    {
        long orderCount = raw != null && raw.getOrderCount() != null ? raw.getOrderCount() : 0L;
        long completed = raw != null && raw.getCompletedCount() != null ? raw.getCompletedCount() : 0L;
        long heads = raw != null && raw.getTotalHeads() != null ? raw.getTotalHeads() : 0L;
        long supplierCount = raw != null && raw.getSupplierCount() != null ? raw.getSupplierCount() : 0L;
        BigDecimal amount = raw != null && raw.getTotalAmount() != null
                ? raw.getTotalAmount() : BigDecimal.ZERO;

        AppDistributorTradeSummaryVo vo = new AppDistributorTradeSummaryVo();
        vo.setOrderCount(orderCount);
        vo.setTotalAmount(amount);
        vo.setTotalHeads(heads);
        vo.setSupplierCount(supplierCount);
        vo.setCompletionRate(ratioPercent(completed, orderCount));
        vo.setAvgUnitPrice(avgUnitPrice(amount, heads));
        return vo;
    }

    public static List<AppDistributorAmountTrendPointVo> buildHourlyTrend(
            List<AppDistributorAmountBucketDto> rows)
    {
        Map<Integer, BigDecimal> byHour = toHourAmountMap(rows);
        List<AppDistributorAmountTrendPointVo> series = new ArrayList<>(AppDistributorHomeRules.HOURLY_BUCKETS);
        for (int h = 0; h < AppDistributorHomeRules.HOURLY_BUCKETS; h++)
        {
            AppDistributorAmountTrendPointVo point = new AppDistributorAmountTrendPointVo();
            point.setBucketLabel(String.format("%02d:00", h));
            point.setAmount(byHour.getOrDefault(h, BigDecimal.ZERO));
            series.add(point);
        }
        return series;
    }

    public static List<AppDistributorAmountTrendPointVo> buildDailyTrend(
            List<AppDistributorAmountBucketDto> rows, LocalDate startDay, LocalDate endDay)
    {
        Map<String, BigDecimal> byDay = toDayAmountMap(rows);
        List<AppDistributorAmountTrendPointVo> series = new ArrayList<>();
        for (LocalDate d = startDay; !d.isAfter(endDay); d = d.plusDays(1))
        {
            String key = d.format(DATE_FMT);
            AppDistributorAmountTrendPointVo point = new AppDistributorAmountTrendPointVo();
            point.setBucketLabel(CHART_DAY_LABEL.format(d));
            point.setAmount(byDay.getOrDefault(key, BigDecimal.ZERO));
            series.add(point);
        }
        return series;
    }

    public static Date toDayStart(LocalDate day)
    {
        return Date.from(day.atStartOfDay(ZONE).toInstant());
    }

    public static Date toDayEnd(LocalDate day)
    {
        return Date.from(day.atTime(23, 59, 59).atZone(ZONE).toInstant());
    }

    private static Map<Integer, BigDecimal> toHourAmountMap(List<AppDistributorAmountBucketDto> rows)
    {
        Map<Integer, BigDecimal> map = new HashMap<>();
        if (rows == null)
        {
            return map;
        }
        for (AppDistributorAmountBucketDto row : rows)
        {
            if (row == null || row.getBucketKey() == null)
            {
                continue;
            }
            try
            {
                map.put(Integer.parseInt(row.getBucketKey().trim()),
                        row.getAmount() != null ? row.getAmount() : BigDecimal.ZERO);
            }
            catch (NumberFormatException ignored)
            {
                // skip malformed bucket
            }
        }
        return map;
    }

    private static Map<String, BigDecimal> toDayAmountMap(List<AppDistributorAmountBucketDto> rows)
    {
        Map<String, BigDecimal> map = new HashMap<>();
        if (rows == null)
        {
            return map;
        }
        for (AppDistributorAmountBucketDto row : rows)
        {
            if (row != null && row.getBucketKey() != null)
            {
                map.put(row.getBucketKey(),
                        row.getAmount() != null ? row.getAmount() : BigDecimal.ZERO);
            }
        }
        return map;
    }

    private static BigDecimal ratioPercent(long part, long total)
    {
        if (total <= 0)
        {
            return BigDecimal.ZERO.setScale(1, RoundingMode.HALF_UP);
        }
        return BigDecimal.valueOf(part)
                .multiply(BigDecimal.valueOf(100))
                .divide(BigDecimal.valueOf(total), 1, RoundingMode.HALF_UP);
    }

    private static BigDecimal avgUnitPrice(BigDecimal amount, long heads)
    {
        if (heads <= 0)
        {
            return BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
        }
        return amount.divide(BigDecimal.valueOf(heads), 2, RoundingMode.HALF_UP);
    }

    public static final class DateRange
    {
        public final LocalDate startDay;

        public final LocalDate endDay;

        public final String trendGranularity;

        public DateRange(LocalDate startDay, LocalDate endDay, String trendGranularity)
        {
            this.startDay = startDay;
            this.endDay = endDay;
            this.trendGranularity = trendGranularity;
        }
    }
}
