package com.taais.biz.service.impl;

import java.io.File;
import java.io.IOException;
import java.util.*;

import cn.hutool.core.util.ObjectUtil;
import com.esotericsoftware.minlog.Log;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import com.taais.biz.constant.BizConstant;
import com.taais.biz.domain.TargetIdentificationSubtask;
import com.taais.biz.domain.bo.DataAmplificationTaskBo;
import com.taais.biz.domain.bo.TargetIdentificationSubtaskBo;
import com.taais.biz.domain.bo.TargetIdentificationSubtaskDetailsBo;
import com.taais.biz.domain.dto.DataAmplifyDto;
import com.taais.biz.domain.dto.task.CreateTargetIdentificationTaskDto;
import com.taais.biz.domain.dto.task.TaskDto;
import com.taais.biz.domain.vo.AlgorithmModelVo;
import com.taais.biz.domain.vo.AlgorithmTaskConfigurationVo;
import com.taais.biz.domain.vo.DataVo;
import com.taais.biz.service.*;
import com.taais.common.core.config.TaaisConfig;
import com.taais.common.core.utils.MapstructUtils;
import com.taais.common.core.utils.StringUtils;
import com.taais.common.core.utils.uuid.UUID;
import com.taais.common.json.utils.JsonUtils;
import com.taais.common.orm.core.page.PageQuery;
import com.taais.common.core.core.page.PageResult;
import com.taais.common.orm.core.service.impl.BaseServiceImpl;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.taais.biz.mapper.TargetIdentificationTaskMapper;
import com.taais.biz.domain.TargetIdentificationTask;
import com.taais.biz.domain.bo.TargetIdentificationTaskBo;
import com.taais.biz.domain.vo.TargetIdentificationTaskVo;

import javax.security.auth.Subject;

import static com.taais.biz.domain.table.TargetIdentificationTaskTableDef.TARGET_IDENTIFICATION_TASK;

/**
 * 目标识别任务Service业务层处理
 *
 * @author km
 * 2024-08-17
 */
@Service
@Slf4j
public class TargetIdentificationTaskServiceImpl extends BaseServiceImpl<TargetIdentificationTaskMapper, TargetIdentificationTask> implements ITargetIdentificationTaskService {
    @Resource
    private TargetIdentificationTaskMapper targetIdentificationTaskMapper;

    @Resource
    private TargetIdentificationSubtaskServiceImpl subtaskService;
    @Resource
    private TargetIdentificationSubtaskDetailsServiceImpl subtaskDetailsService;

    @Resource
    private IAlgorithmTaskConfigurationService algorithmTaskConfigurationService;

    @Resource
    private DataServiceImpl dataService;

    @Resource
    private IDataAmplificationTaskService dataAmplificationTaskService;

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

    private QueryWrapper buildQueryWrapper(TargetIdentificationTaskBo targetIdentificationTaskBo) {
        QueryWrapper queryWrapper = super.buildBaseQueryWrapper();
        queryWrapper.and(TARGET_IDENTIFICATION_TASK.NAME.like
        (targetIdentificationTaskBo.getName()));
        queryWrapper.and(TARGET_IDENTIFICATION_TASK.STATUS.eq
        (targetIdentificationTaskBo.getStatus()));
        queryWrapper.and(TARGET_IDENTIFICATION_TASK.REMARKS.eq
        (targetIdentificationTaskBo.getRemarks()));
        queryWrapper.orderBy(TARGET_IDENTIFICATION_TASK.CREATE_TIME.desc());
        return queryWrapper;
    }

    /**
     * 查询目标识别任务
     *
     * @param id 目标识别任务主键
     * @return 目标识别任务
     */
    @Override
    public TargetIdentificationTaskVo selectById(Long id) {
            return this.getOneAs(query().where(TARGET_IDENTIFICATION_TASK.ID.eq(id)), TargetIdentificationTaskVo.class);

    }

    /**
     * 查询目标识别任务列表
     *
     * @param targetIdentificationTaskBo 目标识别任务Bo
     * @return 目标识别任务集合
     */
    @Override
    public List<TargetIdentificationTaskVo> selectList(TargetIdentificationTaskBo targetIdentificationTaskBo) {
        QueryWrapper queryWrapper = buildQueryWrapper(targetIdentificationTaskBo);
            return this.listAs(queryWrapper, TargetIdentificationTaskVo.class);
    }

