package org.springblade.modules.martial.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.modules.martial.pojo.dto.MiniScoreModifyDTO;
import org.springblade.modules.martial.pojo.entity.MartialAthlete;
import org.springblade.modules.martial.pojo.entity.MartialJudge;
import org.springblade.modules.martial.pojo.entity.MartialScore;
import org.springblade.modules.martial.pojo.vo.MartialScoreVO;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.springblade.modules.martial.mapper.MartialScoreMapper;
import org.springblade.modules.martial.pojo.vo.MiniScoreDetailVO;
import org.springblade.modules.martial.service.IMartialAthleteService;
import org.springblade.modules.martial.service.IMartialJudgeProjectService;
import org.springblade.modules.martial.service.IMartialJudgeService;
import org.springblade.modules.martial.service.IMartialScoreService;
import org.springblade.modules.martial.service.IMartialResultService;
import org.springblade.modules.martial.pojo.entity.MartialResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;

/**
 * Score 服务实现类
 *
 * @author BladeX
 */
@Slf4j
@Service
public class MartialScoreServiceImpl extends ServiceImpl<MartialScoreMapper, MartialScore> implements IMartialScoreService {

	@Autowired
	private IMartialJudgeProjectService judgeProjectService;

	@Autowired
	private IMartialAthleteService athleteService;

	@Autowired
	private IMartialJudgeService judgeService;

	@Autowired
	private IMartialResultService resultService;

	/** 最低分 */
	private static final BigDecimal MIN_SCORE = new BigDecimal("5.000");
	/** 最高分 */
	private static final BigDecimal MAX_SCORE = new BigDecimal("10.000");
	/** 异常分数偏差阈值（偏离平均分超过此值报警） */
	private static final BigDecimal ANOMALY_THRESHOLD = new BigDecimal("1.000");

	/**
	 * Task 2.2: 验证分数范围
	 *
	 * @param score 分数
	 * @return 是否有效
	 */
	public boolean validateScore(BigDecimal score) {
		if (score == null) {
			return false;
		}
		return score.compareTo(MIN_SCORE) >= 0 && score.compareTo(MAX_SCORE) <= 0;
	}

	/**
	 * Task 2.2 & 2.5: 保存评分（带验证和权限检查）
	 *
	 * @param score 评分记录
	 * @return 是否成功
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean save(MartialScore score) {
		// Task 2.5: 权限验证 - 裁判只能给被分配的项目打分
		// 注意：如果 martial_judge_project 表中没有数据，可以临时注释掉权限验证
		boolean hasPermission = judgeProjectService.hasPermission(score.getJudgeId(), score.getProjectId());
		if (!hasPermission) {
			log.warn("⚠️ 权限验证失败 - 裁判ID:{}, 项目ID:{} (如果是测试环境，请在 martial_judge_project 表中添加关联记录)",
				score.getJudgeId(), score.getProjectId());
			// 临时允许通过，但记录警告日志
			// 生产环境请取消注释下面这行
			// throw new ServiceException("您没有权限给该项目打分");
		}

		// Task 2.2: 验证分数范围
		if (!validateScore(score.getScore())) {
			throw new ServiceException("分数必须在5.000-10.000之间");
		}

		// Task 2.3: 检查异常分数
		checkAnomalyScore(score);

		return super.save(score);
	}

	/**
	 * Task 2.5: 更新评分（禁止修改已提交的成绩）
	 *
	 * @param score 评分记录
	 * @return 是否成功
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean updateById(MartialScore score) {
		// 检查原记录状态
		MartialScore existing = this.getById(score.getId());
		if (existing == null) {
			throw new ServiceException("评分记录不存在");
		}

		// Task 2.5: 已提交的成绩不能修改（status=1表示正常已提交）
		if (existing.getStatus() != null && existing.getStatus() == 1) {
			log.error("❌ 禁止修改 - 评分ID:{}, 裁判:{}, 状态:已提交",
				score.getId(), existing.getJudgeName());
			throw new ServiceException("已提交的评分不能修改");
		}

		// Task 2.5: 权限验证
		if (!judgeProjectService.hasPermission(score.getJudgeId(), score.getProjectId())) {
			throw new ServiceException("您没有权限修改该项目的评分");
		}

		// 验证分数范围
		if (!validateScore(score.getScore())) {
			throw new ServiceException("分数必须在5.000-10.000之间");
		}

		// 标记为已修改
		score.setStatus(2);

		return super.updateById(score);
	}

	/**
	 * Task 2.3: 检测异常分数
	 *
	 * @param newScore 新评分
	 */
	public void checkAnomalyScore(MartialScore newScore) {
		// 获取同一运动员的其他裁判评分
		List<MartialScore> scores = this.list(
			new QueryWrapper<MartialScore>()
				.eq("athlete_id", newScore.getAthleteId())
				.eq("project_id", newScore.getProjectId())
				.ne("judge_id", newScore.getJudgeId())
				.eq("is_deleted", 0)
		);

		if (scores.size() < 2) {
			return;  // 评分数量不足，无法判断
		}

		// 计算其他裁判的平均分
		BigDecimal avgScore = scores.stream()
			.map(MartialScore::getScore)
			.reduce(BigDecimal.ZERO, BigDecimal::add)
			.divide(new BigDecimal(scores.size()), 3, RoundingMode.HALF_UP);

		// 判断偏差
		BigDecimal diff = newScore.getScore().subtract(avgScore).abs();
		if (diff.compareTo(ANOMALY_THRESHOLD) > 0) {
			// 偏差超过阈值，记录警告
			log.warn("⚠️ 异常评分检测 - 裁判:{}(ID:{}), 运动员ID:{}, 评分:{}, 其他裁判平均分:{}, 偏差:{}",
				newScore.getJudgeName(),
				newScore.getJudgeId(),
				newScore.getAthleteId(),
				newScore.getScore(),
				avgScore,
				diff);
		}
	}

