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

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import com.ruoyi.web.modules.screen.domain.dto.TradeSalesDestinationCountDto;
import com.ruoyi.web.modules.screen.domain.dto.TradeSalesMonthTradeDto;
import com.ruoyi.web.modules.screen.domain.dto.TradeSalesQuoteRawDto;
import com.ruoyi.web.modules.screen.domain.vo.TradeSalesDestinationVo;
import com.ruoyi.web.modules.screen.domain.vo.TradeSalesMonthlyTrendVo;
import com.ruoyi.web.modules.screen.domain.vo.TradeSalesOriginQuoteVo;
import com.ruoyi.web.modules.screen.domain.vo.TradeSalesQualityGradeVo;
import com.ruoyi.web.modules.trading.domain.BizGradeWeightConfig;
import com.ruoyi.web.modules.trading.support.DistributorRules;
import com.ruoyi.web.modules.trading.support.MarketDailyQuoteRules;

/**
 * DP-JY-UT-003～018
 */
@DisplayName("TradeSalesScreenSupport")
class TradeSalesScreenSupportTest
{
    @Test
    @DisplayName("DP-JY-UT-004 历史年 statDate")
    void ut004_historicalStatDate()
    {
        assertEquals(LocalDate.of(2024, 12, 31), HomeScreenSupport.resolveStatDate(2024));
    }

    @Test
    @DisplayName("DP-JY-UT-007 最近 12 个月补零")
    void ut007_monthlyTwelve()
    {
        TradeSalesMonthTradeDto m3 = monthTrade(2026, 3, 5L, "100000");
        TradeSalesMonthTradeDto m8 = monthTrade(2026, 8, 3L, "60000");
        List<TradeSalesMonthlyTrendVo> trend = TradeSalesScreenSupport.buildRolling12MonthTradeTrend(
                Arrays.asList(m3, m8), java.time.YearMonth.of(2026, 8));
        assertEquals(12, trend.size());
        assertEquals(2026, trend.get(11).getStatYear());
        assertEquals(8, trend.get(11).getMonth());
        assertEquals(3L, trend.get(11).getTradeHeads());
        assertEquals(2025, trend.get(0).getStatYear());
        assertEquals(9, trend.get(0).getMonth());
        assertEquals(0L, trend.get(0).getTradeHeads());
    }

    @Test
    @DisplayName("DP-JY-UT-010 销售去向三档")
    void ut010_salesDestination()
    {
        TradeSalesDestinationCountDto local = dest(DistributorRules.SALES_DESTINATION_LOCAL_CITY, 10L);
        TradeSalesDestinationCountDto out = dest(DistributorRules.SALES_DESTINATION_OUT_PROVINCE, 5L);
        List<TradeSalesDestinationVo> list = TradeSalesScreenSupport.buildSalesDestination(
                Arrays.asList(local, out), 15L);
        assertEquals(3, list.size());
        assertEquals(10L, list.get(0).getTradeHeads());
        assertEquals(new BigDecimal("66.7"), list.get(0).getRatio());
    }

    @Test
    @DisplayName("DP-JY-UT-014 同日多维度聚合")
    void ut014_dailyQuoteAggregate()
    {
        LocalDate d = LocalDate.of(2026, 5, 14);
        List<TradeSalesQuoteRawDto> rows = Arrays.asList(
                quoteRow(d, "30", "35", "31", MarketDailyQuoteRules.PRICE_UNIT_PER_JIN),
                quoteRow(d, "32", "36", "33", MarketDailyQuoteRules.PRICE_UNIT_PER_JIN));
        TradeSalesOriginQuoteVo vo = TradeSalesScreenSupport.buildOriginQuote(rows, LocalDate.of(2026, 5, 20));
        assertTrue(vo.isHasQuoteData());
        assertEquals(new BigDecimal("30.00"), vo.getDailyTrend().get(0).getMinPrice());
        assertEquals(new BigDecimal("36.00"), vo.getDailyTrend().get(0).getMaxPrice());
        assertEquals(new BigDecimal("32.00"), vo.getDailyTrend().get(0).getAvgPrice());
    }

    @Test
    @DisplayName("DP-JY-UT-016 缺日走势图填 0")
    void ut016_gapDaysZeroOnChart()
    {
        LocalDate d1 = LocalDate.of(2026, 5, 14);
        LocalDate d7 = LocalDate.of(2026, 5, 20);
        List<TradeSalesQuoteRawDto> rows = Arrays.asList(
                quoteRow(d1, "30", "35", "32", MarketDailyQuoteRules.PRICE_UNIT_PER_JIN),
                quoteRow(d7, "31", "36", "33", MarketDailyQuoteRules.PRICE_UNIT_PER_JIN));
        TradeSalesOriginQuoteVo vo = TradeSalesScreenSupport.buildOriginQuote(rows, d7);
        assertEquals(7, vo.getDailyTrend().size());
        assertEquals(BigDecimal.ZERO.setScale(2), vo.getDailyTrend().get(1).getAvgPrice());
    }

    @Test
    @DisplayName("DP-JY-UT-017 7 日均价不含无登记日")
    void ut017_avgExcludesGapDays()
    {
        LocalDate d1 = LocalDate.of(2026, 5, 14);
        LocalDate d7 = LocalDate.of(2026, 5, 20);
        List<TradeSalesQuoteRawDto> rows = Arrays.asList(
                quoteRow(d1, "30", "35", "32", MarketDailyQuoteRules.PRICE_UNIT_PER_JIN),
                quoteRow(d7, "31", "36", "34", MarketDailyQuoteRules.PRICE_UNIT_PER_JIN));
        TradeSalesOriginQuoteVo vo = TradeSalesScreenSupport.buildOriginQuote(rows, d7);
        assertEquals(new BigDecimal("33.00"), vo.getAvgPrice7d());
    }

