package com.dora.service;

import com.dora.entity.ContentRevenueStage;
import com.dora.entity.Course;
import com.dora.entity.CourseChapter;
import com.dora.entity.CourseVideo;
import com.dora.entity.RevenueConfig;
import com.dora.mapper.ContentRevenueStageMapper;
import com.dora.mapper.CourseChapterMapper;
import com.dora.mapper.CourseMapper;
import com.dora.mapper.CourseVideoMapper;
import com.dora.mapper.RevenueConfigMapper;
import com.dora.mapper.WorkflowMapper;
import com.dora.service.impl.ContentRevenueStageServiceImpl;
import org.junit.jupiter.api.BeforeEach;
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 java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

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

/**
 * 内容收益阶段服务测试类
 */
@ExtendWith(MockitoExtension.class)
class ContentRevenueStageServiceTest {

    @Mock
    private ContentRevenueStageMapper contentRevenueStageMapper;

    @Mock
    private RevenueConfigMapper revenueConfigMapper;

    @Mock
    private WorkflowMapper workflowMapper;

    @Mock
    private CourseMapper courseMapper;

    @Mock
    private CourseVideoMapper courseVideoMapper;

    @Mock
    private CourseChapterMapper courseChapterMapper;

    @InjectMocks
    private ContentRevenueStageServiceImpl contentRevenueStageService;

    private List<ContentRevenueStage> mockStages;
    private List<RevenueConfig> mockConfigs;

    @BeforeEach
    void setUp() {
        // 准备测试数据
        mockStages = new ArrayList<>();
        
        // 阶段1: 1000次观看，100元奖励
        ContentRevenueStage stage1 = new ContentRevenueStage();
        stage1.setId(1L);
        stage1.setContentType(2);
        stage1.setContentId(100L);
        stage1.setUserId(1L);
        stage1.setStageLevel(1);
        stage1.setStageName("初级视频创作者");
        stage1.setTargetCount(1000);
        stage1.setRewardAmount(new BigDecimal("100.00"));
        stage1.setCurrentCount(500);
        stage1.setIsAchieved(0);
        mockStages.add(stage1);

        // 阶段2: 5000次观看，300元奖励
        ContentRevenueStage stage2 = new ContentRevenueStage();
        stage2.setId(2L);
        stage2.setContentType(2);
        stage2.setContentId(100L);
        stage2.setUserId(1L);
        stage2.setStageLevel(2);
        stage2.setStageName("中级视频创作者");
        stage2.setTargetCount(5000);
        stage2.setRewardAmount(new BigDecimal("300.00"));
        stage2.setCurrentCount(500);
        stage2.setIsAchieved(0);
        mockStages.add(stage2);

        // 准备配置数据
        mockConfigs = Arrays.asList(
                createRevenueConfig("video_basic", 1, "初级视频创作者", 1000, "100.00"),
                createRevenueConfig("video_intermediate", 2, "中级视频创作者", 5000, "300.00"),
                createRevenueConfig("video_advanced", 3, "高级视频创作者", 10000, "800.00")
        );
    }

    @Test
    void testIsWorkflowVideo_returnsTrue_whenVideoIsWorkflow() {
        // Given
        String vodVideoId = "workflow_video_123";
        when(workflowMapper.countByVodVideoId(vodVideoId)).thenReturn(1);

        // When
        boolean result = contentRevenueStageService.isWorkflowVideo(vodVideoId);

        // Then
        assertTrue(result);
        verify(workflowMapper).countByVodVideoId(vodVideoId);
    }

    @Test
    void testIsWorkflowVideo_returnsFalse_whenVideoIsNotWorkflow() {
        // Given
        String vodVideoId = "regular_video_123";
        when(workflowMapper.countByVodVideoId(vodVideoId)).thenReturn(0);

        // When
        boolean result = contentRevenueStageService.isWorkflowVideo(vodVideoId);

        // Then
        assertFalse(result);
        verify(workflowMapper).countByVodVideoId(vodVideoId);
    }

    @Test
    void testCheckAndUpdateVideoRevenueStages_achievesNewStage() {
        // Given
        Long videoId = 100L;
        Long ownerId = 1L;
        Integer currentViewCount = 1000; // 达到第一个阶段目标

        when(contentRevenueStageMapper.selectByContentId(2, videoId)).thenReturn(mockStages);
        when(contentRevenueStageMapper.updateCurrentCount(any(), any())).thenReturn(1);
        when(contentRevenueStageMapper.markAsAchieved(any(), any())).thenReturn(1);

        // When
        List<ContentRevenueStage> result = contentRevenueStageService
                .checkAndUpdateVideoRevenueStages(videoId, ownerId, currentViewCount);

        // Then
        assertEquals(1, result.size());
        assertEquals("初级视频创作者", result.get(0).getStageName());
        assertEquals(new BigDecimal("100.00"), result.get(0).getRewardAmount());
        
        verify(contentRevenueStageMapper).updateCurrentCount(1L, currentViewCount);
        verify(contentRevenueStageMapper).markAsAchieved(eq(1L), any());
    }