    /**
     * 分页查询目标识别任务列表
     *
     * @param targetIdentificationTaskBo 目标识别任务Bo
     * @return 分页目标识别任务集合
     */
    @Override
    public PageResult<TargetIdentificationTaskVo> selectPage(TargetIdentificationTaskBo targetIdentificationTaskBo) {
        QueryWrapper queryWrapper = buildQueryWrapper(targetIdentificationTaskBo);
            Page<TargetIdentificationTaskVo> page = this.pageAs(PageQuery.build(), queryWrapper, TargetIdentificationTaskVo.class);
        return PageResult.build(page);
    }

    /**
     * 新增目标识别任务
     *
     * @param targetIdentificationTaskBo 目标识别任务Bo
     * @return 结果:true 操作成功，false 操作失败
     */
    @Override
    public boolean insert(TargetIdentificationTaskBo targetIdentificationTaskBo) {
    TargetIdentificationTask targetIdentificationTask =MapstructUtils.convert(targetIdentificationTaskBo, TargetIdentificationTask. class);
        System.out.println("Insert task: " + targetIdentificationTask);

        boolean result = this.save(targetIdentificationTask);//使用全局配置的雪花算法主键生成器生成ID值

        System.out.println("Insert task: " + targetIdentificationTask);

        return result;
    }

    public TargetIdentificationTask insertTask(TargetIdentificationTaskBo targetIdentificationTaskBo) {
        TargetIdentificationTask targetIdentificationTask =MapstructUtils.convert(targetIdentificationTaskBo, TargetIdentificationTask. class);

        boolean result = this.save(targetIdentificationTask);//使用全局配置的雪花算法主键生成器生成ID值

        System.out.println("Insert task: " + targetIdentificationTask);

        return targetIdentificationTask;
    }

    /**
     * 修改目标识别任务
     *
     * @param targetIdentificationTaskBo 目标识别任务Bo
     * @return 结果:true 更新成功，false 更新失败
     */
    @Override
    public boolean update(TargetIdentificationTaskBo targetIdentificationTaskBo) {
        TargetIdentificationTask targetIdentificationTask =MapstructUtils.convert(targetIdentificationTaskBo, TargetIdentificationTask. class);
        if (ObjectUtil.isNotNull(targetIdentificationTask) && ObjectUtil.isNotNull(targetIdentificationTask.getId())){
            boolean updated = this.updateById(targetIdentificationTask);
                return updated;
        }
        return false;
    }

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

    @Override
    public void taskRun() {
        Long id = subtaskDetailsService.getAvailableTask();
        if (id != null) {
            subtaskService.executeOneTask(id);
        }
        //TargetIdentificationTask task = targetIdentificationTaskMapper.getAvailableTask();
        //if (task == null) {
        //    return;
        //}
        //subtaskService.taskRun(task);
    }

    @Override
    @Transactional
    public String createTask(CreateTargetIdentificationTaskDto taskDto) {
        String errorMsg = "";
        // 检查数据是否有问题
        errorMsg = checkTask(taskDto);

        if (StringUtils.isNotEmpty(errorMsg)){
            return errorMsg;
        }
        // 创建任务
        TargetIdentificationTaskBo taskBo = new TargetIdentificationTaskBo();
        taskBo.setName(taskDto.getTaskName());
        taskBo.setStatus(BizConstant.TASK_STATUS_PENDING);
        TargetIdentificationTask task = this.insertTask(taskBo);
        taskBo.setId(task.getId());
        System.out.println("taskBo is: " + taskBo);

        Map<String, String> records = null;
        // 创建训练子任务
        if(taskDto.getTaskItemList().contains("1")){
            records = createTrainTask(taskBo.getId(),taskDto);
        }
        // 创建测试子任务
        if (taskDto.getTaskItemList().contains("2")){
            createTestTask(taskBo.getId(),taskDto, records);
        }

        // 创建扩增子任务
        if (taskDto.getHasTrainAugmentation()) {
            if (taskDto.getTrainAugmentationParams() != null && StringUtils.isNotEmpty(taskDto.getTrainAugmentationParams().getOtherParams())) {
                createDataAmplifyTask(taskBo.getId(), taskDto);
            }
        }
        return null;
    }