    @Test
    @DisplayName("DP-JY-UT-019 无行情空态")
    void ut019_noQuote()
    {
        TradeSalesOriginQuoteVo vo = TradeSalesScreenSupport.buildOriginQuote(
                Collections.emptyList(), LocalDate.of(2026, 5, 20));
        assertFalse(vo.isHasQuoteData());
        assertNull(vo.getAvgPrice7d());
        assertEquals(7, vo.getDailyTrend().size());
    }

    @Test
    @DisplayName("产地行情 create_time 为空时不 NPE 且默认计价单位")
    void originQuote_nullCreateTime_noNpe()
    {
        LocalDate d = LocalDate.of(2026, 5, 20);
        TradeSalesQuoteRawDto row = new TradeSalesQuoteRawDto();
        row.setQuoteDate(Date.from(d.atStartOfDay(ZoneId.of("Asia/Shanghai")).toInstant()));
        row.setPriceMin(new BigDecimal("30"));
        row.setPriceMax(new BigDecimal("35"));
        row.setAvgPrice(new BigDecimal("32"));
        row.setPriceUnit(MarketDailyQuoteRules.PRICE_UNIT_PER_JIN);
        row.setCreateTime(null);
        TradeSalesOriginQuoteVo vo = TradeSalesScreenSupport.buildOriginQuote(
                Collections.singletonList(row), d);
        assertTrue(vo.isHasQuoteData());
        assertEquals("2026-05-20", vo.getLastQuoteDate());
        assertEquals(MarketDailyQuoteRules.PRICE_UNIT_PER_JIN, vo.getPriceUnit());
        assertEquals("元/斤", vo.getPriceUnitLabel());
    }

    @Test
    @DisplayName("DP-JY-UT-012 品质四档固定")
    void ut012_qualityFourBuckets()
    {
        BizGradeWeightConfig a = gradeConfig("A", "一级", 1);
        BizGradeWeightConfig b = gradeConfig("B", "二级", 2);
        TradeSalesQualityGradeVo vo = TradeSalesScreenSupport.buildQualityGrade(
                Collections.singletonList(gradeCount("B", 3L)),
                Arrays.asList(a, b),
                3L);
        assertTrue(vo.isHasGradeData());
        assertEquals(2, vo.getItems().size());
        assertEquals(3L, vo.getItems().get(1).getTradeHeads());
        assertEquals(new BigDecimal("100.0"), vo.getItems().get(1).getRatio());
        assertEquals("二级", vo.getItems().get(1).getGradeName());
    }

    @Test
    @DisplayName("品质展示优先明细 grade_name 快照")
    void qualityPreferLineGradeName()
    {
        com.ruoyi.web.modules.screen.domain.dto.TradeSalesGradeCountDto row =
                new com.ruoyi.web.modules.screen.domain.dto.TradeSalesGradeCountDto();
        row.setGradeCode("B");
        row.setGradeName("二级(快照)");
        row.setTradeHeads(5L);
        BizGradeWeightConfig config = gradeConfig("B", "二级", 2);
        TradeSalesQualityGradeVo vo = TradeSalesScreenSupport.buildQualityGrade(
                Collections.singletonList(row),
                Collections.singletonList(config),
                5L);
        assertEquals("二级(快照)", vo.getItems().get(0).getGradeName());
    }

    private static TradeSalesMonthTradeDto monthTrade(int statYear, int month, long heads, String amount)
    {
        TradeSalesMonthTradeDto dto = new TradeSalesMonthTradeDto();
        dto.setStatYear(statYear);
        dto.setMonth(month);
        dto.setTradeHeads(heads);
        dto.setTradeAmount(new BigDecimal(amount));
        return dto;
    }

    private static TradeSalesDestinationCountDto dest(int salesDestination, long heads)
    {
        TradeSalesDestinationCountDto dto = new TradeSalesDestinationCountDto();
        dto.setSalesDestination(salesDestination);
        dto.setTradeHeads(heads);
        return dto;
    }

    private static TradeSalesQuoteRawDto quoteRow(
            LocalDate date, String min, String max, String avg, int unit)
    {
        TradeSalesQuoteRawDto row = new TradeSalesQuoteRawDto();
        row.setQuoteDate(Date.from(date.atStartOfDay(ZoneId.of("Asia/Shanghai")).toInstant()));
        row.setPriceMin(new BigDecimal(min));
        row.setPriceMax(new BigDecimal(max));
        row.setAvgPrice(new BigDecimal(avg));
        row.setPriceUnit(unit);
        row.setCreateTime(row.getQuoteDate());
        return row;
    }

    private static BizGradeWeightConfig gradeConfig(String code, String name, int sort)
    {
        BizGradeWeightConfig c = new BizGradeWeightConfig();
        c.setGradeCode(code);
        c.setGradeName(name);
        c.setSortOrder(sort);
        return c;
    }

    private static com.ruoyi.web.modules.screen.domain.dto.TradeSalesGradeCountDto gradeCount(
            String code, long heads)
    {
        com.ruoyi.web.modules.screen.domain.dto.TradeSalesGradeCountDto dto =
                new com.ruoyi.web.modules.screen.domain.dto.TradeSalesGradeCountDto();
        dto.setGradeCode(code);
        dto.setTradeHeads(heads);
        return dto;
    }
}
