package com.taais.biz.service.impl;

import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson2.JSON;
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.vo.AlgorithmConfigTrackVo;
import com.taais.biz.domain.vo.AlgorithmModelTrackVo;
import com.taais.biz.domain.vo.StartToInfraredTask;
import com.taais.biz.utils.ZipUtils;
import com.taais.common.core.config.TaaisConfig;
import com.taais.common.core.constant.Constants;
import com.taais.common.core.core.domain.CommonResult;
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.core.core.page.PageResult;
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.taais.biz.mapper.TrackSequenceMapper;
import com.taais.biz.domain.bo.TrackSequenceBo;
import com.taais.biz.domain.vo.TrackSequenceVo;
import com.taais.biz.service.ITrackSequenceService;

import static com.taais.biz.constant.BizConstant.VideoStatus.NOT_START;
import static com.taais.biz.domain.table.TrackSequenceTableDef.TRACK_SEQUENCE;
import static com.taais.biz.service.impl.VideoStableServiceImpl.*;
import static com.taais.biz.service.impl.VideoStableServiceImpl.makeDir;

/**
 * 注视轨迹序列Service业务层处理
 *
 * @author wangkang
 * 2024-09-22
 */
@Service
public class TrackSequenceServiceImpl extends BaseServiceImpl<TrackSequenceMapper, TrackSequence> implements ITrackSequenceService {
    @Value("${server.task_stop_url}")
    private String task_stop_url;

    @Autowired
    private AlgorithmConfigTrackServiceImpl algorithmConfigTrackService;
    @Autowired
    private AlgorithmModelTrackServiceImpl algorithmModelTrackService;

    @Autowired
    private ISysOssService ossService;

    @Resource
    private TrackSequenceMapper trackSequenceMapper;

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

    private QueryWrapper buildQueryWrapper(TrackSequenceBo trackSequenceBo) {
        QueryWrapper queryWrapper = super.buildBaseQueryWrapper();
        queryWrapper.and(TRACK_SEQUENCE.NAME.like
            (trackSequenceBo.getName()));
        queryWrapper.and(TRACK_SEQUENCE.STATUS.eq
            (trackSequenceBo.getStatus()));
        queryWrapper.and(TRACK_SEQUENCE.START_TIME.eq
            (trackSequenceBo.getStartTime()));
        queryWrapper.and(TRACK_SEQUENCE.END_TIME.eq
            (trackSequenceBo.getEndTime()));
        queryWrapper.and(TRACK_SEQUENCE.COST_SECOND.eq
            (trackSequenceBo.getCostSecond()));
        queryWrapper.and(TRACK_SEQUENCE.LOG.eq
            (trackSequenceBo.getLog()));
        queryWrapper.and(TRACK_SEQUENCE.REMARKS.eq
            (trackSequenceBo.getRemarks()));
        queryWrapper.and(TRACK_SEQUENCE.URL.eq
            (trackSequenceBo.getUrl()));
        queryWrapper.and(TRACK_SEQUENCE.INPUT_OSS_ID.eq
            (trackSequenceBo.getInputOssId()));
        queryWrapper.and(TRACK_SEQUENCE.INPUT_PATH.eq
            (trackSequenceBo.getInputPath()));
        queryWrapper.and(TRACK_SEQUENCE.OUTPUT_PATH.eq
            (trackSequenceBo.getOutputPath()));
        queryWrapper.and(TRACK_SEQUENCE.ZIP_FILE_PATH.eq
            (trackSequenceBo.getZipFilePath()));

        return queryWrapper;
    }

    /**
     * 查询注视轨迹序列
     *
     * @param id 注视轨迹序列主键
     * @return 注视轨迹序列
     */
    @Override
    public TrackSequenceVo selectById(Long id) {
        return this.getOneAs(query().where(TRACK_SEQUENCE.ID.eq(id)), TrackSequenceVo.class);

    }

    /**
     * 查询注视轨迹序列列表
     *
     * @param trackSequenceBo 注视轨迹序列Bo
     * @return 注视轨迹序列集合
     */
    @Override
    public List<TrackSequenceVo> selectList(TrackSequenceBo trackSequenceBo) {
        QueryWrapper queryWrapper = buildQueryWrapper(trackSequenceBo);
        return this.listAs(queryWrapper, TrackSequenceVo.class);
    }

    /**
     * 分页查询注视轨迹序列列表
     *
     * @param trackSequenceBo 注视轨迹序列Bo
     * @return 分页注视轨迹序列集合
     */
    @Override
    public PageResult<TrackSequenceVo> selectPage(TrackSequenceBo trackSequenceBo) {
        QueryWrapper queryWrapper = buildQueryWrapper(trackSequenceBo);
        Page<TrackSequenceVo> page = this.pageAs(PageQuery.build(), queryWrapper, TrackSequenceVo.class);
        page.getRecords().forEach(entity -> {
            Long modelId = entity.getAlgorithmModelId();
            AlgorithmModelTrackVo model = algorithmModelTrackService.selectById(modelId);
            if (ObjectUtil.isNotNull(model)) {
                AlgorithmConfigTrackVo config = algorithmConfigTrackService.selectById(model.getAlgorithmId());
                entity.setType(config.getType());
                entity.setSubsystem(config.getSubsystem());
                entity.setAlgorithmName(config.getAlgorithmName());
                entity.setModelName(model.getModelName());
            }
        });
        return PageResult.build(page);
    }

