package com.ycwl.basic.pricing.service;

import com.ycwl.basic.pricing.dto.VoucherInfo;
import com.ycwl.basic.pricing.entity.PriceVoucherBatchConfig;
import com.ycwl.basic.pricing.entity.PriceVoucherCode;
import com.ycwl.basic.pricing.entity.PriceVoucherUsageRecord;
import com.ycwl.basic.pricing.enums.VoucherCodeStatus;
import com.ycwl.basic.pricing.enums.VoucherDiscountType;
import com.ycwl.basic.pricing.mapper.PriceVoucherBatchConfigMapper;
import com.ycwl.basic.pricing.mapper.PriceVoucherCodeMapper;
import com.ycwl.basic.pricing.mapper.PriceVoucherUsageRecordMapper;
import com.ycwl.basic.pricing.service.impl.VoucherServiceImpl;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import java.math.BigDecimal;
import java.util.Date;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;

/**
 * 可重复使用券码服务测试
 */
public class ReusableVoucherServiceTest {
    
    @Mock
    private PriceVoucherCodeMapper voucherCodeMapper;
    
    @Mock
    private PriceVoucherBatchConfigMapper voucherBatchConfigMapper;
    
    @Mock
    private PriceVoucherUsageRecordMapper usageRecordMapper;
    
    @InjectMocks
    private VoucherServiceImpl voucherService;
    
    private PriceVoucherCode testVoucherCode;
    private PriceVoucherBatchConfig testBatchConfig;
    
    @BeforeEach
    void setUp() {
        MockitoAnnotations.openMocks(this);
        
        // 创建测试数据
        testVoucherCode = new PriceVoucherCode();
        testVoucherCode.setId(1L);
        testVoucherCode.setCode("TEST123");
        testVoucherCode.setBatchId(1L);
        testVoucherCode.setScenicId(1L);
        testVoucherCode.setUserId(1001L);
        testVoucherCode.setStatus(VoucherCodeStatus.CLAIMED_AVAILABLE.getCode());
        testVoucherCode.setCurrentUseCount(1);
        testVoucherCode.setLastUsedTime(new Date());
        testVoucherCode.setDeleted(0);
        
        testBatchConfig = new PriceVoucherBatchConfig();
        testBatchConfig.setId(1L);
        testBatchConfig.setBatchName("测试批次");
        testBatchConfig.setDiscountType(VoucherDiscountType.REDUCE_PRICE.getCode());
        testBatchConfig.setDiscountValue(new BigDecimal("10.00"));
        testBatchConfig.setMaxUseCount(3); // 可使用3次
        testBatchConfig.setMaxUsePerUser(2); // 每用户最多2次
        testBatchConfig.setUseIntervalHours(24); // 间隔24小时
        testBatchConfig.setStatus(1);
        testBatchConfig.setDeleted(0);
    }
    
    @Test
    void testValidateReusableVoucherCode_Success() {
        // Given
        when(voucherCodeMapper.selectByCode("TEST123")).thenReturn(testVoucherCode);
        when(voucherBatchConfigMapper.selectById(1L)).thenReturn(testBatchConfig);
        when(usageRecordMapper.countByFaceIdAndVoucherCodeId(1001L, 1L)).thenReturn(1); // 用户已使用1次
        
        // 模拟距离上次使用已超过24小时
        Date lastUseTime = new Date(System.currentTimeMillis() - 25 * 60 * 60 * 1000); // 25小时前
        when(usageRecordMapper.getLastUseTimeByFaceIdAndVoucherCodeId(1001L, 1L)).thenReturn(lastUseTime);
        
        // When
        VoucherInfo result = voucherService.validateAndGetVoucherInfo("TEST123", 1001L, 1L);
        
        // Then
        assertNotNull(result);
        assertEquals("TEST123", result.getVoucherCode());
        assertTrue(result.getAvailable());
        assertEquals(Integer.valueOf(1), result.getCurrentUseCount());
        assertEquals(Integer.valueOf(3), result.getMaxUseCount());
        assertEquals(Integer.valueOf(2), result.getRemainingUseCount());
    }
    
    @Test
    void testValidateVoucherCode_ReachedMaxUseCount() {
        // Given - 券码已达到最大使用次数
        testVoucherCode.setCurrentUseCount(3);
        testVoucherCode.setStatus(VoucherCodeStatus.CLAIMED_EXHAUSTED.getCode());
        
        when(voucherCodeMapper.selectByCode("TEST123")).thenReturn(testVoucherCode);
        when(voucherBatchConfigMapper.selectById(1L)).thenReturn(testBatchConfig);
        
        // When
        VoucherInfo result = voucherService.validateAndGetVoucherInfo("TEST123", 1001L, 1L);
        
        // Then
        assertNotNull(result);
        assertFalse(result.getAvailable());
        assertEquals("券码使用次数已达上限", result.getUnavailableReason());
    }
    
