package com.taais.biz.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import com.taais.biz.constant.BizConstant;
import com.taais.biz.domain.*;
import com.taais.biz.domain.bo.AlgorithmSubtaskBo;
import com.taais.biz.domain.dto.AlgorithmConfigParamDto;
import com.taais.biz.domain.dto.AlgorithmRequestDto;
import com.taais.biz.domain.vo.AlgorithmBizProcessVo;
import com.taais.biz.domain.vo.AlgorithmDataProcessVo;
import com.taais.biz.domain.vo.AlgorithmSubtaskVo;
import com.taais.biz.mapper.AlgorithmSubtaskMapper;
import com.taais.biz.service.*;
import com.taais.common.core.constant.Constants;
import com.taais.common.core.core.page.PageResult;
import com.taais.common.core.utils.MapstructUtils;
import com.taais.common.core.utils.StringUtils;
import com.taais.common.orm.core.page.PageQuery;
import com.taais.common.orm.core.service.impl.BaseServiceImpl;
import com.taais.system.domain.vo.SysOssVo;
import com.taais.system.service.ISysOssService;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.math.NumberUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

import java.lang.reflect.Type;
import java.util.*;

import static com.taais.biz.domain.table.AlgorithmSubtaskTableDef.ALGORITHM_SUBTASK;

/**
 * 算法子任务Service业务层处理
 *
 * @author Allen
 * 2024-06-11
 */
@Service
public class AlgorithmSubtaskServiceImpl extends BaseServiceImpl<AlgorithmSubtaskMapper, AlgorithmSubtask> implements IAlgorithmSubtaskService {

    private static final Logger log = LoggerFactory.getLogger(AlgorithmSubtaskServiceImpl.class);
    @Resource
    private WebClient.Builder webClientBuilder;
    @Resource
    private AlgorithmSubtaskMapper algorithmSubtaskMapper;
    @Resource
    private IAlgorithmDataSetService dataSetService;
    @Resource
    private IAlgorithmDataProcessService dataProcessService;
    @Resource
    private IAlgorithmBizProcessService bizProcessService;
    @Resource
    private IAlgorithmConfigService algorithmConfigService;
    @Resource
    private IAlgorithmModelService modelService;
    @Resource
    private ISysOssService ossService;

    @Override
    public QueryWrapper query() {
        return super.query().from(ALGORITHM_SUBTASK);
    }

    private QueryWrapper buildQueryWrapper(AlgorithmSubtaskBo algorithmSubtaskBo) {
        QueryWrapper queryWrapper = super.buildBaseQueryWrapper();
        queryWrapper.and(ALGORITHM_SUBTASK.NAME.like
        (algorithmSubtaskBo.getName()));
        queryWrapper.and(ALGORITHM_SUBTASK.STATUS.eq
        (algorithmSubtaskBo.getStatus()));
        queryWrapper.and(ALGORITHM_SUBTASK.TYPE.eq
        (algorithmSubtaskBo.getType()));
        queryWrapper.and(ALGORITHM_SUBTASK.START_TIME.eq
        (algorithmSubtaskBo.getStartTime()));
        queryWrapper.and(ALGORITHM_SUBTASK.END_TIME.eq
        (algorithmSubtaskBo.getEndTime()));
        queryWrapper.and(ALGORITHM_SUBTASK.TASK_ID.eq
        (algorithmSubtaskBo.getTaskId()));
        return queryWrapper;
    }

    /**
     * 查询算法子任务
     *
     * @param id 算法子任务主键
     * @return 算法子任务
     */
    @Override
    public AlgorithmSubtaskVo selectById(Long id) {
            return this.getOneAs(query().where(ALGORITHM_SUBTASK.ID.eq(id)), AlgorithmSubtaskVo.class);

    }

    /**
     * 查询算法子任务列表
     *
     * @param algorithmSubtaskBo 算法子任务Bo
     * @return 算法子任务集合
     */
    @Override
    public List<AlgorithmSubtaskVo> selectList(AlgorithmSubtaskBo algorithmSubtaskBo) {
        QueryWrapper queryWrapper = buildQueryWrapper(algorithmSubtaskBo);
            return this.listAs(queryWrapper, AlgorithmSubtaskVo.class);
    }

