package org.springblade.modules.martial;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
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.springblade.core.log.exception.ServiceException;
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.IMartialProjectService;
import org.springblade.modules.martial.service.IMartialScoreService;
import org.springblade.modules.martial.service.impl.MartialResultServiceImpl;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

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

/**
 * 成绩计算引擎测试类
 *
 * @author BladeX
 */
@ExtendWith(MockitoExtension.class)
@DisplayName("成绩计算引擎测试")
public class MartialResultServiceTest {

	@InjectMocks
	private MartialResultServiceImpl resultService;

	@Mock
	private IMartialScoreService scoreService;

	@Mock
	private IMartialProjectService projectService;

	private Long athleteId = 1L;
	private Long projectId = 1L;
	private List<MartialScore> mockScores;
	private MartialProject mockProject;

	@BeforeEach
	void setUp() {
		// 准备测试数据
		mockScores = new ArrayList<>();
		mockProject = new MartialProject();
		mockProject.setId(projectId);
		mockProject.setProjectName("长拳");
		mockProject.setDifficultyCoefficient(new BigDecimal("1.2"));
	}

	@Test
	@DisplayName("测试1.1: 计算有效平均分 - 正常情况（5个裁判）")
	void testCalculateValidAverageScore_Normal() {
		// 准备5个裁判评分: 9.0, 9.5, 9.2, 8.8, 9.3
		// 去掉最高(9.5)和最低(8.8)后：9.0 + 9.2 + 9.3 = 27.5 / 3 = 9.167
		createMockScores(new double[]{9.0, 9.5, 9.2, 8.8, 9.3});
		when(scoreService.list(any(QueryWrapper.class))).thenReturn(mockScores);

		BigDecimal result = resultService.calculateValidAverageScore(athleteId, projectId);

		assertEquals(new BigDecimal("9.167"), result);
	}

	@Test
	@DisplayName("测试1.1: 计算有效平均分 - 边界情况（3个裁判）")
	void testCalculateValidAverageScore_MinimumJudges() {
		// 3个裁判：9.0, 9.5, 8.5
		// 去掉最高(9.5)和最低(8.5)后：9.0 / 1 = 9.000
		createMockScores(new double[]{9.0, 9.5, 8.5});
		when(scoreService.list(any(QueryWrapper.class))).thenReturn(mockScores);

		BigDecimal result = resultService.calculateValidAverageScore(athleteId, projectId);

		assertEquals(new BigDecimal("9.000"), result);
	}

	@Test
	@DisplayName("测试1.1: 计算有效平均分 - 异常情况（裁判不足）")
	void testCalculateValidAverageScore_InsufficientJudges() {
		// 只有2个裁判，应该抛出异常
		createMockScores(new double[]{9.0, 9.5});
		when(scoreService.list(any(QueryWrapper.class))).thenReturn(mockScores);

		ServiceException exception = assertThrows(
			ServiceException.class,
			() -> resultService.calculateValidAverageScore(athleteId, projectId)
		);

		assertTrue(exception.getMessage().contains("裁判人数不足3人"));
	}

	@Test
	@DisplayName("测试1.1: 计算有效平均分 - 异常情况（无评分）")
	void testCalculateValidAverageScore_NoScores() {
		when(scoreService.list(any(QueryWrapper.class))).thenReturn(new ArrayList<>());

		ServiceException exception = assertThrows(
			ServiceException.class,
			() -> resultService.calculateValidAverageScore(athleteId, projectId)
		);

		assertTrue(exception.getMessage().contains("尚未有裁判评分"));
	}

	@Test
	@DisplayName("测试1.3: 应用难度系数 - 正常情况")
	void testApplyDifficultyCoefficient_Normal() {
		when(projectService.getById(projectId)).thenReturn(mockProject);

		// 平均分9.0 * 难度系数1.2 = 10.800
		BigDecimal result = resultService.applyDifficultyCoefficient(
			new BigDecimal("9.0"),
			projectId
		);

		assertEquals(new BigDecimal("10.800"), result);
	}

	@Test
	@DisplayName("测试1.3: 应用难度系数 - 默认系数")
	void testApplyDifficultyCoefficient_DefaultCoefficient() {
		mockProject.setDifficultyCoefficient(null); // 难度系数为空
		when(projectService.getById(projectId)).thenReturn(mockProject);

		// 默认系数1.00
		BigDecimal result = resultService.applyDifficultyCoefficient(
			new BigDecimal("9.0"),
			projectId
		);

		assertEquals(new BigDecimal("9.000"), result);
	}

	@Test
	@DisplayName("测试1.5: 自动排名 - 无并列情况")
	void testAutoRanking_NoTies() {
		// 创建3个成绩：9.5, 9.2, 8.8
		List<MartialResult> results = new ArrayList<>();
		results.add(createResult(1L, new BigDecimal("9.5")));
		results.add(createResult(2L, new BigDecimal("9.2")));
		results.add(createResult(3L, new BigDecimal("8.8")));

		// Mock服务方法（需要在实际测试中实现完整的mock）
		// 预期：第1名、第2名、第3名
		assertEquals(3, results.size());
	}

	@Test
	@DisplayName("测试1.5: 自动排名 - 有并列情况")
	void testAutoRanking_WithTies() {
		// 创建4个成绩：9.5, 9.5, 9.2, 8.8
		List<MartialResult> results = new ArrayList<>();
		results.add(createResult(1L, new BigDecimal("9.5")));
		results.add(createResult(2L, new BigDecimal("9.5"))); // 并列第1
		results.add(createResult(3L, new BigDecimal("9.2")));
		results.add(createResult(4L, new BigDecimal("8.8")));

		// 预期：第1名(并列)、第1名(并列)、第3名、第4名
		assertEquals(4, results.size());
	}

	@Test
	@DisplayName("测试1.6: 分配奖牌 - 正常情况")
	void testAssignMedals_Normal() {
		List<MartialResult> results = new ArrayList<>();
		MartialResult gold = createResult(1L, new BigDecimal("9.5"));
		MartialResult silver = createResult(2L, new BigDecimal("9.2"));
		MartialResult bronze = createResult(3L, new BigDecimal("8.8"));

		gold.setRanking(1);
		silver.setRanking(2);
		bronze.setRanking(3);

		results.add(gold);
		results.add(silver);
		results.add(bronze);

		// 验证前三名应该有对应的奖牌
		assertEquals(3, results.size());
	}

	@Test
	@DisplayName("测试精度计算 - BigDecimal保留3位小数")
	void testBigDecimalPrecision() {
		BigDecimal value1 = new BigDecimal("9.1234");
		BigDecimal value2 = new BigDecimal("1.2");

		BigDecimal result = value1.multiply(value2)
			.setScale(3, java.math.RoundingMode.HALF_UP);

		assertEquals(new BigDecimal("10.948"), result);
	}

	// ===== 辅助方法 =====

	private void createMockScores(double[] scores) {
		mockScores.clear();
		for (int i = 0; i < scores.length; i++) {
			MartialScore score = new MartialScore();
			score.setId((long) (i + 1));
			score.setAthleteId(athleteId);
			score.setProjectId(projectId);
			score.setJudgeId((long) (i + 1));
			score.setScore(new BigDecimal(String.valueOf(scores[i])));
			mockScores.add(score);
		}
	}

	private MartialResult createResult(Long id, BigDecimal finalScore) {
		MartialResult result = new MartialResult();
		result.setId(id);
		result.setAthleteId(id);
		result.setProjectId(projectId);
		result.setFinalScore(finalScore);
		return result;
	}
}
