/**
 * 20241029 ©WANGKANG 1686617586@qq.com
 */

package com.taais.biz.service.impl;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.DecimalFormat;
import java.util.*;

import cn.hutool.core.util.ObjectUtil;
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.ToInfrared;
import com.taais.biz.domain.bo.ToInfraredBo;
import com.taais.biz.domain.vo.AlgorithmConfigTrackVo;
import com.taais.biz.domain.vo.AlgorithmModelTrackVo;
import com.taais.biz.domain.vo.StartTaskConfig;
import com.taais.biz.domain.vo.ToInfraredVo;
import com.taais.biz.mapper.ToInfraredMapper;
import com.taais.biz.service.IToInfraredService;
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 lombok.extern.log4j.Log4j2;
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 static com.taais.biz.constant.BizConstant.VideoStatus.NOT_START;
import static com.taais.biz.domain.table.ToInfraredTableDef.TO_INFRARED;
import static com.taais.biz.service.impl.DataSeqServiceImpl.calculateImagePath;
import static com.taais.biz.service.impl.DataSeqServiceImpl.isImageFile;
import static com.taais.biz.service.impl.VideoStableServiceImpl.*;

/**
 * 可见光转红外Service业务层处理
 *
 * @author 0
 * 2024-09-20
 */
@Service
@Log4j2
public class ToInfraredServiceImpl extends BaseServiceImpl<ToInfraredMapper, ToInfrared> implements IToInfraredService {
    @Value("${server.port}")
    String port;

    @Value("${taais.profile}")
    String profile;

    @Value("${server.task_stop_url}")
    private String task_stop_url;

    @Autowired
    private ISysOssService ossService;

    @Resource
    private ToInfraredMapper toInfraredMapper;
    @Autowired
    private AlgorithmConfigTrackServiceImpl algorithmConfigTrackService;
    @Autowired
    private AlgorithmModelTrackServiceImpl algorithmModelTrackService;

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

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