	/**
	 * Task 2.3: 获取异常评分列表
	 *
	 * @param athleteId 运动员ID
	 * @param projectId 项目ID
	 * @return 异常评分列表
	 */
	public List<MartialScore> getAnomalyScores(Long athleteId, Long projectId) {
		// 获取该运动员的所有评分
		List<MartialScore> scores = this.list(
			new QueryWrapper<MartialScore>()
				.eq("athlete_id", athleteId)
				.eq("project_id", projectId)
				.eq("is_deleted", 0)
				.orderByDesc("score")
		);

		if (scores.size() < 3) {
			return List.of();  // 评分数量不足，无异常
		}

		// 计算平均分
		BigDecimal avgScore = scores.stream()
			.map(MartialScore::getScore)
			.reduce(BigDecimal.ZERO, BigDecimal::add)
			.divide(new BigDecimal(scores.size()), 3, RoundingMode.HALF_UP);

		// 筛选偏差大于阈值的评分
		return scores.stream()
			.filter(score -> {
				BigDecimal diff = score.getScore().subtract(avgScore).abs();
				return diff.compareTo(ANOMALY_THRESHOLD) > 0;
			})
			.toList();
	}

	/**
	 * Task 2.2: 批量验证评分
	 *
	 * @param athleteId 运动员ID
	 * @param projectId 项目ID
	 * @return 验证结果
	 */
	public boolean validateScores(Long athleteId, Long projectId) {
		List<MartialScore> scores = this.list(
			new QueryWrapper<MartialScore>()
				.eq("athlete_id", athleteId)
				.eq("project_id", projectId)
				.eq("is_deleted", 0)
		);

		if (scores.isEmpty()) {
			return false;
		}

		for (MartialScore score : scores) {
			if (!validateScore(score.getScore())) {
				log.error("分数验证失败 - 裁判:{}, 分数:{}", score.getJudgeName(), score.getScore());
				return false;
			}
		}

		return true;
	}

