package cn.iocoder.yudao.module.llm.service.modelassesstaskmanual;

import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.llm.controller.admin.modelassesstaskmanual.vo.*;
import cn.iocoder.yudao.module.llm.controller.admin.modelassesstaskmanualbackup.vo.ModelAssessTaskManualBackupSaveReqVO;
import cn.iocoder.yudao.module.llm.dal.dataobject.basemodel.BaseModelDO;
import cn.iocoder.yudao.module.llm.dal.dataobject.dataset.DatasetDO;
import cn.iocoder.yudao.module.llm.dal.dataobject.modelassesstaskmanual.ManualModelAnswerDO;
import cn.iocoder.yudao.module.llm.dal.dataobject.modelassesstaskmanual.ModelAssessDimensionDO;
import cn.iocoder.yudao.module.llm.dal.dataobject.modelassesstaskmanual.ModelAssessTaskDimensionDO;
import cn.iocoder.yudao.module.llm.dal.dataobject.modelassesstaskmanual.ModelAssessTaskManualDO;
import cn.iocoder.yudao.module.llm.dal.dataobject.modelassesstaskmanualbackup.ModelAssessTaskManualBackupDO;
import cn.iocoder.yudao.module.llm.dal.dataobject.modelservice.ModelServiceDO;
import cn.iocoder.yudao.module.llm.dal.mysql.basemodel.BaseModelMapper;
import cn.iocoder.yudao.module.llm.dal.mysql.dataset.DatasetMapper;
import cn.iocoder.yudao.module.llm.dal.mysql.modelassesstaskmanual.*;
import cn.iocoder.yudao.module.llm.dal.mysql.modelservice.ModelServiceMapper;
import cn.iocoder.yudao.module.llm.service.async.AsyncModelAccessManualService;
import cn.iocoder.yudao.module.llm.service.dataset.DatasetQuestionService;
import cn.iocoder.yudao.module.llm.service.modelassesstaskmanualbackup.ModelAssessTaskManualBackupService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.llm.enums.ErrorCodeConstants.MODEL_ASSESS_TASK_MANUAL_NAME_EXISTS;
import static cn.iocoder.yudao.module.llm.enums.ErrorCodeConstants.MODEL_ASSESS_TASK_MANUAL_NOT_EXISTS;

/**
 * 模型评估任务 —— 人工评估 Service 实现类
 *
 * @author 华大大模型
 */
@Service
@Validated
public class ModelAssessTaskManualServiceImpl implements ModelAssessTaskManualService {

    @Resource
    private ModelAssessTaskManualMapper modelAssessTaskManualMapper;
    @Resource
    private ModelAssessTaskDimensionMapper modelAssessTaskDimensionMapper;
    @Resource
    private ModelServiceMapper modelServiceMapper;
    @Resource
    private DatasetMapper datasetMapper;
    @Resource
    private ModelAssessDimensionMapper modelAssessDimensionMapper;
    @Resource
    private DatasetQuestionService datasetQuestionService;

    @Resource
    private AsyncModelAccessManualService asyncModelAccessManualService;
    @Resource
    private ModelAssessTaskManualBackupService modelAssessTaskManualBackupService;
    @Resource
    private ManualModelAnnoMapper manualModelAnnoMapper;
    @Resource
    private BaseModelMapper baseModelMapper;
    @Resource
    private ManualModelAnswerMapper manualModelAnswerMapper;