        return queryWrapper;
    }

    /**
     * 查询可见光转红外
     *
     * @param id 可见光转红外主键
     * @return 可见光转红外
     */
    @Override
    public ToInfraredVo selectById(Long id) {
        return this.getOneAs(query().where(TO_INFRARED.ID.eq(id)), ToInfraredVo.class);

    }

    /**
     * 查询可见光转红外列表
     *
     * @param toInfraredBo 可见光转红外Bo
     * @return 可见光转红外集合
     */
    @Override
    public List<ToInfraredVo> selectList(ToInfraredBo toInfraredBo) {
        QueryWrapper queryWrapper = buildQueryWrapper(toInfraredBo);
        return this.listAs(queryWrapper, ToInfraredVo.class);
    }

    /**
     * 分页查询可见光转红外列表
     *
     * @param toInfraredBo 可见光转红外Bo
     * @return 分页可见光转红外集合
     */
    @Override
    public PageResult<ToInfraredVo> selectPage(ToInfraredBo toInfraredBo) {
        QueryWrapper queryWrapper = buildQueryWrapper(toInfraredBo);
        Page<ToInfraredVo> page = this.pageAs(PageQuery.build(), queryWrapper, ToInfraredVo.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 toInfraredBo 可见光转红外Bo
     * @return 结果:true 操作成功，false 操作失败
     */
    @Override
    public boolean insert(ToInfraredBo toInfraredBo) {
        // 检查input_oss_id是否存在
        if (ObjectUtil.isNull(toInfraredBo.getInputOssId())) {
            return false;
        }

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

        ToInfrared toInfrared = new ToInfrared();

        toInfrared.setInputOssId(toInfraredBo.getInputOssId());
        toInfrared.setUrl(ossEntity.getUrl());

        String filePath = ossEntity.getFileName();
        String localPath = TaaisConfig.getProfile();
        String resourcePath = localPath + StringUtils.substringAfter(filePath, Constants.RESOURCE_PREFIX);
        toInfrared.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());
        toInfrared.setOutputPath(outPath.toString());

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

        toInfrared.setName(toInfraredBo.getName());
        toInfrared.setStatus(NOT_START);
        toInfrared.setRemarks(toInfraredBo.getRemarks());

        toInfrared.setAlgorithmModelId(toInfraredBo.getAlgorithmModelId());

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

    /**
     * 修改可见光转红外
     *
     * @param toInfraredBo 可见光转红外Bo
     * @return 结果:true 更新成功，false 更新失败
     */
    @Override
    public boolean update(ToInfraredBo toInfraredBo) {
        ToInfrared toInfrared = MapstructUtils.convert(toInfraredBo, ToInfrared.class);
        if (ObjectUtil.isNotNull(toInfrared) && ObjectUtil.isNotNull(toInfrared.getId())) {
            boolean updated = this.updateById(toInfrared);
            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) {
        ToInfrared 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(entity.getId().toString() + BizConstant.TO_INFRARED_SUFFIX);

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

        File file = new File(inputPath.toString());
        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());

        StartTaskConfig startTaskConfig = new StartTaskConfig();
        startTaskConfig.setBizType(BizConstant.BizType.TO_INFRARED);
        startTaskConfig.setBizId(entity.getId());

        startTaskConfig.setOtherParams(algorithmConfigTrack.getParameterConfig());

        startTaskConfig.setSource_dir(entity.getInputPath());
        startTaskConfig.setResult_dir(entity.getOutputPath());
        startTaskConfig.setLog_path(entity.getOutputPath() + File.separator + getLogFileName(entity));

        if (BizConstant.AlgorithmType.REASONING.equals(algorithmConfigTrack.getType())) {
            startTaskConfig.setModel_path(algorithmModelTrack.getModelAddress());
            startTaskConfig.setSource_dir(entity.getInputPath() + File.separator + BizConstant.PREDICT_PATH);
            startTaskConfig.setResult_dir(entity.getOutputPath() + File.separator + BizConstant.PREDICT_PATH);
        }


        HttpResponseEntity responseEntity = sendPostMsg(algorithmConfigTrack.getAlgorithmAddress(), startTaskConfig);
        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) {
        ToInfrared toInfrared = getById(id);

        StartTaskConfig startTaskConfig = new StartTaskConfig();
        startTaskConfig.setBizType(BizConstant.BizType.TO_INFRARED);
        startTaskConfig.setBizId(toInfrared.getId());

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

    @Override
    public CommonResult getResult(TaskTrackResultBo taskTrackResultBo) {
        Long id = taskTrackResultBo.getBizId();
        String status = taskTrackResultBo.getStatus();
        String msg = taskTrackResultBo.getMsg();
        ToInfrared 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);
        /*
         * 这里有很多需要做的：1. 保存所有模型到oss 2. 模型文件保存到算法模型配置表中
         */
/*
        Path resultsPath = Paths.get(entity.getInputPath());
        Path modelPath = resultsPath.resolve(BizConstant.MODEL_PATH);

        File modelDir = new File(modelPath.toString());

        if(!modelDir.exists() || !modelDir.isDirectory()) {
            return CommonResult.fail("模型文件不存在");
        }

        for (File f : modelDir.listFiles()) {
            if (f.isFile() && f.getName().endsWith(BizConstant.MODEL_SUFFIX)) {
                SysOssVo upload = ossService.upload(f);
                AlgorithmModelTrack algorithmModelTrack = new AlgorithmModelTrack();
                algorithmModelTrack.set
                algorithmModelTrackService.insert();
            }
        }
*/


        return CommonResult.success();
    }

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

//        String osName = System.getProperty("os.name");
//        if (osName.toLowerCase().contains("windows")) {
//            outputPath = outputPath;
//        }

        AlgorithmModelTrackVo model = algorithmModelTrackService.selectById(toInfrared.getAlgorithmModelId());
        AlgorithmConfigTrackVo config = algorithmConfigTrackService.selectById(model.getAlgorithmId());

        if (BizConstant.AlgorithmType.TRAIN.equals(config.getType())) { // 如果是训练类型，返回模型
            return getLastModelFile(toInfrared);
        } else if (BizConstant.AlgorithmType.REASONING.equals(config.getType())) { // 如果是推理类型，返回图片集
            return getPredictImages(toInfrared);
        } else {
            return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
        }
    }

    private String getLogFileName(ToInfrared entity) {
        return entity.getId() + BizConstant.TO_INFRARED_SUFFIX + ".log";
    }

    @Override
    public CommonResult getLog(Long id) {
        ToInfrared entity = getById(id);
        String outPutPath = entity.getOutputPath();
        String logPath = outPutPath + File.separator + getLogFileName(entity);
        System.out.println(logPath);
        File file = new File(logPath);
        if (!file.exists()) {
            return CommonResult.fail("日志文件不存在！");
        }
        return CommonResult.success(readLogContent(logPath), "success");
    }

    public static String readLogContent(String path) {
        String content = "";
        try (BufferedReader br = new BufferedReader(new FileReader(path))) {
            String line;
            while ((line = br.readLine()) != null) {
                content += (line + "<br />\n");
            }
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("读取文件失败！" + e.getMessage());
            content = "读取文件失败！" + e.getMessage();
        }

        return content;
    }

    @Override
    public CommonResult getModelList(Long id) {
        ToInfrared entity = getById(id);
        String outPutPath = entity.getOutputPath();
        String modelPath = outPutPath + File.separator + "model";
        File modelDir = new File(modelPath);
        if (!modelDir.exists()) {
            return CommonResult.fail("模型输出目录不存在！");
        }
        File[] files = modelDir.listFiles();
        Integer idx = 0;
        ArrayList<Map<String, String>> res = new ArrayList<>();
        for (File file : files) {
            idx += 1;
            Map<String, String> tmp = new HashMap<>();
            tmp.put("id", idx.toString());
            tmp.put("name", file.getName());
            String filePath = file.getPath();
            tmp.put("path", filePath);
            // todo 获取真正的url
            // http://localhost:9090/profile/upload/2024/10/27/1_1729404909511_20241027153840A001.zip
            String url = "http://localhost:" + port + Constants.RESOURCE_PREFIX + filePath.substring(profile.length());
            url = url.replaceAll("\\\\", "/"); // windows
            tmp.put("url", url);

            double fileSize = (file.length() / (1024.0 * 1024.0));
            DecimalFormat decimalFormat = new DecimalFormat("#.##");
            String formatFileSize = decimalFormat.format(fileSize);
            tmp.put("size", formatFileSize + "MB");
            res.add(tmp);
        }
        return CommonResult.success(res, "success");
    }

    @Override
    public CommonResult previewPredictResult(Long id) {
        ToInfrared 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 + File.separator + BizConstant.PREDICT_PATH);
        Path outputPath = path.resolveSibling(entity.getId().toString() + BizConstant.TO_INFRARED_SUFFIX + File.separator + BizConstant.PREDICT_PATH);

        String urlPrefix = inputOssEntity.getUrl().substring(0, inputOssEntity.getUrl().indexOf(Constants.RESOURCE_PREFIX) + Constants.RESOURCE_PREFIX.length());

        return getCompareImage(urlPrefix, inputPath.toString(), outputPath.toString());
    }

    public static CommonResult getCompareImage(String urlPrefix, String inputPath, String outputPath) {
        try {
             List<String> inputImageList = calculateImagePath(urlPrefix, inputPath);
            List<String> outputImageList = calculateImagePath(urlPrefix, outputPath);

            HashMap<String,String> nameToUrl1 = new HashMap<>();

            for (String imageUrl : inputImageList) {
                String name = imageUrl.substring(imageUrl.lastIndexOf("/") + 1);
                nameToUrl1.put(name, imageUrl);
            }
            HashMap<String,String> nameToUrl2 = new HashMap<>();
            for (String imageUrl : outputImageList) {
                String name = imageUrl.substring(imageUrl.lastIndexOf("/") + 1);
                nameToUrl2.put(name, imageUrl);
            }
            List<String> inputImageNames = new ArrayList<>(nameToUrl1.keySet());

            inputImageNames.sort(Comparator.naturalOrder());

            List<Map<String, String>> resultList = new ArrayList<>();
            for (String inputImageName : inputImageNames) {
                Map<String, String> map = new HashMap<>();
                map.put("inputUrl", nameToUrl1.get(inputImageName));
                map.put("outputUrl", nameToUrl2.get(inputImageName));
                resultList.add(map);
            }

            return CommonResult.success(resultList, "success");
        } catch (Exception e) {
            System.out.println("getImages error: " + e.getMessage());
            return CommonResult.fail("getImages error: " + e.getMessage());
        }
    }

    private ResponseEntity<org.springframework.core.io.Resource> getPredictImages(ToInfrared toInfrared) {
        String outputPath = toInfrared.getOutputPath();
        String predictDir = outputPath + File.separator + BizConstant.PREDICT_PATH;

        String zipFilePath = outputPath + File.separator + "predict.zip";

        File file = new File(zipFilePath);
        if (!file.exists()) {
            try {
                ZipUtils.zipFolderFiles(predictDir, 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);
    }

    public ResponseEntity<org.springframework.core.io.Resource> getLastModelFile(ToInfrared toInfrared) {
        String outputPath = toInfrared.getOutputPath();
        File modelDir = new File(outputPath + File.separator + BizConstant.MODEL_PATH);

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

        File[] modelFiles = modelDir.listFiles();
        if (modelFiles == null || modelFiles.length == 0) {
            return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
        }

        for (int i = 0; i < modelFiles.length; i++) {
            System.out.println(modelFiles[i].getName());
        }

        File returnFile = modelFiles[modelFiles.length - 1];

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

//    @Override
//    public ResponseEntity<org.springframework.core.io.Resource> zipImages(Long id) {
//        ToInfrared toInfrared = this.getById(id);
//        if (ObjectUtil.isNull(toInfrared)) {
//            return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
//        }
//
//        String outputPath = toInfrared.getOutputPath();
//        String zipFilePath = outputPath + ".zip";
//
//        String osName = System.getProperty("os.name");
//        if (osName.toLowerCase().contains("windows")) {
//            zipFilePath = "C:" + zipFilePath;
//        }
//
//        try {
//            ZipUtils.zipFolderFiles(outputPath, zipFilePath);
//        } catch (IOException e) {
//            throw new RuntimeException(e);
//        }
//
//        File file = new File(zipFilePath);
//
//        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);
//    }
}
