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

import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;

import java.text.ParseException;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.web.modules.trading.MarketEntryTestSamples;
import com.ruoyi.web.modules.trading.domain.BizMarketEntry;
import com.ruoyi.web.modules.trading.mapper.BizMarketStallMapper;
import com.ruoyi.web.modules.trading.mapper.BizSupplierMapper;

@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
@DisplayName("MarketEntryValidation")
class MarketEntryValidationTest
{
    @Mock
    private BizMarketStallMapper bizMarketStallMapper;

    @Mock
    private BizSupplierMapper bizSupplierMapper;

    @Mock
    private MarketEntryEarTagResolver marketEntryEarTagResolver;

    @InjectMocks
    private MarketEntryValidation validation;

    @BeforeEach
    void stubRefs()
    {
        when(bizMarketStallMapper.selectBizMarketStallById(MarketEntryTestSamples.STALL_ID_1))
                .thenReturn(MarketEntryTestSamples.activeStall());
        when(bizSupplierMapper.selectBizSupplierById(MarketEntryTestSamples.SUPPLIER_ID_1))
                .thenReturn(MarketEntryTestSamples.activeSupplier());
        when(marketEntryEarTagResolver.listEligible(eq(MarketEntryTestSamples.SUPPLIER_ID_1), any()))
                .thenReturn(Arrays.asList("E103", "E104"));
        when(marketEntryEarTagResolver.loadOccupiedEarTags(any()))
                .thenReturn(Collections.emptySet());
    }

    @Test
    @DisplayName("ZCZX-SCJLDJ-UT-001 耳标英文逗号拆分")
    void ut001_parse()
    {
        assertEquals(Arrays.asList("E101", "E102"), EarTagCodec.parse("E101,E102"));
    }

    @Test
    @DisplayName("ZCZX-SCJLDJ-UT-002 耳标去重拒绝")
    void ut002_dup()
    {
        assertThrows(ServiceException.class, () -> EarTagCodec.parse("E101,E101"));
    }

    @Test
    @DisplayName("ZCZX-SCJLDJ-UT-003 耳标单段超长")
    void ut003_longTag()
    {
        char[] chars = new char[33];
        Arrays.fill(chars, 'X');
        assertThrows(ServiceException.class, () -> EarTagCodec.parse(new String(chars)));
    }

    @Test
    @DisplayName("ZCZX-SCJLDJ-UT-004 M1 客户端 quantity 被耳标数覆盖")
    void ut004_m1OverrideClientQty() throws ParseException
    {
        BizMarketEntry row = MarketEntryTestSamples.twoTagSaveRow();
        row.setQuantity(1);
        validation.prepareAndValidateForSave(row);
        assertEquals(2, row.getQuantity().intValue());
    }

    @Test
    @DisplayName("ZCZX-SCJLDJ-UT-005 M1 至少一头")
    void ut005_m1Empty() throws ParseException
    {
        BizMarketEntry row = MarketEntryTestSamples.normalSaveRow();
        row.setEarTagList(Collections.emptyList());
        assertThrows(ServiceException.class, () -> validation.prepareAndValidateForSave(row));
    }

    @Test
    @DisplayName("ZCZX-SCJLDJ-UT-006 M1 合法")
    void ut006_m1Pass() throws ParseException
    {
        BizMarketEntry row = MarketEntryTestSamples.twoTagSaveRow();
        row.setQuantity(2);
        validation.prepareAndValidateForSave(row);
        assertEquals(2, row.getQuantity().intValue());
        assertEquals(Arrays.asList("E103", "E104"), row.getEarTagList());
        assertEquals("01", row.getStallCode());
    }

    @Test
    @DisplayName("ZCZX-SCJLDJ-UT-007 M4 他单占用")
    void ut007_m4Occupied() throws ParseException
    {
        when(marketEntryEarTagResolver.listEligible(eq(MarketEntryTestSamples.SUPPLIER_ID_1), eq(null)))
                .thenReturn(Collections.singletonList("E104"));
        when(marketEntryEarTagResolver.loadOccupiedEarTags(null))
                .thenReturn(Collections.singleton("E101"));
        BizMarketEntry row = MarketEntryTestSamples.normalSaveRow();
        row.setEarTagList(Collections.singletonList("E101"));
        assertThrows(ServiceException.class, () -> validation.prepareAndValidateForSave(row));
    }