    @Override
    public Long createModelAssessTaskManual (ModelAssessTaskManualSaveReqVO createReqVO) {
        validateModelAssessTaskManualNameExists(createReqVO);
        // 插入
        ModelAssessTaskManualDO modelAssessTaskManual = BeanUtils.toBean(createReqVO, ModelAssessTaskManualDO.class);
        modelAssessTaskManual.setStatus(3);
        modelAssessTaskManualMapper.insert(modelAssessTaskManual);
        List<ModelAssessTaskDimensionSaveReqVO> dimensions = createReqVO.getDimensions();
        if (!CollectionUtils.isEmpty(dimensions)) {
            dimensions.stream().forEach(dimension -> {
                dimension.setTaskId(modelAssessTaskManual.getId());
                ModelAssessTaskDimensionDO modelAssessTaskDimensionDO = BeanUtils.toBean(dimension, ModelAssessTaskDimensionDO.class);
                modelAssessTaskDimensionMapper.insert(modelAssessTaskDimensionDO);
            });
        }
        asyncModelAccessManualService.startModelAccess(modelAssessTaskManual);
        // 返回
        return modelAssessTaskManual.getId();
    }

    @Override
    public void updateModelAssessTaskManual (ModelAssessTaskManualSaveReqVO updateReqVO) {
        // 校验存在
        validateModelAssessTaskManualNameExists(updateReqVO);
        validateModelAssessTaskManualExists(updateReqVO.getId());
        // 更新
        ModelAssessTaskManualDO updateObj = BeanUtils.toBean(updateReqVO, ModelAssessTaskManualDO.class);
        List<ModelAssessTaskDimensionSaveReqVO> dimensions = updateReqVO.getDimensions();
        if (!CollectionUtils.isEmpty(dimensions)) {
            List<Long> dimensionsIds = dimensions.stream()
                    .filter(dimension -> dimension.getId() != null)
                    .map(dimension -> dimension.getId()).collect(Collectors.toList());
            LambdaQueryWrapper<ModelAssessTaskDimensionDO> wrapper = new LambdaQueryWrapper<ModelAssessTaskDimensionDO>()
                    .eq(ModelAssessTaskDimensionDO::getTaskId, updateReqVO.getId());
            if (!CollectionUtils.isEmpty(dimensionsIds)) {
                wrapper.notIn(ModelAssessTaskDimensionDO::getId, dimensionsIds);
            }
            modelAssessTaskDimensionMapper.delete(wrapper);
            dimensions.stream().forEach(dimension -> {
                ModelAssessTaskDimensionDO modelAssessTaskDimensionDO = BeanUtils.toBean(dimension, ModelAssessTaskDimensionDO.class);
                modelAssessTaskDimensionMapper.insertOrUpdate(modelAssessTaskDimensionDO);
            });
        }

        modelAssessTaskManualMapper.updateById(updateObj);
    }

    @Override
    public void deleteModelAssessTaskManual (Long id) {
        // 校验存在
        validateModelAssessTaskManualExists(id);
        LambdaUpdateWrapper<ModelAssessTaskManualDO> wrapper = new LambdaUpdateWrapper<>();
        wrapper.eq(ModelAssessTaskManualDO::getId, id);
        wrapper.set(ModelAssessTaskManualDO::getDeleted, true);
        // 删除
        modelAssessTaskManualMapper.update(null, wrapper);
    }

    private void validateModelAssessTaskManualExists (Long id) {
        if (modelAssessTaskManualMapper.selectById(id) == null) {
            throw exception(MODEL_ASSESS_TASK_MANUAL_NOT_EXISTS);
        }
    }

    private void validateModelAssessTaskManualNameExists (ModelAssessTaskManualSaveReqVO reqVO) {
        LambdaQueryWrapper<ModelAssessTaskManualDO> wrapper = new LambdaQueryWrapper<ModelAssessTaskManualDO>()
                .eq(ModelAssessTaskManualDO::getTaskName, reqVO.getTaskName());
        if (reqVO.getId() != null) {
            wrapper.ne(ModelAssessTaskManualDO::getId, reqVO.getId());
        }
        List<ModelAssessTaskManualDO> modelAssessTaskManualDOS = modelAssessTaskManualMapper.selectList(wrapper);
        if (!CollectionUtils.isEmpty(modelAssessTaskManualDOS)) {
            throw exception(MODEL_ASSESS_TASK_MANUAL_NAME_EXISTS);
        }
    }