    private Map<String, String> createTrainTask(Long taskId, CreateTargetIdentificationTaskDto taskDto) {
        List<TaskDto> algTaskList = taskDto.getAlgTaskList();
        List<String> trainBatchNumList = taskDto.getTrainBatchNumList();
        if (trainBatchNumList.isEmpty()) {
            return null;
        }
        TargetIdentificationSubtaskBo subtask = new TargetIdentificationSubtaskBo();
        subtask.setName("训练");
        subtask.setStatus(BizConstant.TASK_STATUS_PENDING);
        subtask.setTaskId(taskId);
        subtask.setRemarks("DEFAULT_REMARK");

        TargetIdentificationSubtask savedTask = subtaskService.insertSubtask(subtask);
        subtask.setId(savedTask.getId());
        System.out.println("subtask is: " + subtask);

        Map<String, String> records = new HashMap<>();

        for (TaskDto algTask : algTaskList) {
            // 通过算法id 获取算法配置
            Long algorithmId = algTask.getAlgorithmId();
            AlgorithmTaskConfigurationVo algorithmModelVo = algorithmTaskConfigurationService.selectById(algorithmId);

            String algUrl = algorithmModelVo.getTrainUrl();
            String algName = algorithmModelVo.getName();

            List<String> params = List.of(algTask.getParams().split(";;;"));

            for (int i = 0; i < trainBatchNumList.size(); i++) {
                String batchNum = trainBatchNumList.get(i);
                TargetIdentificationSubtaskDetailsBo subtaskDetail = new TargetIdentificationSubtaskDetailsBo();
                // 通过算法id获取算法配置
                subtaskDetail.setSubtaskId(subtask.getId());
                subtaskDetail.setName(algName + "_训练");
                subtaskDetail.setStatus(BizConstant.TASK_STATUS_PENDING);
                subtaskDetail.setAlgorithmId(algTask.getAlgorithmId());
                subtaskDetail.setType(algUrl);
                subtaskDetail.setDataBatchNums(batchNum);
                subtaskDetail.setParameters(params.get(0));
                String subtaskPath = "/" + UUID.randomUUID().toString().replace("-", "_");
                records.put(algName, subtaskPath);
                subtaskDetail.setPreprocessPath(subtaskPath);
                subtaskDetail.setResultPath(subtaskPath + "/result");
                subtaskDetail.setIndex((long) i);
                copyFilesToPath(batchNum, subtaskPath);

                subtaskDetailsService.insert(subtaskDetail);
            }
        }
        return records;
    }

    public static final String PATH_PREFIX = TaaisConfig.getProfile() + "/task";
    public static final String WORK_DIR = TaaisConfig.getProfile();
    //public static final String PATH_PREFIX = "ObjectDetection_Web/task";
    //public static final String WORK_DIR = "ObjectDetection_Web";
    /**
     * 移动文件到对应文件夹
     * @param batch
     * @param path
     */
    private void copyFilesToPath(String batch, String path) {
        String[] batches = batch.split(",");

        File dir = new File(PATH_PREFIX + path);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        dir = new File(PATH_PREFIX + path + "/images");
        if (!dir.exists()) {
            dir.mkdirs();
        }
        dir = new File(PATH_PREFIX + path + "/labels");
        if (!dir.exists()) {
            dir.mkdirs();
        }
        dir = new File(PATH_PREFIX + path + "/result");
        if (!dir.exists()) {
            dir.mkdirs();
        }

        System.out.println(dir.getAbsolutePath());

        for (String batchNum : batches) {
            List<DataVo> dataVoList = dataService.getDataByBatchNum(batchNum);
            for (DataVo dataVo : dataVoList) {
                String[] strings = dataVo.getUrl().split("/profile");
                String relativePath = WORK_DIR + strings[strings.length - 1];
                relativePath = relativePath.replace("\\", "/").replace("//", "/");
                File file = new File(relativePath);
                System.out.println(file.getAbsolutePath());
                if (file.exists()) {
                    try {
                        File dist = new File(PATH_PREFIX + path + "/images/" + file.getName());
                        FileUtils.copyFile(file, dist);
                        System.out.println("file dist: " + dist.getAbsolutePath());
                    } catch (IOException e) {
                        Log.debug("bug found");
                        continue;
                    }
                }
                if (dataVo.getLabelurl() != null) {
                    strings = dataVo.getLabelurl().split("/profile");
                    relativePath = WORK_DIR + strings[strings.length - 1];
                    relativePath = relativePath.replace("\\", "/").replace("//", "/");
                    file = new File(relativePath);
                    if (file.exists()) {
                        try {
                            File dist = new File(PATH_PREFIX + path + "/labels/" + file.getName());
                            FileUtils.copyFile(file, dist);
                            System.out.println("file dist: " + dist.getAbsolutePath());
                        } catch (IOException e) {
                            Log.debug("bug found");
                            continue;
                        }
                    }
                }
            }
        }
    }