    /**
     * 分页查询算法子任务列表
     *
     * @param algorithmSubtaskBo 算法子任务Bo
     * @return 分页算法子任务集合
     */
    @Override
    public PageResult<AlgorithmSubtaskVo> selectPage(AlgorithmSubtaskBo algorithmSubtaskBo) {
        QueryWrapper queryWrapper = buildQueryWrapper(algorithmSubtaskBo);
            Page<AlgorithmSubtaskVo> page = this.pageAs(PageQuery.build(), queryWrapper, AlgorithmSubtaskVo.class);
        return PageResult.build(page);
    }

    /**
     * 新增算法子任务
     *
     * @param algorithmSubtaskBo 算法子任务Bo
     * @return 结果:true 操作成功，false 操作失败
     */
    @Override
    public boolean insert(AlgorithmSubtaskBo algorithmSubtaskBo) {
    AlgorithmSubtask algorithmSubtask =MapstructUtils.convert(algorithmSubtaskBo, AlgorithmSubtask. class);
        boolean save = this.save(algorithmSubtask);
        algorithmSubtaskBo.setId(algorithmSubtask.getId());
        algorithmSubtask.setVersion(algorithmSubtask.getVersion());
        return save;//使用全局配置的雪花算法主键生成器生成ID值
    }

    /**
     * 修改算法子任务
     *
     * @param algorithmSubtaskBo 算法子任务Bo
     * @return 结果:true 更新成功，false 更新失败
     */
    @Override
    public boolean update(AlgorithmSubtaskBo algorithmSubtaskBo) {
        AlgorithmSubtask algorithmSubtask =MapstructUtils.convert(algorithmSubtaskBo, AlgorithmSubtask. class);
        if (ObjectUtil.isNotNull(algorithmSubtask) && ObjectUtil.isNotNull(algorithmSubtask.getId())){
            return this.updateById(algorithmSubtask);
        }
        return false;
    }

    /**
     * 批量删除算法子任务
     *
     * @param ids 需要删除的算法子任务主键集合
     * @return 结果:true 删除成功，false 删除失败
     */
    @Transactional
    @Override
    public boolean deleteByIds(Long[] ids) {
        return this.removeByIds(Arrays.asList(ids));
    }

    @Override
    public void subtaskRun(Long taskId) {
        AlgorithmSubtask firstNeedProcessSubtask = algorithmSubtaskMapper.getFirstNeedProcessSubtask(taskId);
        if (firstNeedProcessSubtask != null) {
            // todo allen 现在的type是1是训练，2是验证，3是测试
//            // 数据增强
//            if ("1".equals(firstNeedProcessSubtask.getType())) {
//                List<AlgorithmDataProcessVo> processVoList =  dataProcessService.getProcessBySubtaskId(firstNeedProcessSubtask.getId());
//                dateSetProcess(processVoList);
//            // 数据扩充
//            } else if ("2".equals(firstNeedProcessSubtask.getType())) {
//                List<AlgorithmDataProcessVo> processVoList =  dataProcessService.getProcessBySubtaskId(firstNeedProcessSubtask.getId());
//                dateSetProcess(processVoList);
//            // 算法调用
//            } else if ("3".equals(firstNeedProcessSubtask.getType())) {
            // error branch
                List<AlgorithmBizProcessVo> bizProcessVoList = bizProcessService.getStatus0ProcessBySubtaskId(firstNeedProcessSubtask.getId());
                bizProcess(bizProcessVoList);
//            } else {
//                log.warn("type is wrong, type: {}", firstNeedProcessSubtask.getType());
//            }
        } else {
            log.error("subtaskRun.getFirstNeedProcessSubtask is null, taskId:{}", taskId);
        }
    }

    @Override
    public List<AlgorithmSubtaskVo> getSubtaskByTaskId(Long taskId) {
        return mapper.getSubtaskByTaskId(taskId);
    }