    @Override
    public ModelAssessTaskManualRespVO getModelAssessTaskManual (Long id) {
        ModelAssessTaskManualDO modelAssessTaskManual = modelAssessTaskManualMapper.selectById(id);
        ModelAssessTaskManualRespVO result = BeanUtils.toBean(modelAssessTaskManual, ModelAssessTaskManualRespVO.class);
        List<ModelAssessTaskDimensionDO> modelAssessTaskDimensionDOS = modelAssessTaskDimensionMapper.selectList(new LambdaQueryWrapper<ModelAssessTaskDimensionDO>()
                .eq(ModelAssessTaskDimensionDO::getTaskId, id)
                .eq(ModelAssessTaskDimensionDO::getDeleted, false));
        List<ModelAssessTaskDimensionRespVO> modelAssessTaskDimensionRespVOS = BeanUtils.toBean(modelAssessTaskDimensionDOS, ModelAssessTaskDimensionRespVO.class);
        // 提取id
        List<Long> dimensionIds = modelAssessTaskDimensionDOS.stream().map(ModelAssessTaskDimensionDO::getDimensionId).collect(Collectors.toList());
        // 查询模型评估维度 将维度名称返回
        if (!CollectionUtils.isEmpty(dimensionIds)) {
            List<ModelAssessDimensionDO> modelAssessDimensionDOS = modelAssessDimensionMapper.selectList(new LambdaQueryWrapper<ModelAssessDimensionDO>()
                    .in(ModelAssessDimensionDO::getId, dimensionIds));
            Map<Long, ModelAssessDimensionDO> longModelDimesionDOMap = cn.iocoder.yudao.framework.common.util.collection.
                    CollectionUtils.convertMap(modelAssessDimensionDOS, ModelAssessDimensionDO::getId);
            modelAssessTaskDimensionRespVOS.stream().forEach(
                    modelAssessTaskDimensionRespVO -> {
                        ModelAssessDimensionDO modelAssessDimensionDO = longModelDimesionDOMap.get(modelAssessTaskDimensionRespVO.getDimensionId());
                        if (modelAssessDimensionDO != null) {
                            modelAssessTaskDimensionRespVO.setDimensionName(modelAssessDimensionDO.getDimension());
                        }
                    }
            );
        }
        result.setDimensions(modelAssessTaskDimensionRespVOS);
        if (modelAssessTaskManual.getModelType() == 0) {
            ModelServiceDO modelServiceDO = modelServiceMapper.selectById(modelAssessTaskManual.getModelService());
            if (modelServiceDO != null) {
                result.setModelServiceName(modelServiceDO.getServiceName());
            }
        } else {
            BaseModelDO baseModelDO = baseModelMapper.selectById(modelAssessTaskManual.getModelService());
            if (baseModelDO != null) {
                result.setModelServiceName(baseModelDO.getModelName());
            }
        }
        DatasetDO datasetDO = datasetMapper.selectById(modelAssessTaskManual.getDataset());
        if (datasetDO != null) {
            result.setDatasetName(datasetDO.getDatasetName());
        }
        return result;
    }