    @Test
    void testInitializeVideoRevenueStages() {
        // Given
        Long videoId = 200L;
        Long ownerId = 2L;
        
        when(revenueConfigMapper.selectByConfigType("video")).thenReturn(mockConfigs);
        when(contentRevenueStageMapper.insert(any())).thenReturn(1);

        // When
        contentRevenueStageService.initializeVideoRevenueStages(videoId, ownerId);

        // Then
        verify(revenueConfigMapper).selectByConfigType("video");
        verify(contentRevenueStageMapper, times(3)).insert(any(ContentRevenueStage.class));
    }

    @Test
    void testGetVideoRevenueStages() {
        // Given
        Long videoId = 100L;
        when(contentRevenueStageMapper.selectByContentId(2, videoId)).thenReturn(mockStages);

        // When
        List<ContentRevenueStage> result = contentRevenueStageService.getVideoRevenueStages(videoId);

        // Then
        assertEquals(2, result.size());
        verify(contentRevenueStageMapper).selectByContentId(2, videoId);
    }

    @Test
    void testCalculateTotalRevenue() {
        // Given
        Long userId = 1L;
        Integer contentType = 2;
        BigDecimal expectedRevenue = new BigDecimal("400.00");
        
        when(contentRevenueStageMapper.sumUnwithdrawnAmount(userId)).thenReturn(expectedRevenue);

        // When
        BigDecimal result = contentRevenueStageService.calculateTotalRevenue(userId, contentType);

        // Then
        assertEquals(expectedRevenue, result);
        verify(contentRevenueStageMapper).sumUnwithdrawnAmount(userId);
    }

    @Test
    void testIsFreeAccessVideo_courseIsFree() {
        // Given
        Long videoId = 100L;
        Long courseId = 1L;
        Course freeCourse = new Course();
        freeCourse.setId(courseId);
        freeCourse.setIsFree(1);
        freeCourse.setLevel(2);
        
        when(courseMapper.selectById(courseId)).thenReturn(freeCourse);

        // When
        boolean result = contentRevenueStageService.isFreeAccessVideo(videoId, courseId);

        // Then
        assertTrue(result);
        verify(courseMapper).selectById(courseId);
    }

    @Test
    void testIsFreeAccessVideo_courseLevelZero() {
        // Given
        Long videoId = 100L;
        Long courseId = 1L;
        Course freeCourse = new Course();
        freeCourse.setId(courseId);
        freeCourse.setIsFree(0);
        freeCourse.setLevel(0); // 所有用户都可访问
        
        when(courseMapper.selectById(courseId)).thenReturn(freeCourse);

        // When
        boolean result = contentRevenueStageService.isFreeAccessVideo(videoId, courseId);

        // Then
        assertTrue(result);
        verify(courseMapper).selectById(courseId);
    }

    @Test
    void testIsFreeAccessVideo_paidCourse() {
        // Given
        Long videoId = 100L;
        Long courseId = 1L;
        Course paidCourse = new Course();
        paidCourse.setId(courseId);
        paidCourse.setIsFree(0);
        paidCourse.setLevel(2); // 需要VIP权限
        
        when(courseMapper.selectById(courseId)).thenReturn(paidCourse);

        // When
        boolean result = contentRevenueStageService.isFreeAccessVideo(videoId, courseId);

        // Then
        assertFalse(result);
        verify(courseMapper).selectById(courseId);
    }

    @Test
    void testIsFreeAccessVideo_courseVideoWithoutCourseId() {
        // Given
        Long videoId = 100L;
        Long courseId = 1L;
        Long chapterId = 1L;
        
        CourseVideo courseVideo = new CourseVideo();
        courseVideo.setChapterId(chapterId);
        
        CourseChapter chapter = new CourseChapter();
        chapter.setId(chapterId);
        chapter.setCourseId(courseId);
        
        Course freeCourse = new Course();
        freeCourse.setId(courseId);
        freeCourse.setIsFree(1);
        freeCourse.setLevel(1);
        
        when(courseVideoMapper.selectByVideoId(videoId)).thenReturn(courseVideo);
        when(courseChapterMapper.selectById(chapterId)).thenReturn(chapter);
        when(courseMapper.selectById(courseId)).thenReturn(freeCourse);

        // When
        boolean result = contentRevenueStageService.isFreeAccessVideo(videoId, null);

        // Then
        assertTrue(result);
        verify(courseVideoMapper).selectByVideoId(videoId);
        verify(courseChapterMapper).selectById(chapterId);
        verify(courseMapper).selectById(courseId);
    }

    @Test
    void testIsFreeAccessVideo_regularVideo() {
        // Given
        Long videoId = 100L;
        
        when(courseVideoMapper.selectByVideoId(videoId)).thenReturn(null);

        // When
        boolean result = contentRevenueStageService.isFreeAccessVideo(videoId, null);

        // Then
        assertFalse(result); // 普通视频参与收益分成
        verify(courseVideoMapper).selectByVideoId(videoId);
    }

    private RevenueConfig createRevenueConfig(String configKey, Integer level, String levelName, 
                                            Integer targetCount, String rewardAmount) {
        RevenueConfig config = new RevenueConfig();
        config.setConfigKey(configKey);
        config.setConfigType("video");
        config.setLevel(level);
        config.setLevelName(levelName);
        config.setTargetCount(targetCount);
        config.setRewardAmount(new BigDecimal(rewardAmount));
        return config;
    }
}