    /**
     * 新增注视轨迹序列
     *
     * @param trackSequenceBo 注视轨迹序列Bo
     * @return 结果:true 操作成功，false 操作失败
     */
    @Override
    public boolean insert(TrackSequenceBo trackSequenceBo) {
        // 检查input_oss_id是否存在
        if (ObjectUtil.isNull(trackSequenceBo.getInputOssId())) {
            return false;
        }

        SysOssVo ossEntity = ossService.getById(trackSequenceBo.getInputOssId());
        if (ObjectUtil.isNull(ossEntity)) {
            return false;
        }

        TrackSequence trackSequence = new TrackSequence();

        trackSequence.setInputOssId(trackSequenceBo.getInputOssId());
        trackSequence.setUrl(ossEntity.getUrl());

        String filePath = ossEntity.getFileName();
        String localPath = TaaisConfig.getProfile();
        String resourcePath = localPath + StringUtils.substringAfter(filePath, Constants.RESOURCE_PREFIX);
        trackSequence.setInputPath(resourcePath);

        String fileName = StringUtils.substringAfterLast(filePath, "/");
        String fileName_without_suffix = removeFileExtension(fileName);

        Path path = Paths.get(resourcePath);
        Path outPath = path.resolveSibling(fileName_without_suffix + "_images" + System.currentTimeMillis());
        trackSequence.setOutputPath(outPath.toString());

        trackSequence.setZipFilePath(path.resolveSibling(fileName_without_suffix + ".zip").toString());

        trackSequence.setName(trackSequenceBo.getName());
        trackSequence.setStatus(NOT_START);
        trackSequence.setRemarks(trackSequenceBo.getRemarks());

        trackSequence.setAlgorithmModelId(trackSequenceBo.getAlgorithmModelId());

        return this.save(trackSequence);// 使用全局配置的雪花算法主键生成器生成ID值
    }

    /**
     * 修改注视轨迹序列
     *
     * @param trackSequenceBo 注视轨迹序列Bo
     * @return 结果:true 更新成功，false 更新失败
     */
    @Override
    public boolean update(TrackSequenceBo trackSequenceBo) {
        TrackSequence trackSequence = MapstructUtils.convert(trackSequenceBo, TrackSequence.class);
        if (ObjectUtil.isNotNull(trackSequence) && ObjectUtil.isNotNull(trackSequence.getId())) {
            boolean updated = this.updateById(trackSequence);
            return updated;
        }
        return false;
    }

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

    @Override
    public CommonResult start(Long id) {
        TrackSequence entity = getById(id);

        SysOssVo inputOssEntity = ossService.getById(entity.getInputOssId());

        String filePath = inputOssEntity.getFileName();
        String localPath = TaaisConfig.getProfile();
        String resourcePath = localPath + StringUtils.substringAfter(filePath, Constants.RESOURCE_PREFIX);

        String fileName = StringUtils.substringAfterLast(filePath, "/");
        String fileName_without_suffix = removeFileExtension(fileName);

        Path path = Paths.get(resourcePath);
        Path inputPath = path.resolveSibling(fileName_without_suffix + BizConstant.UNZIP_SUFFIX);
        Path outputPath = path.resolveSibling(fileName_without_suffix + BizConstant.TRACK_SEQUENCE_SUFFIX);

        makeDir(inputPath.toString());
        makeDir(outputPath.toString());

        File file = new File(resourcePath);
        if (!file.exists()) {
            ZipUtils.unzip(resourcePath, inputPath.toString());
        }

        entity.setInputPath(inputPath.toString());
        entity.setOutputPath(outputPath.toString());

        entity.setStartTime(new Date());

        AlgorithmModelTrack algorithmModelTrack = algorithmModelTrackService.getById(entity.getAlgorithmModelId());
        AlgorithmConfigTrack algorithmConfigTrack = algorithmConfigTrackService.getById(algorithmModelTrack.getAlgorithmId());

        StartToInfraredTask startToInfraredTask = new StartToInfraredTask();
        startToInfraredTask.setBizType(BizConstant.BizType.TRACK_SEQUENCE);
        startToInfraredTask.setBizId(entity.getId());

        startToInfraredTask.setOtherParams(algorithmConfigTrack.getParameterConfig());

        startToInfraredTask.setSource_dir(entity.getInputPath());
        startToInfraredTask.setResult_dir(entity.getOutputPath());

        if (BizConstant.AlgorithmType.REASONING.equals(algorithmConfigTrack.getType())) {
            if(algorithmModelTrack.getModelName().startsWith("masc") || algorithmModelTrack.getModelName().startsWith("MASC")) {
                String modelPath = algorithmModelTrack.getModelAddress() + File.separator + algorithmModelTrack.getModelName().substring(5);
                startToInfraredTask.setModel_path(modelPath);
            }
            else if(algorithmModelTrack.getModelName().startsWith("cat") || algorithmModelTrack.getModelName().startsWith("CAT")) {
                String modelPath = algorithmModelTrack.getModelAddress();
                startToInfraredTask.setModel_path(modelPath);
            }
            else {
                return CommonResult.fail("模型命名失败，请以MASC或CAT开头命名模型");
            }
        }


        HttpResponseEntity responseEntity = sendPostMsg(algorithmConfigTrack.getAlgorithmAddress(), startToInfraredTask);
        if (ObjectUtil.isNotNull(responseEntity) && responseEntity.getStatus() == 200) {
            entity.setStatus(BizConstant.VideoStatus.RUNNING);
            updateById(entity);
            return CommonResult.success("任务开始成功，请等待完成");
        } else {
            entity.setStatus(BizConstant.VideoStatus.FAILED);
            updateById(entity);
            return CommonResult.fail("任务开始失败，请检查！");
        }
    }