    @Override
    public PageResult<ModelAssessTaskManualRespVO> getModelAssessTaskManualPage1 (ModelAssessTaskManualPageReqVO pageReqVO) {
        PageResult<ModelAssessTaskManualDO> modelAssessTaskManualDOPageResult = modelAssessTaskManualMapper.selectPage(pageReqVO);
        PageResult<ModelAssessTaskManualRespVO> result = BeanUtils.toBean(modelAssessTaskManualDOPageResult, ModelAssessTaskManualRespVO.class);
        if (CollectionUtils.isNotEmpty(result.getList())) {
            // 我的模型获取校验
            List<Long> serviceMyIds = modelAssessTaskManualDOPageResult.getList().stream().
                    filter(modelAssessTaskManualDO -> modelAssessTaskManualDO.getModelType() == 0)
                    .map(ModelAssessTaskManualDO::getModelService).collect(Collectors.toList());
            List<ModelServiceDO> modelServiceDOS = new ArrayList<>();
            if (CollectionUtils.isNotEmpty(serviceMyIds)) {
                modelServiceDOS = modelServiceMapper.selectList(new LambdaQueryWrapper<ModelServiceDO>()
                        .in(ModelServiceDO::getId, serviceMyIds));
            }
            Map<Long, ModelServiceDO> serviceMyMaps = cn.iocoder.yudao.framework.common.util.collection.
                    CollectionUtils.convertMap(modelServiceDOS, ModelServiceDO::getId);
            // 人工模型获取校验
            List<Long> serviceForeseeIds = modelAssessTaskManualDOPageResult.getList().stream().
                    filter(modelAssessTaskManualDO -> modelAssessTaskManualDO.getModelType() == 1)
                    .map(ModelAssessTaskManualDO::getModelService).collect(Collectors.toList());
            List<BaseModelDO> baseModelDOS = new ArrayList<>();
            if (CollectionUtils.isNotEmpty(serviceForeseeIds)) {
                baseModelDOS = baseModelMapper.selectList(new LambdaQueryWrapper<BaseModelDO>()
                        .in(BaseModelDO::getId, serviceForeseeIds));
            }
            Map<Long, BaseModelDO> serviceForeseeMaps = cn.iocoder.yudao.framework.common.util.collection.
                    CollectionUtils.convertMap(baseModelDOS, BaseModelDO::getId);
            result.getList().stream().forEach(modelAssessTaskManualRespVO -> {
                if (modelAssessTaskManualRespVO.getModelType() == 0) {
                    ModelServiceDO modelServiceDO = serviceMyMaps.get(modelAssessTaskManualRespVO.getModelService());
                    if (modelServiceDO != null) {
                        modelAssessTaskManualRespVO.setModelServiceName(modelServiceDO.getServiceName());
                    }
                } else {
                    BaseModelDO baseModelDO = serviceForeseeMaps.get(modelAssessTaskManualRespVO.getModelService());
                    if (baseModelDO != null) {
                        modelAssessTaskManualRespVO.setModelServiceName(baseModelDO.getModelName());
                    }
                }

            });
        }
        return result;
    }

    @Override
    public PageResult<ModelAssessTaskManualDO> getModelAssessTaskManualPage (ModelAssessTaskManualPageReqVO pageReqVO) {
        return modelAssessTaskManualMapper.selectPage(pageReqVO);
    }


    @Override
    public void backupModelAssessTaskManual (Long id) {
        ModelAssessTaskManualDO modelDo = modelAssessTaskManualMapper.selectById(id);
        if (modelDo == null) {
            throw exception(MODEL_ASSESS_TASK_MANUAL_NOT_EXISTS);
        }
        this.modelAssessTaskManualBackupService.isBackup(id);
        ModelAssessTaskManualBackupSaveReqVO backupDo = BeanUtils.toBean(modelDo, ModelAssessTaskManualBackupSaveReqVO.class);
        backupDo.setId(null);
        backupDo.setSrcId(id);
        Long modelAssessTaskManualBackup = this.modelAssessTaskManualBackupService.createModelAssessTaskManualBackup(backupDo);

        modelDo.setBackupId(modelAssessTaskManualBackup);
        modelAssessTaskManualMapper.updateById(modelDo);
        ModelAssessTaskManualBackupDO modelAssessTaskManualBackup1 = this.modelAssessTaskManualBackupService.getModelAssessTaskManualBackup(modelAssessTaskManualBackup);
        asyncModelAccessManualService.manualBackup(modelAssessTaskManualBackup1);
    }

    @Override
    public void unBackup (Long id) {
        ModelAssessTaskManualDO modelAssessTaskManualDO = modelAssessTaskManualMapper.selectById(id);
        Long backupId;
        if (modelAssessTaskManualDO == null){
            backupId  = modelAssessTaskManualMapper.selectTrueById(id);
        }else {
            backupId = modelAssessTaskManualDO.getBackupId();
            modelAssessTaskManualDO.setBackupId(0L);
            modelAssessTaskManualMapper.updateById(modelAssessTaskManualDO);
        }
        this.modelAssessTaskManualBackupService.unBackup(backupId);
    }