    @Test
    @DisplayName("ZCZX-SCJLDJ-UT-008 M4 编辑释放自身")
    void ut008_m4SelfRelease() throws ParseException
    {
        when(marketEntryEarTagResolver.listEligible(eq(MarketEntryTestSamples.SUPPLIER_ID_1),
                eq(MarketEntryTestSamples.ENTRY_ID)))
                .thenReturn(Arrays.asList("E101", "E103"));
        BizMarketEntry row = MarketEntryTestSamples.normalSaveRow();
        row.setId(MarketEntryTestSamples.ENTRY_ID);
        row.setEarTagList(Collections.singletonList("E101"));
        validation.prepareAndValidateForSave(row);
        assertEquals(1, row.getQuantity().intValue());
    }

    @Test
    @DisplayName("ZCZX-SCJLDJ-UT-009 M2 死淘耳标")
    void ut009_m2Dead() throws ParseException
    {
        when(marketEntryEarTagResolver.listEligible(eq(MarketEntryTestSamples.SUPPLIER_ID_1), any()))
                .thenReturn(Collections.singletonList("E103"));
        BizMarketEntry row = MarketEntryTestSamples.normalSaveRow();
        row.setEarTagList(Collections.singletonList("E999"));
        assertThrows(ServiceException.class, () -> validation.prepareAndValidateForSave(row));
    }

    @Test
    @DisplayName("ZCZX-SCJLDJ-UT-010 M2 合法耳标")
    void ut010_m2Pass() throws ParseException
    {
        BizMarketEntry row = MarketEntryTestSamples.normalSaveRow();
        assertDoesNotThrow(() -> validation.prepareAndValidateForSave(row));
    }

    @Test
    @DisplayName("ZCZX-SCJLDJ-UT-011 进栏时间晚于当前")
    void ut011_futureTime() throws ParseException
    {
        BizMarketEntry row = MarketEntryTestSamples.normalSaveRow();
        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.DAY_OF_MONTH, 1);
        row.setEntryTime(cal.getTime());
        assertThrows(ServiceException.class, () -> validation.prepareAndValidateForSave(row));
    }

    @Test
    @DisplayName("ZCZX-SCJLDJ-UT-012 进栏时间等于当前")
    void ut012_nowTime()
    {
        BizMarketEntry row = new BizMarketEntry();
        row.setEntryTime(new Date());
        row.setStallId(MarketEntryTestSamples.STALL_ID_1);
        row.setSupplierId(MarketEntryTestSamples.SUPPLIER_ID_1);
        row.setEarTagList(Collections.singletonList("E103"));
        assertDoesNotThrow(() -> validation.prepareAndValidateForSave(row));
    }

    @Test
    @DisplayName("ZCZX-SCJLDJ-UT-013 列表日期区间颠倒")
    void ut013_dateRange()
    {
        BizMarketEntry query = new BizMarketEntry();
        query.setEntryDateStart("2026-05-31");
        query.setEntryDateEnd("2026-05-01");
        assertThrows(ServiceException.class, () -> validation.validateListQuery(query));
    }

    @Test
    @DisplayName("ZCZX-SCJLDJ-UT-014 栏位已删")
    void ut014_stallDeleted()
    {
        when(bizMarketStallMapper.selectBizMarketStallById(MarketEntryTestSamples.STALL_ID_1))
                .thenReturn(null);
        BizMarketEntry row = new BizMarketEntry();
        row.setEntryTime(new Date());
        row.setStallId(MarketEntryTestSamples.STALL_ID_1);
        row.setSupplierId(MarketEntryTestSamples.SUPPLIER_ID_1);
        row.setEarTagList(Collections.singletonList("E103"));
        assertThrows(ServiceException.class, () -> validation.prepareAndValidateForSave(row));
    }

    @Test
    @DisplayName("ZCZX-SCJLDJ-UT-015 供应商已删")
    void ut015_supplierDeleted()
    {
        when(bizSupplierMapper.selectBizSupplierById(MarketEntryTestSamples.SUPPLIER_ID_1))
                .thenReturn(null);
        BizMarketEntry row = new BizMarketEntry();
        row.setEntryTime(new Date());
        row.setStallId(MarketEntryTestSamples.STALL_ID_1);
        row.setSupplierId(MarketEntryTestSamples.SUPPLIER_ID_1);
        row.setEarTagList(Collections.singletonList("E103"));
        assertThrows(ServiceException.class, () -> validation.prepareAndValidateForSave(row));
    }

    @Test
    @DisplayName("ZCZX-SCJLDJ-UT-016 quantity 以耳标数覆盖")
    void ut016_quantityOverride() throws ParseException
    {
        BizMarketEntry row = MarketEntryTestSamples.twoTagSaveRow();
        row.setQuantity(9);
        validation.prepareAndValidateForSave(row);
        assertEquals(2, row.getQuantity().intValue());
    }
}