    private void bizProcess(List<AlgorithmBizProcessVo> bizProcessVoList) {
        Long algorithmId = null;
        Long modelId = null;
        String parameters = null;
        String preprocessPath = null;
        String resultPath = null;
        String url = null;
        StringJoiner errorMsg = null;
        for (AlgorithmBizProcessVo bizProcessVo : bizProcessVoList) {
            url = null;
            errorMsg = new StringJoiner(System.lineSeparator());
            algorithmId = bizProcessVo.getAlgorithmId();
            modelId = bizProcessVo.getModelId();
            parameters = bizProcessVo.getParameters();
            preprocessPath = bizProcessVo.getPreprocessPath();
            resultPath = bizProcessVo.getResultPath();
            AlgorithmConfig config = algorithmConfigService.getById(algorithmId);
            if (config == null) {
                log.error("算法配置未找到！！！algorithmId：{}", algorithmId);
                errorMsg.add("算法配置未找到！！！");
            } else {
                url = config.getAlgorithmAddress();
            }
            AlgorithmModel model = null;
            if (modelId != null) {
                model = modelService.getById(modelId);
            }
            // send http
            AlgorithmRequestDto algorithmRequestDto = new AlgorithmRequestDto();
            algorithmRequestDto.setBizType(BizConstant.TYPE_DATA_BIZ_PROCESS);
            algorithmRequestDto.setBizId(bizProcessVo.getId());
            algorithmRequestDto.setSourcePath(BizConstant.DOCKER_BASE_PATH + preprocessPath);
            algorithmRequestDto.setResultPath(BizConstant.DOCKER_BASE_PATH + resultPath);
            algorithmRequestDto.setLogPath(BizConstant.DOCKER_BASE_PATH + bizProcessVo.getLog());
            Gson gson = new Gson();
            Type listType = new TypeToken<List<AlgorithmConfigParamDto>>() {}.getType();
            if (StringUtils.isNotEmpty(parameters)) {
                List<AlgorithmConfigParamDto> paramDtoList = gson.fromJson(parameters, listType);
                Map<String, Object> otherParams = new HashMap<>(paramDtoList.size());
                if (model == null) {
                    log.error("模型配置未找到！！！modelId：{}", modelId);
                    errorMsg.add("模型配置未找到！！！");
                    // 找到训练的模型地址
                    String trainModelPath = mapper.getTrainModelPath(bizProcessVo.getId());
                    if (StringUtils.isNotEmpty(trainModelPath)){
                        otherParams.put("weight_path", BizConstant.DOCKER_BASE_PATH + trainModelPath + BizConstant.DOCKER_PT_PATH);
                    }
                } else {
                    SysOssVo modelOss = ossService.getById(Long.valueOf(model.getModelAddress()));
                    otherParams.put("pretrained_model", BizConstant.DOCKER_BASE_PATH + StringUtils.substringAfter(modelOss.getFileName(), Constants.RESOURCE_PREFIX));
                }
                for (AlgorithmConfigParamDto algorithmConfigParamDto : paramDtoList) {
                    String value = StringUtils.isNotEmpty(algorithmConfigParamDto.getValue()) ? algorithmConfigParamDto.getValue() : algorithmConfigParamDto.getDefaultValue();
                    if(NumberUtils.isCreatable(value)){
                        otherParams.put(algorithmConfigParamDto.getAgName(), NumberUtils.createNumber(value));
                    } else {
                        otherParams.put(algorithmConfigParamDto.getAgName(), value);
                    }
                }
                algorithmRequestDto.setOtherParams(otherParams);
            }
            String httpResult = null;
            Mono<String> response = null;
            if (StringUtils.isEmpty(url)) {
                errorMsg.add("url是空!!!");
            } else {
                log.info("http post url:{},body:{}", url,algorithmRequestDto);
                WebClient webClient = webClientBuilder.build();
                response = webClient.post()
                    .uri(url)
                    .bodyValue(algorithmRequestDto)
                    .retrieve()
                    .bodyToMono(String.class);
            }
            // process httpResult
            log.info("httpResult:{}", httpResult);
            // update AlgorithmBizProcess
            AlgorithmBizProcess algorithmBizProcess = MapstructUtils.convert(bizProcessVo, AlgorithmBizProcess.class);
            if (errorMsg.length() != 0) {
                algorithmBizProcess.setStatus(BizConstant.TASK_STATUS_FAILED);
                algorithmBizProcess.setRemarks(errorMsg.toString());
            } else {
                algorithmBizProcess.setStatus(BizConstant.TASK_STATUS_PROCESSING);
                algorithmBizProcess.setStartTime(new Date());
            }
            bizProcessService.updateById(algorithmBizProcess);
            if (response != null) {
                httpResult = response.block();
                log.info("bizProcess id: {} http response {}", bizProcessVo.getId(), httpResult);
            }
        }
    }