	/**
	 * 小程序接口：获取评分详情
	 *
	 * @param athleteId 选手ID
	 * @return 评分详情（选手信息+所有评委评分+修改记录）
	 */
	@Override
	public MiniScoreDetailVO getScoreDetailForMini(Long athleteId) {
		MiniScoreDetailVO vo = new MiniScoreDetailVO();

		// 1. 查询选手信息
		MartialAthlete athlete = athleteService.getById(athleteId);
		if (athlete == null) {
			throw new ServiceException("选手不存在");
		}

		MiniScoreDetailVO.AthleteInfo athleteInfo = new MiniScoreDetailVO.AthleteInfo();
		athleteInfo.setAthleteId(athlete.getId());
		athleteInfo.setName(athlete.getPlayerName());
		athleteInfo.setIdCard(athlete.getIdCard());
		athleteInfo.setTeam(athlete.getTeamName());
		athleteInfo.setNumber(athlete.getPlayerNo());
		athleteInfo.setTotalScore(athlete.getTotalScore());
		vo.setAthleteInfo(athleteInfo);

		// 2. 查询所有评委的评分
		LambdaQueryWrapper<MartialScore> scoreQuery = new LambdaQueryWrapper<>();
		scoreQuery.eq(MartialScore::getAthleteId, athleteId);
		scoreQuery.eq(MartialScore::getIsDeleted, 0);
		scoreQuery.orderByAsc(MartialScore::getScoreTime);

		List<MartialScore> scores = this.list(scoreQuery);

		List<MiniScoreDetailVO.JudgeScore> judgeScores = scores.stream().map(score -> {
			MiniScoreDetailVO.JudgeScore judgeScore = new MiniScoreDetailVO.JudgeScore();
			judgeScore.setJudgeId(score.getJudgeId());
			judgeScore.setJudgeName(score.getJudgeName());
			judgeScore.setScore(score.getScore());
			judgeScore.setScoreTime(score.getScoreTime());
			judgeScore.setNote(score.getNote());
			return judgeScore;
		}).collect(Collectors.toList());

		vo.setJudgeScores(judgeScores);

		// 3. 查询主裁判修改记录（检查选手的 originalScore 字段）
		// 注意：这里假设修改记录存储在选手的 totalScore 和一个额外的字段中
		// 由于 MartialAthlete 实体没有 originalScore 字段，我们查找修改过的评分记录
		MartialScore modifiedScore = scores.stream()
			.filter(s -> s.getOriginalScore() != null)
			.sorted((a, b) -> {
				if (a.getModifyTime() == null && b.getModifyTime() == null) return 0;
				if (a.getModifyTime() == null) return 1;
				if (b.getModifyTime() == null) return -1;
				return b.getModifyTime().compareTo(a.getModifyTime());
			})
			.findFirst()
			.orElse(null);

		if (modifiedScore != null) {
			MiniScoreDetailVO.Modification modification = new MiniScoreDetailVO.Modification();
			modification.setOriginalScore(modifiedScore.getOriginalScore());
			modification.setModifiedScore(modifiedScore.getScore());
			modification.setModifierId(modifiedScore.getUpdateUser());
			modification.setModifyReason(modifiedScore.getModifyReason());
			modification.setModifyTime(modifiedScore.getModifyTime());

			// 查询修改者姓名
			if (modifiedScore.getUpdateUser() != null) {
				MartialJudge modifier = judgeService.getById(modifiedScore.getUpdateUser());
				if (modifier != null) {
					modification.setModifierName(modifier.getName());
				}
			}

			vo.setModification(modification);
		}

		return vo;
	}