    @Test
    void testValidateVoucherCode_UserReachedMaxUsePerUser() {
        // Given - 用户已达到个人使用上限
        when(voucherCodeMapper.selectByCode("TEST123")).thenReturn(testVoucherCode);
        when(voucherBatchConfigMapper.selectById(1L)).thenReturn(testBatchConfig);
        when(usageRecordMapper.countByFaceIdAndVoucherCodeId(1001L, 1L)).thenReturn(2); // 用户已使用2次
        
        // When
        VoucherInfo result = voucherService.validateAndGetVoucherInfo("TEST123", 1001L, 1L);
        
        // Then
        assertNotNull(result);
        assertFalse(result.getAvailable());
        assertEquals("您使用该券码的次数已达上限", result.getUnavailableReason());
    }
    
    @Test
    void testValidateVoucherCode_WithinInterval() {
        // Given - 距离上次使用不足24小时
        when(voucherCodeMapper.selectByCode("TEST123")).thenReturn(testVoucherCode);
        when(voucherBatchConfigMapper.selectById(1L)).thenReturn(testBatchConfig);
        when(usageRecordMapper.countByFaceIdAndVoucherCodeId(1001L, 1L)).thenReturn(1);
        
        // 模拟距离上次使用仅10小时
        Date lastUseTime = new Date(System.currentTimeMillis() - 10 * 60 * 60 * 1000);
        when(usageRecordMapper.getLastUseTimeByFaceIdAndVoucherCodeId(1001L, 1L)).thenReturn(lastUseTime);
        
        // When
        VoucherInfo result = voucherService.validateAndGetVoucherInfo("TEST123", 1001L, 1L);
        
        // Then
        assertNotNull(result);
        assertFalse(result.getAvailable());
        assertTrue(result.getUnavailableReason().contains("请等待"));
        assertTrue(result.getUnavailableReason().contains("小时后再次使用"));
    }
    
    @Test
    void testMarkVoucherAsUsed_UpdateCountAndStatus() {
        // Given
        when(voucherCodeMapper.selectByCode("TEST123")).thenReturn(testVoucherCode);
        when(voucherBatchConfigMapper.selectById(1L)).thenReturn(testBatchConfig);
        
        // When
        voucherService.markVoucherAsUsed("TEST123", "测试使用", "ORDER001", new BigDecimal("10.00"), 1L);
        
        // Then
        verify(usageRecordMapper, times(1)).insert(any(PriceVoucherUsageRecord.class));
        verify(voucherCodeMapper, times(1)).updateById(any(PriceVoucherCode.class));
        verify(voucherBatchConfigMapper, times(1)).updateUsedCount(eq(1L), eq(1));
    }
    
    @Test
    void testMarkVoucherAsUsed_ReachMaxUseCount() {
        // Given - 使用后将达到最大使用次数
        testVoucherCode.setCurrentUseCount(2); // 当前已使用2次，再使用1次将达到上限3次
        
        when(voucherCodeMapper.selectByCode("TEST123")).thenReturn(testVoucherCode);
        when(voucherBatchConfigMapper.selectById(1L)).thenReturn(testBatchConfig);
        
        // When
        voucherService.markVoucherAsUsed("TEST123", "测试使用", "ORDER001", new BigDecimal("10.00"), 1L);
        
        // Then
        verify(usageRecordMapper, times(1)).insert(any(PriceVoucherUsageRecord.class));
        verify(voucherCodeMapper, times(1)).updateById(argThat(voucherCode -> 
            voucherCode.getCurrentUseCount() == 3 && 
            voucherCode.getStatus().equals(VoucherCodeStatus.CLAIMED_EXHAUSTED.getCode())
        ));
        verify(voucherBatchConfigMapper, times(1)).updateUsedCount(eq(1L), eq(1));
    }
    
    @Test
    void testMarkVoucherAsUsed_SingleUseCompatibility() {
        // Given - 测试单次使用兼容性（maxUseCount = 1）
        testBatchConfig.setMaxUseCount(1);
        testVoucherCode.setCurrentUseCount(0);
        
        when(voucherCodeMapper.selectByCode("TEST123")).thenReturn(testVoucherCode);
        when(voucherBatchConfigMapper.selectById(1L)).thenReturn(testBatchConfig);
        
        // When
        voucherService.markVoucherAsUsed("TEST123", "测试使用", "ORDER001", new BigDecimal("10.00"), 1L);
        
        // Then - 应该设置为USED状态以兼容原有逻辑
        verify(voucherCodeMapper, times(1)).updateById(argThat(voucherCode -> 
            voucherCode.getCurrentUseCount() == 1 && 
            voucherCode.getStatus().equals(VoucherCodeStatus.USED.getCode()) &&
            voucherCode.getUsedTime() != null
        ));
    }
}