    @Override
    public CommonResult stop(Long id) {
        TrackSequence entity = getById(id);

        StartToInfraredTask startToInfraredTask = new StartToInfraredTask();
        startToInfraredTask.setBizType(BizConstant.BizType.TRACK_SEQUENCE);
        startToInfraredTask.setBizId(entity.getId());

        HttpResponseEntity responseEntity = sendPostMsg(task_stop_url, startToInfraredTask);
        if (ObjectUtil.isNotNull(responseEntity) && responseEntity.getStatus() == 200) {
            entity.setStatus(BizConstant.VideoStatus.INTERRUPTED);
            updateById(entity);
            return CommonResult.success("终止任务成功");
        } else {
            return CommonResult.fail("终止任务失败");
        }
    }

    @Override
    public ResponseEntity<org.springframework.core.io.Resource> zipImages(Long id) {
        TrackSequence entity = this.getById(id);
        if (ObjectUtil.isNull(entity)) {
            return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
        }

        String outputPath = "";
        String zipFilePath = "";

        AlgorithmModelTrack algorithmModelTrack = algorithmModelTrackService.getById(entity.getAlgorithmModelId());
        AlgorithmConfigTrack algorithmConfigTrack = algorithmConfigTrackService.getById(algorithmModelTrack.getAlgorithmId());

        if (BizConstant.AlgorithmType.REASONING.equals(algorithmConfigTrack.getType())) {
            outputPath = entity.getOutputPath() + File.separator + "predict";
            zipFilePath = outputPath + ".zip";
        } else if (BizConstant.AlgorithmType.TEST.equals(algorithmConfigTrack.getType())) {
            outputPath = entity.getOutputPath() + File.separator + "evaluate";
            zipFilePath = outputPath + ".zip";
        } else if (BizConstant.AlgorithmType.TRAIN.equals(algorithmConfigTrack.getType())) {
            outputPath = entity.getOutputPath();
            zipFilePath = outputPath + ".zip";
        } else {
            System.out.println("未知算法类型！");
            return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
        }

        File file = new File(zipFilePath);
        if (!file.exists()) {
            try {
                ZipUtils.zipFolderFiles(outputPath, zipFilePath);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }

        if (!file.exists() || !file.isFile()) {
            return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
        }

        org.springframework.core.io.Resource resource = new FileSystemResource(file);
        return ResponseEntity.ok()
            .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + file.getName() + "\"")
            .header(HttpHeaders.CONTENT_TYPE, "application/octet-stream")
            .body(resource);
    }

    @Override
    public CommonResult getResult(TaskTrackResultBo taskTrackResultBo) {
        Long id = taskTrackResultBo.getBizId();
        String status = taskTrackResultBo.getStatus();
        String msg = taskTrackResultBo.getMsg();
        TrackSequence entity = getById(id);
        entity.setLog(msg);
        entity.setStatus("200".equals(status) ? BizConstant.VideoStatus.END : BizConstant.VideoStatus.FAILED);
        entity.setEndTime(new Date());
        try {
            entity.setCostSecond((entity.getEndTime().getTime() - entity.getStartTime().getTime()) / 1000);
        } catch (Exception e) {
            entity.setCostSecond(null);
        }
        updateById(entity);

        AlgorithmModelTrack algorithmModelTrack = algorithmModelTrackService.getById(entity.getAlgorithmModelId());
        algorithmModelTrack.setModelStatus("200".equals(status) ? BizConstant.ModelStatus.END : BizConstant.ModelStatus.FAILED);

        AlgorithmConfigTrack algorithmConfigTrack = algorithmConfigTrackService.getById(algorithmModelTrack.getAlgorithmId());
        String params = algorithmConfigTrack.getParameterConfig();
        HashMap<String, Object> parse = (HashMap<String, Object>) JSON.parse((params));

        if ("200".equals(status) && ObjectUtil.isNull(algorithmModelTrack.getModelAddress())) {
            algorithmModelTrack.setModelAddress(entity.getOutputPath() + File.separator + ((HashMap<String, String>) parse.get("dataset")).get("classes"));

            System.out.println(parse.get("dataset"));
            algorithmModelTrackService.updateById(algorithmModelTrack);
        }
        return CommonResult.success();
    }
}