    /**
     * 移动文件到对应文件夹
     * @param batch
     * @param path
     */
    private void copyFilesToPath(String batch, String path, boolean moveLabelFile) {
        String[] batches = batch.split(",");

        File dir = new File(PATH_PREFIX + path);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        dir = new File(PATH_PREFIX + path + "/images");
        if (!dir.exists()) {
            dir.mkdirs();
        }
        dir = new File(PATH_PREFIX + path + "/labels");
        if (!dir.exists()) {
            dir.mkdirs();
        }
        dir = new File(PATH_PREFIX + path + "/result");
        if (!dir.exists()) {
            dir.mkdirs();
        }

        for (String batchNum : batches) {
            List<DataVo> dataVoList = dataService.getDataByBatchNum(batchNum);
            for (DataVo dataVo : dataVoList) {
                log.info("dataVo: " + dataVo.getUrl());
                String[] strings = null;
                String relativePath = null;
                File file = null;

                if (dataVo.getLabelurl() != null) {
                    strings = dataVo.getLabelurl().split("/profile");
                    relativePath = WORK_DIR + strings[strings.length - 1];
                    relativePath = relativePath.replace("\\", "/").replace("//", "/");
                    log.info("relativePath: " + relativePath);
                    file = new File(relativePath);
                    if (file.exists()) {
                        if (!moveLabelFile) {
                            continue;
                        }
                        try {
                            File dist = new File(PATH_PREFIX + path + "/labels/" + file.getName());
                            FileUtils.copyFile(file, dist);
                            System.out.println("file dist: " + dist.getAbsolutePath());
                        } catch (IOException e) {
                            log.error("e happens: {}", e.getMessage());
                            continue;
                        }
                    }
                } else if (moveLabelFile) {
                    continue;
                }

                strings = dataVo.getUrl().split("/profile");
                relativePath = WORK_DIR + strings[strings.length - 1];
                relativePath = relativePath.replace("\\", "/").replace("//", "/");
                log.info("relativePath url: " + relativePath);
                file = new File(relativePath);
                if (file.exists()) {
                    try {
                        File dist = new File(PATH_PREFIX + path + "/images/" + file.getName());
                        log.info("get filename: {}", file.getName());
                        FileUtils.copyFile(file, dist);
                        System.out.println("file dist: " + dist.getAbsolutePath());
                    } catch (IOException e) {
                        Log.debug("bug found");
                        continue;
                    }
                }
            }
        }
    }