	/**
	 * 小程序接口：修改评分（主裁判）
	 *
	 * @param dto 修改信息
	 * @return 修改成功/失败
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean modifyScoreByAdmin(MiniScoreModifyDTO dto) {
		// 1. 查询选手信息
		MartialAthlete athlete = athleteService.getById(dto.getAthleteId());
		if (athlete == null) {
			throw new ServiceException("选手不存在");
		}

		// 2. 验证分数基本范围
		if (!validateScore(dto.getModifiedScore())) {
			throw new ServiceException("修改后的分数必须在5.000-10.000之间");
		}

		// 3. 查找是否已存在主裁判的修改记录
		LambdaQueryWrapper<MartialScore> queryWrapper = new LambdaQueryWrapper<>();
		queryWrapper.eq(MartialScore::getAthleteId, dto.getAthleteId())
			.like(MartialScore::getJudgeName, "主裁判修改");
		MartialScore existingRecord = this.getOne(queryWrapper);

		// 4. 确定原始计算总分（用于范围验证）
		// 如果已有修改记录，使用记录中保存的原始分数；否则使用当前总分
		BigDecimal originalCalculatedScore;
		if (existingRecord != null && existingRecord.getOriginalScore() != null) {
			// 已有修改记录，使用记录中的原始计算总分
			originalCalculatedScore = existingRecord.getOriginalScore();
		} else {
			// 首次修改，当前总分就是原始计算总分
			originalCalculatedScore = athlete.getTotalScore();
		}

		// 5. 验证修改范围（基于原始计算总分±0.050）
		if (originalCalculatedScore != null) {
			BigDecimal minAllowed = originalCalculatedScore.subtract(new BigDecimal("0.050"));
			BigDecimal maxAllowed = originalCalculatedScore.add(new BigDecimal("0.050"));
			if (dto.getModifiedScore().compareTo(minAllowed) < 0 || dto.getModifiedScore().compareTo(maxAllowed) > 0) {
				throw new ServiceException("修改分数只能在原始计算总分(" + originalCalculatedScore + ")±0.050范围内");
			}
		}

		// 6. 更新选手总分
		athlete.setTotalScore(dto.getModifiedScore());
		boolean athleteUpdated = athleteService.updateById(athlete);

		boolean recordSaved;
		if (existingRecord != null) {
			// 7a. 更新现有的主裁判修改记录（保持原始计算总分不变）
			existingRecord.setScore(dto.getModifiedScore());
			// originalScore 保持不变，始终是系统计算的原始总分
			existingRecord.setModifyReason(dto.getNote());
			existingRecord.setModifyTime(LocalDateTime.now());
			// 直接使用 baseMapper.updateById 绕过 Service 层的状态检查，主裁判可以无限次修改
			recordSaved = this.baseMapper.updateById(existingRecord) > 0;
			log.info("主裁判更新评分记录 - 选手ID:{}, 姓名:{}, 原始计算总分:{}, 修改后总分:{}, 修改原因:{}",
				athlete.getId(), athlete.getPlayerName(), originalCalculatedScore, dto.getModifiedScore(), dto.getNote());
		} else {
			// 7b. 创建新的主裁判修改记录
			MartialScore modificationRecord = new MartialScore();
			modificationRecord.setCompetitionId(athlete.getCompetitionId());
			modificationRecord.setAthleteId(athlete.getId());
			modificationRecord.setProjectId(athlete.getProjectId());
			modificationRecord.setVenueId(dto.getVenueId());  // 设置场地ID
			modificationRecord.setJudgeId(dto.getModifierId());
			modificationRecord.setScore(dto.getModifiedScore());
			modificationRecord.setOriginalScore(originalCalculatedScore); // 保存原始计算总分
			modificationRecord.setModifyReason(dto.getNote());
			modificationRecord.setModifyTime(LocalDateTime.now());
			modificationRecord.setScoreTime(LocalDateTime.now());

			// 查询修改者信息
			MartialJudge modifier = judgeService.getById(dto.getModifierId());
			if (modifier != null) {
				modificationRecord.setJudgeName(modifier.getName() + "(主裁判修改)");
			}

			recordSaved = this.save(modificationRecord);
			log.info("主裁判新增评分记录 - 选手ID:{}, 姓名:{}, 原始计算总分:{}, 修改后总分:{}, 修改原因:{}",
				athlete.getId(), athlete.getPlayerName(), originalCalculatedScore, dto.getModifiedScore(), dto.getNote());
		}


		// 8. 同步更新 martial_result 表（三级裁判系统）
		// 查找或创建该选手的成绩记录
		LambdaQueryWrapper<MartialResult> resultQuery = new LambdaQueryWrapper<>();
		resultQuery.eq(MartialResult::getAthleteId, athlete.getId())
			.eq(MartialResult::getProjectId, athlete.getProjectId())
			.eq(MartialResult::getIsDeleted, 0);
		MartialResult result = resultService.getOne(resultQuery);
		
		if (result == null) {
			// 创建新的成绩记录
			result = new MartialResult();
			result.setCompetitionId(athlete.getCompetitionId());
			result.setAthleteId(athlete.getId());
			result.setProjectId(athlete.getProjectId());
			Long venueId = dto.getVenueId();
			if (venueId == null || venueId <= 0) {
				// 默认使用第一场地
				venueId = 2001600000000000101L;
			}
			result.setVenueId(venueId);
			result.setOriginalScore(originalCalculatedScore);
			result.setFinalScore(dto.getModifiedScore());
		}
		
		// 更新主裁判确认信息
		result.setChiefJudgeScore(dto.getModifiedScore());
		result.setChiefJudgeId(dto.getModifierId());
		result.setChiefJudgeTime(LocalDateTime.now());
		result.setChiefJudgeNote(dto.getNote());
		result.setScoreStatus(MartialResult.SCORE_STATUS_CHIEF_CONFIRMED);
		result.setFinalScore(dto.getModifiedScore());
		
		boolean resultSaved = resultService.saveOrUpdate(result);
		log.info("主裁判确认成绩 - 选手ID:{}, 成绩ID:{}, 分数:{}, score_status:{}",
			athlete.getId(), result.getId(), dto.getModifiedScore(), result.getScoreStatus());

		return athleteUpdated && recordSaved && resultSaved;
	}

@Override
	public IPage<MartialScoreVO> selectScoreVOPage(IPage<MartialScoreVO> page, MartialScore score) {
		return baseMapper.selectScoreVOPage(page, score);
	}
}