    private void dateSetProcess(List<AlgorithmDataProcessVo> processVoList) {
        String url;
        String parameters;
        String preprocessPath;
        String resultPath;
        StringJoiner errorMsg;
        for (AlgorithmDataProcessVo processVo : processVoList) {
            errorMsg = new StringJoiner(System.lineSeparator());
            url = null;
            Long algorithmId = processVo.getAlgorithmId();
            parameters = processVo.getParameters();
            preprocessPath = processVo.getPreprocessPath();
            resultPath = processVo.getResultPath();
            AlgorithmConfig config = algorithmConfigService.getById(algorithmId);
            if (config == null) {
                log.error("算法配置未找到！！！algorithmId：{}", algorithmId);
                errorMsg.add("算法配置未找到！！！");
            } else {
                url = config.getAlgorithmAddress();
            }
            // send http
            AlgorithmRequestDto algorithmRequestDto = new AlgorithmRequestDto();
            algorithmRequestDto.setBizType(BizConstant.TYPE_DATA_BIZ_PROCESS);
            algorithmRequestDto.setBizId(processVo.getId());
            algorithmRequestDto.setSourcePath(BizConstant.DOCKER_BASE_PATH + preprocessPath);
            algorithmRequestDto.setResultPath(BizConstant.DOCKER_BASE_PATH + resultPath);
            algorithmRequestDto.setLogPath(processVo.getLog());
            Gson gson = new Gson();
            Type listType = new TypeToken<List<AlgorithmConfigParamDto>>() {}.getType();
            if (StringUtils.isNotEmpty(parameters)) {
                List<AlgorithmConfigParamDto> paramDtoList = gson.fromJson(parameters, listType);
                Map<String, Object> otherParams = new HashMap<>(paramDtoList.size());
                for (AlgorithmConfigParamDto algorithmConfigParamDto : paramDtoList) {
                    String value = StringUtils.isNotEmpty(algorithmConfigParamDto.getValue()) ? algorithmConfigParamDto.getValue() : algorithmConfigParamDto.getDefaultValue();
                    if(NumberUtils.isDigits(value)){
                        otherParams.put(algorithmConfigParamDto.getAgName(), NumberUtils.createFloat(value));
                    } else {
                        otherParams.put(algorithmConfigParamDto.getAgName(), value);
                    }
                }
                algorithmRequestDto.setOtherParams(otherParams);
            }
            String httpResult = null;
            if (StringUtils.isEmpty(url)) {
                errorMsg.add("url是空!!!");
            } else {
//                httpResult = HttpUtils.sendPost(url, gson.toJson(algorithmRequestDto));
                WebClient webClient = webClientBuilder.build();
                Mono<String> response = webClient.post()
                    .uri(url)
                    .bodyValue(algorithmRequestDto)
                    .retrieve()
                    .bodyToMono(String.class);
                httpResult = response.block();
            }
            // process httpResult
            log.info("httpResult:{}", httpResult);

            // update AlgorithmDataProcess
            AlgorithmDataProcess algorithmDataProcess = MapstructUtils.convert(processVo, AlgorithmDataProcess.class);
            if (errorMsg.length() != 0) {
                algorithmDataProcess.setStatus(BizConstant.TASK_STATUS_FAILED);
                algorithmDataProcess.setLog(errorMsg.toString());
            } else {
                algorithmDataProcess.setStatus(BizConstant.TASK_STATUS_PROCESSING);
                algorithmDataProcess.setStartTime(new Date());
            }
            dataProcessService.updateById(algorithmDataProcess);
        }
    }
}