    /**
     * 检查是否有验证集
     * @param batch
     */
    private boolean hasValidationSet(String batch) {
        String[] batches = batch.split(",");

        for (String batchNum : batches) {
            List<DataVo> dataVoList = dataService.getDataByBatchNum(batchNum);
            for (DataVo dataVo : dataVoList) {
                String[] strings = null;

                if (StringUtils.isNotEmpty(dataVo.getLabelurl())) {
                    strings = dataVo.getLabelurl().split("/profile");
                    String relativePath = WORK_DIR + strings[strings.length - 1];
                    relativePath = relativePath.replace("\\", "/").replace("//", "/");
                    File file = new File(relativePath);
                    if (file.exists()) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    /**
     * 检查是否有test集
     * @param batch
     */
    private boolean hasTestSet(String batch) {
        String[] batches = batch.split(",");

        for (String batchNum : batches) {
            List<DataVo> dataVoList = dataService.getDataByBatchNum(batchNum);
            for (DataVo dataVo : dataVoList) {
                if (StringUtils.isEmpty(dataVo.getLabelurl())) {
                    return true;
                }
            }
        }
        return false;
    }

    private void createTestTask(Long taskId, CreateTargetIdentificationTaskDto taskDto, Map<String, String> records) {
        List<TaskDto> algTaskList = taskDto.getAlgTaskList();
        List<String> testBatchNumList = taskDto.getTestBatchNumList();
        if (testBatchNumList.isEmpty()) {
            return;
        }
        TargetIdentificationSubtaskBo subtask = new TargetIdentificationSubtaskBo();
        subtask.setName("测试");
        subtask.setStatus(BizConstant.TASK_STATUS_PENDING);
        subtask.setTaskId(taskId);
        subtask.setRemarks("DEFAULT_REMARK");

        TargetIdentificationSubtask savedTask = subtaskService.insertSubtask(subtask);
        subtask.setId(savedTask.getId());
        System.out.println("subtask is: " + subtask);

        for (TaskDto algTask : algTaskList) {
            // 通过算法id 获取算法配置
            Long algorithmId = algTask.getAlgorithmId();
            AlgorithmTaskConfigurationVo algorithmModelVo = algorithmTaskConfigurationService.selectById(algorithmId);

            String algUrl = algorithmModelVo.getTestUrl();
            String algName = algorithmModelVo.getName();
            List<String> params = List.of(algTask.getParams().split(";;;"));

            for (int i = 0; i < testBatchNumList.size(); i++) {
                String batchNum = testBatchNumList.get(i);
                log.info("'batchNum' is: {}", batchNum);
                TargetIdentificationSubtaskDetailsBo subtaskDetail = new TargetIdentificationSubtaskDetailsBo();
                // 通过算法id获取算法配置
                subtaskDetail.setSubtaskId(subtask.getId());
                subtaskDetail.setName(algName);
                subtaskDetail.setStatus(BizConstant.TASK_STATUS_PENDING);
                subtaskDetail.setAlgorithmId(algTask.getAlgorithmId());
                subtaskDetail.setType(algUrl);
                subtaskDetail.setDataBatchNums(batchNum);
                subtaskDetail.setParameters(params.get(2));
                String subtaskPath = "/" + UUID.randomUUID().toString().replace("-", "_");
                subtaskDetail.setPreprocessPath(subtaskPath);
                subtaskDetail.setResultPath(subtaskPath + "/result");
                subtaskDetail.setIndex((long) i);

                if (hasValidationSet(batchNum)) {
                    subtaskDetail.setName(algName + "_验证");
                    subtaskDetail.setParameters(params.get(1));
                    copyFilesToPath(batchNum, subtaskPath, true);
                    subtaskDetail.setType(algorithmModelVo.getVerifyUrl() + ";;;" + (records != null ? records.get(algName) : ""));
                    subtaskDetailsService.insert(subtaskDetail);
                    // reset to '测试'
                    subtaskPath = "/" + UUID.randomUUID().toString().replace("-", "_");
                    subtaskDetail.setPreprocessPath(subtaskPath);
                    subtaskDetail.setResultPath(subtaskPath + "/result");
                    subtaskDetail.setParameters(params.get(2));
                }

                subtaskDetail.setName(algName + "_测试");
                subtaskDetail.setPreprocessPath(subtaskDetail.getPreprocessPath() + "/images");
                subtaskDetail.setType(algorithmModelVo.getTestUrl() + ";;;" + (records != null ? records.get(algName) : ""));
                if (hasTestSet(batchNum)) {
                    copyFilesToPath(batchNum, subtaskPath, false);
                    subtaskDetailsService.insert(subtaskDetail);
                }
            }
        }
    }

    private void createDataAmplifyTask(Long taskId, CreateTargetIdentificationTaskDto taskDto) {
        List<String> trainBatchNumList = taskDto.getTrainBatchNumList();
        for (String batchNumber : trainBatchNumList) {
            TargetIdentificationSubtaskBo subtask = new TargetIdentificationSubtaskBo();
            subtask.setName("数据扩增");
            subtask.setStatus(BizConstant.TASK_STATUS_PENDING);
            subtask.setTaskId(taskId);
            subtask.setRemarks("DEFAULT_REMARK");
            TargetIdentificationSubtask insertSubtask = subtaskService.insertSubtask(subtask);

            DataAmplifyDto amplifyDto = taskDto.getTrainAugmentationParams();
            DataAmplificationTaskBo dataAmplificationTaskBo = new DataAmplificationTaskBo();
            dataAmplificationTaskBo.setSubTaskId(insertSubtask.getId());
            dataAmplificationTaskBo.setName(amplifyDto.getTaskName());
            dataAmplificationTaskBo.setStatus(BizConstant.TASK_STATUS_PENDING);
            dataAmplificationTaskBo.setDataBatchNums(batchNumber);
            dataAmplificationTaskBo.setAugmentationType(amplifyDto.getAugmentationType());
            dataAmplificationTaskBo.setParameters(JsonUtils.toJsonString(amplifyDto.getOtherParams()));
            dataAmplificationTaskBo.setDelFlag(0);
            dataAmplificationTaskService.insert(dataAmplificationTaskBo);
        }
    }


    // todo 检查数据是否有问题
    private String checkTask(CreateTargetIdentificationTaskDto taskDto) {
        return null;
    }

}
