package org.springblade.modules.martial;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.junit.jupiter.api.*;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.modules.martial.mapper.MartialResultMapper;
import org.springblade.modules.martial.pojo.entity.MartialProject;
import org.springblade.modules.martial.pojo.entity.MartialResult;
import org.springblade.modules.martial.pojo.entity.MartialScore;
import org.springblade.modules.martial.service.IMartialAthleteService;
import org.springblade.modules.martial.service.IMartialCompetitionService;
import org.springblade.modules.martial.service.IMartialProjectService;
import org.springblade.modules.martial.service.IMartialScoreService;
import org.springblade.modules.martial.service.impl.MartialResultServiceImpl;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collections;
import java.util.concurrent.TimeUnit;

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

/**
 * 成绩服务测试类 - 阿里巴巴规范
 *
 * @author test
 * @date 2026-01-16
 */
@ExtendWith(MockitoExtension.class)
@DisplayName("成绩服务单元测试")
class MartialResultServiceAliTest {

    @Spy
    @InjectMocks
    private MartialResultServiceImpl resultService;

    @Mock
    private MartialResultMapper resultMapper;
    @Mock
    private IMartialScoreService scoreService;
    @Mock
    private IMartialAthleteService athleteService;
    @Mock
    private IMartialProjectService projectService;
    @Mock
    private IMartialCompetitionService competitionService;

    // ==================== 计算有效平均分测试 ====================

    @Nested
    @DisplayName("计算有效平均分测试-calculateValidAverageScore()")
    class CalculateValidAverageScoreTests {

        @Test
        @DisplayName("计算平均分-5个裁判评分-应去掉最高最低后取平均")
        void test_calculateValidAverageScore_fiveScores_shouldRemoveMaxMinAndAverage() {
            // Given: 5个裁判评分 9.0, 9.2, 9.5(最高), 8.8(最低), 9.1
            Long athleteId = 1L;
            Long projectId = 10L;
            
            when(scoreService.list(any(QueryWrapper.class))).thenReturn(Arrays.asList(
                buildMartialScore(1L, new BigDecimal("9.0")),
                buildMartialScore(2L, new BigDecimal("9.2")),
                buildMartialScore(3L, new BigDecimal("9.5")),  // 最高分-去掉
                buildMartialScore(4L, new BigDecimal("8.8")),  // 最低分-去掉
                buildMartialScore(5L, new BigDecimal("9.1"))
            ));
            
            // When: 调用计算方法
            BigDecimal result = resultService.calculateValidAverageScore(athleteId, projectId);
            
            // Then: 去掉9.5和8.8后，(9.0+9.2+9.1)/3 = 9.100
            assertEquals(new BigDecimal("9.100"), result, "去掉最高最低后平均分应为9.100");
            verify(scoreService).list(any(QueryWrapper.class));
        }

        @Test
        @DisplayName("计算平均分-3个裁判评分-应去掉最高最低后返回中间值")
        void test_calculateValidAverageScore_threeScores_shouldReturnMiddle() {
            // Given: 3个裁判评分（最少要求）
            when(scoreService.list(any(QueryWrapper.class))).thenReturn(Arrays.asList(
                buildMartialScore(1L, new BigDecimal("9.0")),   // 中间值-保留
                buildMartialScore(2L, new BigDecimal("9.5")),   // 最高分-去掉
                buildMartialScore(3L, new BigDecimal("8.5"))    // 最低分-去掉
            ));
            
            // When: 调用计算方法
            BigDecimal result = resultService.calculateValidAverageScore(1L, 10L);
            
            // Then: 只剩9.0
            assertEquals(new BigDecimal("9.000"), result, "3个评分去掉最高最低后应返回中间值");
        }

        @Test
        @DisplayName("计算平均分-裁判不足3人-应抛出业务异常")
        void test_calculateValidAverageScore_lessThan3Scores_shouldThrowException() {
            // Given: 仅2个裁判评分（不满足最低要求）
            when(scoreService.list(any(QueryWrapper.class))).thenReturn(Arrays.asList(
                buildMartialScore(1L, new BigDecimal("9.0")),
                buildMartialScore(2L, new BigDecimal("9.5"))
            ));
            
            // When & Then: 应抛出业务异常
            ServiceException ex = assertThrows(ServiceException.class,
                () -> resultService.calculateValidAverageScore(1L, 10L));
            
            assertEquals("裁判人数不足3人，无法去最高/最低分", ex.getMessage(),
                "异常消息应明确说明裁判人数不足");
        }

        @Test
        @DisplayName("计算平均分-无评分记录-应抛出业务异常")
        void test_calculateValidAverageScore_noScores_shouldThrowException() {
            // Given: 无评分记录
            when(scoreService.list(any(QueryWrapper.class))).thenReturn(Collections.emptyList());
            
            // When & Then: 应抛出业务异常
            ServiceException ex = assertThrows(ServiceException.class,
                () -> resultService.calculateValidAverageScore(1L, 10L));
            
            assertEquals("该运动员尚未有裁判评分", ex.getMessage(),
                "异常消息应明确说明无评分记录");
        }