    /**
     * 通用的根据条件查询 ModelAssessTaskManualDO 列表并转换为 Map 的方法
     *
     * @param condition   条件，使用 BiFunction 接收 LambdaQueryWrapper 和参数并设置条件
     * @param keyMapper   ApplicationDO 对象映射为 Map 的键
     * @param valueMapper ApplicationDO 对象映射为 Map 的值
     * @param param      查询参数数组
     * @return 人工评估的 Map 集合
     */
    private Map<Long, String> getModelAssessmentTaskMap(BiFunction<LambdaQueryWrapper<ModelAssessTaskManualDO>, Long, LambdaQueryWrapper<ModelAssessTaskManualDO>> condition, Function<ModelAssessTaskManualDO, Long> keyMapper, Function<ModelAssessTaskManualDO, String> valueMapper, Long param) {
        if (param == null) {
            return Collections.emptyMap();
        }

        LambdaQueryWrapper<ModelAssessTaskManualDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper = condition.apply(queryWrapper, param);
//        queryWrapper.in(ModelAssessTaskManualDO::getStatus, 2, 3);

        List<ModelAssessTaskManualDO> modelAssessTaskManualDOS = modelAssessTaskManualMapper.selectList(queryWrapper);
        if (CollectionUtils.isEmpty(modelAssessTaskManualDOS)) {
            return Collections.emptyMap();
        }

        return modelAssessTaskManualDOS.stream().collect(Collectors.toMap(keyMapper, valueMapper));
    }


    /**
     * 根据基础模型 id 获取人工评估
     *
     * @param baseModelId 基础模型 id
     * @return 人工评估的 Map 集合
     */
    @Override
    public Map<Long, String> getModelAssessmentTaskByModelId(Long baseModelId) {
        return getModelAssessmentTaskMap((wrapper, id) -> wrapper.eq(ModelAssessTaskManualDO::getModelService, id).eq(ModelAssessTaskManualDO::getModelType, 1),
                ModelAssessTaskManualDO::getId, ModelAssessTaskManualDO::getTaskName, baseModelId);
    }

    /**
     * 根据模型服务 id 获取人工评估
     *
     * @param modelServiceId 模型服务 id
     * @return 人工评估的 Map 集合
     */
    @Override
    public Map<Long, String> getModelAssessmentTaskByModelServiceId(Long modelServiceId) {
        return getModelAssessmentTaskMap((wrapper, id) -> wrapper.eq(ModelAssessTaskManualDO::getModelService, id).eq(ModelAssessTaskManualDO::getModelType, 0),
                ModelAssessTaskManualDO::getId, ModelAssessTaskManualDO::getTaskName, modelServiceId);
    }

    /**
     * 根据 数据集id 获取 人工评估
     *
     * @param datasetId 数据集id
     * @return 人工评估
     */
    @Override
    public Map<Long, String> getModelAssessmentTaskByDatasetId (Long datasetId) {
        return getModelAssessmentTaskMap((wrapper, id) -> wrapper.eq(ModelAssessTaskManualDO::getDataset, datasetId),
                ModelAssessTaskManualDO::getId, ModelAssessTaskManualDO::getTaskName, datasetId);
    }

