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

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.web.modules.trading.domain.dto.AppSupplierAmountBucketDto;
import com.ruoyi.web.modules.trading.domain.dto.AppSupplierSettlementSummaryDto;
import com.ruoyi.web.modules.trading.domain.dto.AppSupplierTradeSummaryDto;
import com.ruoyi.web.modules.trading.domain.vo.AppSupplierAmountTrendPointVo;
import com.ruoyi.web.modules.trading.domain.vo.AppSupplierHomeDashboardVo;
import com.ruoyi.web.modules.trading.domain.vo.AppSupplierSettlementSummaryVo;
import com.ruoyi.web.modules.trading.domain.vo.AppSupplierTradeSummaryVo;
import com.ruoyi.web.modules.trading.domain.vo.AppSupplierYakStatusVo;

/**
 * 供应商首页统计：时段解析、指标与趋势序列组装。
 */
public final class AppSupplierHomeSupport
{
    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 AppSupplierHomeSupport()
    {
    }

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

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

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

    public static AppSupplierHomeDashboardVo buildDashboard(
            String range,
            AppSupplierTradeSummaryDto tradeRaw,
            AppSupplierSettlementSummaryDto settlementRaw,
            List<AppSupplierAmountBucketDto> bucketRows,
            DateRange dateRange,
            AppSupplierYakStatusVo yakStatus)
    {
        AppSupplierHomeDashboardVo vo = new AppSupplierHomeDashboardVo();
        vo.setRange(range);
        vo.setRangeName(rangeName(range));
        vo.setUpdatedAt(LocalDateTime.now(ZONE).format(DATETIME_FMT));
        vo.setTrendGranularity(dateRange.trendGranularity);
        vo.setTradeSummary(buildTradeSummary(tradeRaw));
        vo.setSettlementSummary(buildSettlementSummary(settlementRaw));
        vo.setYakStatus(yakStatus);
        if (AppSupplierHomeRules.TREND_GRANULARITY_HOUR.equals(dateRange.trendGranularity))
        {
            vo.setAmountTrend(buildHourlyTrend(bucketRows));
        }
        else
        {
            vo.setAmountTrend(buildDailyTrend(bucketRows, dateRange.startDay, dateRange.endDay));
        }
        return vo;
    }

    public static AppSupplierTradeSummaryVo buildTradeSummary(AppSupplierTradeSummaryDto raw)
    {
        AppSupplierTradeSummaryVo vo = new AppSupplierTradeSummaryVo();
        vo.setOrderCount(raw != null && raw.getOrderCount() != null ? raw.getOrderCount() : 0L);
        vo.setTotalAmount(raw != null && raw.getTotalAmount() != null
                ? raw.getTotalAmount() : BigDecimal.ZERO);
        vo.setTotalHeads(raw != null && raw.getTotalHeads() != null ? raw.getTotalHeads() : 0L);
        vo.setPayingBuyerCount(raw != null && raw.getPayingBuyerCount() != null
                ? raw.getPayingBuyerCount() : 0L);
        return vo;
    }

    public static AppSupplierSettlementSummaryVo buildSettlementSummary(AppSupplierSettlementSummaryDto raw)
    {
        AppSupplierSettlementSummaryVo vo = new AppSupplierSettlementSummaryVo();
        vo.setPayableAmount(raw != null && raw.getPayableAmount() != null
                ? raw.getPayableAmount() : BigDecimal.ZERO);
        vo.setServiceFeeAmount(raw != null && raw.getServiceFeeAmount() != null
                ? raw.getServiceFeeAmount() : BigDecimal.ZERO);
        return vo;
    }

    public static AppSupplierYakStatusVo buildYakStatus(
            Long tradedHeads,
            Long enteredPenEarTags,
            Long pendingPayHeads,
            List<String> activeIsolationRows,
            List<String> sickDeadRows)
    {
        AppSupplierYakStatusVo vo = new AppSupplierYakStatusVo();
        vo.setTradedHeads(tradedHeads != null ? tradedHeads : 0L);
        vo.setEnteredPenEarTags(enteredPenEarTags != null ? enteredPenEarTags : 0L);
        vo.setPendingPayHeads(pendingPayHeads != null ? pendingPayHeads : 0L);
        vo.setIsolationEarTags(countIsolationEarTags(activeIsolationRows, sickDeadRows));
        return vo;
    }

    public static long countIsolationEarTags(List<String> activeRows, List<String> sickRows)
    {
        Set<String> active = parseEarTagRows(activeRows);
        Set<String> sick = parseEarTagRows(sickRows);
        active.removeAll(sick);
        return active.size();
    }

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

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

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

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

    private static Set<String> parseEarTagRows(List<String> rows)
    {
        Set<String> tags = new HashSet<>();
        if (rows == null)
        {
            return tags;
        }
        for (String raw : rows)
        {
            if (StringUtils.isEmpty(raw))
            {
                continue;
            }
            tags.addAll(EarTagCodec.parse(raw));
        }
        return tags;
    }

    private static Map<Integer, BigDecimal> toHourAmountMap(List<AppSupplierAmountBucketDto> rows)
    {
        Map<Integer, BigDecimal> map = new HashMap<>();
        if (rows == null)
        {
            return map;
        }
        for (AppSupplierAmountBucketDto 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<AppSupplierAmountBucketDto> rows)
    {
        Map<String, BigDecimal> map = new HashMap<>();
        if (rows == null)
        {
            return map;
        }
        for (AppSupplierAmountBucketDto row : rows)
        {
            if (row != null && row.getBucketKey() != null)
            {
                map.put(row.getBucketKey(),
                        row.getAmount() != null ? row.getAmount() : BigDecimal.ZERO);
            }
        }
        return map;
    }

    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;
        }
    }
}