        @Test
        @Timeout(value = 200, unit = TimeUnit.MILLISECONDS)
        @DisplayName("计算平均分-性能测试-应在200ms内完成")
        void test_calculateValidAverageScore_performance() {
            // Given: 模拟7个裁判评分
            when(scoreService.list(any(QueryWrapper.class))).thenReturn(Arrays.asList(
                buildMartialScore(1L, new BigDecimal("9.0")),
                buildMartialScore(2L, new BigDecimal("9.1")),
                buildMartialScore(3L, new BigDecimal("9.2")),
                buildMartialScore(4L, new BigDecimal("9.3")),
                buildMartialScore(5L, new BigDecimal("9.4")),
                buildMartialScore(6L, new BigDecimal("8.8")),
                buildMartialScore(7L, new BigDecimal("9.5"))
            ));
            
            // When & Then: 100次计算应在200ms内完成
            for (int i = 0; i < 100; i++) {
                resultService.calculateValidAverageScore(1L, 10L);
            }
        }
    }

    // ==================== 应用难度系数测试 ====================

    @Nested
    @DisplayName("应用难度系数测试-applyDifficultyCoefficient()")
    class ApplyDifficultyCoefficientTests {

        @Test
        @DisplayName("应用难度系数-系数1.2-应增加20%分数")
        void test_applyDifficultyCoefficient_coefficient1_2_shouldIncrease20Percent() {
            // Given: 项目难度系数1.20（高难度项目）
            Long projectId = 10L;
            MartialProject project = new MartialProject();
            project.setId(projectId);
            project.setDifficultyCoefficient(new BigDecimal("1.20"));
            
            when(projectService.getById(projectId)).thenReturn(project);
            
            // When: 应用难度系数
            BigDecimal result = resultService.applyDifficultyCoefficient(
                new BigDecimal("9.000"), projectId);
            
            // Then: 9.000 * 1.20 = 10.800
            assertEquals(new BigDecimal("10.800"), result, "应用1.2系数后分数应为10.800");
            verify(projectService).getById(projectId);
        }

        @Test
        @DisplayName("应用难度系数-系数为空-应默认使用1.0")
        void test_applyDifficultyCoefficient_nullCoefficient_shouldDefault1_0() {
            // Given: 项目未设置难度系数
            Long projectId = 10L;
            MartialProject project = new MartialProject();
            project.setId(projectId);
            project.setDifficultyCoefficient(null);
            
            when(projectService.getById(projectId)).thenReturn(project);
            
            // When: 应用难度系数
            BigDecimal result = resultService.applyDifficultyCoefficient(
                new BigDecimal("9.000"), projectId);
            
            // Then: 9.000 * 1.00 = 9.000
            assertEquals(new BigDecimal("9.000"), result, "系数为空时应默认使用1.0");
        }

        @Test
        @DisplayName("应用难度系数-项目不存在-应抛出业务异常")
        void test_applyDifficultyCoefficient_projectNotFound_shouldThrowException() {
            // Given: 项目不存在
            when(projectService.getById(anyLong())).thenReturn(null);
            
            // When & Then: 应抛出业务异常
            ServiceException ex = assertThrows(ServiceException.class,
                () -> resultService.applyDifficultyCoefficient(new BigDecimal("9.000"), 999L));
            
            assertEquals("项目不存在", ex.getMessage(), "异常消息应明确说明项目不存在");
        }

        @Test
        @DisplayName("应用难度系数-系数0.9-应减少10%分数")
        void test_applyDifficultyCoefficient_coefficient0_9_shouldDecrease10Percent() {
            // Given: 项目难度系数0.90（低难度项目）
            Long projectId = 10L;
            MartialProject project = new MartialProject();
            project.setId(projectId);
            project.setDifficultyCoefficient(new BigDecimal("0.90"));
            
            when(projectService.getById(projectId)).thenReturn(project);
            
            // When: 应用难度系数
            BigDecimal result = resultService.applyDifficultyCoefficient(
                new BigDecimal("10.000"), projectId);
            
            // Then: 10.000 * 0.90 = 9.000
            assertEquals(new BigDecimal("9.000"), result, "应用0.9系数后分数应为9.000");
        }
    }

    // ==================== 测试数据构造方法 ====================

    /**
     * 构造评分测试数据
     */
    private MartialScore buildMartialScore(Long judgeId, BigDecimal score) {
        MartialScore s = new MartialScore();
        s.setId(judgeId);
        s.setJudgeId(judgeId);
        s.setAthleteId(100L);
        s.setProjectId(10L);
        s.setScore(score);
        return s;
    }
}