    @Override
    public List<ManualEvaluationReportRespVO> gitViewTheReportDetails (Long id) {
        List<ManualEvaluationReportRespVO> manualEvaluationReportRespVOS = new ArrayList<>();
        LambdaQueryWrapper<ManualModelAnswerDO> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ManualModelAnswerDO::getManalTaskId, id)
                .eq(ManualModelAnswerDO::getStatus, 2)
                .isNotNull(ManualModelAnswerDO::getReqRespVos);
        List<ManualModelAnswerDO> manualModelAnswerDOS = manualModelAnswerMapper.selectList(wrapper);
        // 获取维度
        List<String> collect = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(manualModelAnswerDOS)) {
            List<HashMap<String, Object>> reqRespVos = manualModelAnswerDOS.get(0).getReqRespVos();
            if (CollectionUtils.isNotEmpty(reqRespVos)) {
                List<LabelInformationVO> bean = BeanUtils.toBean(reqRespVos, LabelInformationVO.class);
                collect = bean.stream().map(LabelInformationVO::getLabel).collect(Collectors.toList());
            }

        }
        List<LabelInformationVO> labelInformationVOS = new ArrayList<>();
        for (ManualModelAnswerDO manualModelAnswerDO : manualModelAnswerDOS) {
            List<LabelInformationVO> bean = BeanUtils.toBean(manualModelAnswerDO.getReqRespVos(), LabelInformationVO.class);
            labelInformationVOS.addAll(bean);
        }
        if (CollectionUtils.isNotEmpty(labelInformationVOS)) {
            // 计算平均分
            double average = labelInformationVOS.stream().mapToDouble(LabelInformationVO::getScore).average().getAsDouble();
            double score = average * 50.0;
            ManualEvaluationReportRespVO reportRespVO = new ManualEvaluationReportRespVO();
            double bd = new BigDecimal(average).setScale(2, RoundingMode.HALF_UP).doubleValue();
            double v = new BigDecimal(score).setScale(2, RoundingMode.HALF_UP).doubleValue();
            reportRespVO.setDimension("平均分");
            reportRespVO.setScore(bd);
            reportRespVO.setPercent(v);
            manualEvaluationReportRespVOS.add(reportRespVO);

            if (CollectionUtils.isNotEmpty(collect)){
                boolean allBlank = true;
                for (String collect1 : collect) {
                    if (StringUtils.isNotBlank(collect1)) {
                        allBlank = false;
                        break;
                    }
                }
                if (allBlank) {
                    return manualEvaluationReportRespVOS;
                }

                for (String collect1 : collect) {
                    if (StringUtils.isBlank(collect1)){
                        continue;
                    }
                    List<LabelInformationVO> voList = labelInformationVOS.stream().filter(manualModelAnswerDO -> manualModelAnswerDO.getLabel().equals(collect1)).collect(Collectors.toList());
                    ManualEvaluationReportRespVO reportRespVO1 = new ManualEvaluationReportRespVO();
                    reportRespVO1.setDimension(collect1);
                    double average1 = voList.stream().mapToDouble(LabelInformationVO::getScore).average().getAsDouble();
                    reportRespVO1.setScore(new BigDecimal(average1).setScale(2, RoundingMode.HALF_UP).doubleValue());
                    reportRespVO1.setPercent(new BigDecimal(average1 * 50.0).setScale(2, RoundingMode.HALF_UP).doubleValue());
                    long count = voList.stream().filter(manualModelAnswerDO -> manualModelAnswerDO.getScore() == 0).count();
                    double zeroPercent = (double) count / voList.size() * 100.0;
                    reportRespVO1.setZeroPercent(new BigDecimal(zeroPercent).setScale(2, RoundingMode.HALF_UP).doubleValue());
                    long count1 = voList.stream().filter(manualModelAnswerDO -> manualModelAnswerDO.getScore() == 1).count();
                    long count2 = voList.stream().filter(manualModelAnswerDO -> manualModelAnswerDO.getScore() == 2).count();
                    double onePercent = (double) count1 / voList.size() * 100.0;
                    reportRespVO1.setOnePercent(new BigDecimal(onePercent).setScale(2, RoundingMode.HALF_UP).doubleValue());
                    double twoPercent = (double) count2 / voList.size() * 100.0;
                    reportRespVO1.setTwoPercent(new BigDecimal(twoPercent).setScale(2, RoundingMode.HALF_UP).doubleValue());
                    manualEvaluationReportRespVOS.add(reportRespVO1);
                }
            }

        }
        return manualEvaluationReportRespVOS;
    }

}
