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.MartialAthlete;
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 static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;

/**
 * MartialResultServiceImpl Unit Tests - Apple Standard
 * Tests ACTUALLY call the Service methods with proper mocking
 */
@ExtendWith(MockitoExtension.class)
@DisplayName("Apple Standard: MartialResultServiceImpl Tests")
class MartialResultServiceAppleTest {

    @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;

    // ==================== calculateValidAverageScore() - Real Method Calls ====================

    @Nested
    @DisplayName("test_calculateValidAverageScore - Real Service Method Calls")
    class CalculateValidAverageScoreRealTests {

        @Test
        @DisplayName("test_calculateValidAverageScore_withFiveScores_shouldRemoveMaxMinAndAverage")
        void test_calculateValidAverageScore_withFiveScores_shouldRemoveMaxMinAndAverage() {
            // Arrange
            Long athleteId = 1L;
            Long projectId = 10L;
            
            // Mock scores: 9.0, 9.2, 9.5(max), 8.8(min), 9.1
            // After removing max/min: 9.0, 9.2, 9.1 -> avg = 9.100
            when(scoreService.list(any(QueryWrapper.class))).thenReturn(Arrays.asList(
                createScore(1L, new BigDecimal("9.0")),
                createScore(2L, new BigDecimal("9.2")),
                createScore(3L, new BigDecimal("9.5")),  // max - removed
                createScore(4L, new BigDecimal("8.8")),  // min - removed
                createScore(5L, new BigDecimal("9.1"))
            ));
            
            // Act - Call REAL method
            BigDecimal result = resultService.calculateValidAverageScore(athleteId, projectId);
            
            // Assert
            assertEquals(new BigDecimal("9.100"), result);
            verify(scoreService).list(any(QueryWrapper.class));
        }

        @Test
        @DisplayName("test_calculateValidAverageScore_withThreeScores_shouldRemoveMaxMinAndReturnMiddle")
        void test_calculateValidAverageScore_withThreeScores_shouldRemoveMaxMinAndReturnMiddle() {
            // Arrange
            when(scoreService.list(any(QueryWrapper.class))).thenReturn(Arrays.asList(
                createScore(1L, new BigDecimal("9.0")),   // middle - kept
                createScore(2L, new BigDecimal("9.5")),   // max - removed
                createScore(3L, new BigDecimal("8.5"))    // min - removed
            ));
            
            // Act - Call REAL method
            BigDecimal result = resultService.calculateValidAverageScore(1L, 10L);
            
            // Assert - only 9.0 remains
            assertEquals(new BigDecimal("9.000"), result);
        }

        @Test
        @DisplayName("test_calculateValidAverageScore_withLessThan3Scores_shouldThrowException")
        void test_calculateValidAverageScore_withLessThan3Scores_shouldThrowException() {
            // Arrange - only 2 scores
            when(scoreService.list(any(QueryWrapper.class))).thenReturn(Arrays.asList(
                createScore(1L, new BigDecimal("9.0")),
                createScore(2L, new BigDecimal("9.5"))
            ));
            
            // Act & Assert - Call REAL method
            ServiceException ex = assertThrows(ServiceException.class,
                () -> resultService.calculateValidAverageScore(1L, 10L));
            
            assertEquals("裁判人数不足3人，无法去最高/最低分", ex.getMessage());
        }

        @Test
        @DisplayName("test_calculateValidAverageScore_withEmptyScores_shouldThrowException")
        void test_calculateValidAverageScore_withEmptyScores_shouldThrowException() {
            // Arrange - no scores
            when(scoreService.list(any(QueryWrapper.class))).thenReturn(Collections.emptyList());
            
            // Act & Assert - Call REAL method
            ServiceException ex = assertThrows(ServiceException.class,
                () -> resultService.calculateValidAverageScore(1L, 10L));
            
            assertEquals("该运动员尚未有裁判评分", ex.getMessage());
        }

        @Test
        @DisplayName("test_calculateValidAverageScore_withDuplicateMaxScores_shouldRemoveOnlyOne")
        void test_calculateValidAverageScore_withDuplicateMaxScores_shouldRemoveOnlyOne() {
            // Arrange: 9.5, 9.5, 9.0, 8.5
            // Remove one 9.5 (max) and 8.5 (min)
            // Remaining: 9.5, 9.0 -> avg = 9.250
            when(scoreService.list(any(QueryWrapper.class))).thenReturn(Arrays.asList(
                createScore(1L, new BigDecimal("9.5")),
                createScore(2L, new BigDecimal("9.5")),
                createScore(3L, new BigDecimal("9.0")),
                createScore(4L, new BigDecimal("8.5"))
            ));
            
            // Act - Call REAL method
            BigDecimal result = resultService.calculateValidAverageScore(1L, 10L);
            
            // Assert
            assertEquals(new BigDecimal("9.250"), result);
        }
    }

    // ==================== applyDifficultyCoefficient() - Real Method Calls ====================

    @Nested
    @DisplayName("test_applyDifficultyCoefficient - Real Service Method Calls")
    class ApplyDifficultyCoefficientRealTests {

        @Test
        @DisplayName("test_applyDifficultyCoefficient_withCoefficient1_2_shouldIncrease20Percent")
        void test_applyDifficultyCoefficient_withCoefficient1_2_shouldIncrease20Percent() {
            // Arrange
            Long projectId = 10L;
            MartialProject project = new MartialProject();
            project.setId(projectId);
            project.setDifficultyCoefficient(new BigDecimal("1.20"));
            
            when(projectService.getById(projectId)).thenReturn(project);
            
            // Act - Call REAL method
            BigDecimal result = resultService.applyDifficultyCoefficient(
                new BigDecimal("9.000"), projectId);
            
            // Assert: 9.000 * 1.20 = 10.800
            assertEquals(new BigDecimal("10.800"), result);
            verify(projectService).getById(projectId);
        }

        @Test
        @DisplayName("test_applyDifficultyCoefficient_withNullCoefficient_shouldDefault1_0")
        void test_applyDifficultyCoefficient_withNullCoefficient_shouldDefault1_0() {
            // Arrange
            Long projectId = 10L;
            MartialProject project = new MartialProject();
            project.setId(projectId);
            project.setDifficultyCoefficient(null); // null coefficient
            
            when(projectService.getById(projectId)).thenReturn(project);
            
            // Act - Call REAL method
            BigDecimal result = resultService.applyDifficultyCoefficient(
                new BigDecimal("9.000"), projectId);
            
            // Assert: 9.000 * 1.00 = 9.000
            assertEquals(new BigDecimal("9.000"), result);
        }

        @Test
        @DisplayName("test_applyDifficultyCoefficient_withNonExistentProject_shouldThrowException")
        void test_applyDifficultyCoefficient_withNonExistentProject_shouldThrowException() {
            // Arrange
            when(projectService.getById(anyLong())).thenReturn(null);
            
            // Act & Assert - Call REAL method
            ServiceException ex = assertThrows(ServiceException.class,
                () -> resultService.applyDifficultyCoefficient(new BigDecimal("9.000"), 999L));
            
            assertEquals("项目不存在", ex.getMessage());
        }
    }

    // ==================== Helper Methods ====================

    private MartialScore createScore(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;
    }
}
