package com.taais.biz.service.impl;

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.HttpResponseEntity;
import com.taais.biz.domain.DataAugmentation;
import com.taais.biz.domain.TransmissionObject;
import com.taais.biz.domain.bo.DataAugmentationBo;
import com.taais.biz.domain.bo.DataAugmentationResultBo;
import com.taais.biz.domain.bo.DataAugmentationStartResultBo;
import com.taais.biz.domain.bo.VideoStableStartResultBo;
import com.taais.biz.domain.vo.DataAugmentationVo;
import com.taais.biz.mapper.DataAugmentationMapper;
import com.taais.biz.service.IDataAugmentationService;
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.core.page.PageResult;
import com.taais.common.core.utils.MapstructUtils;
import com.taais.common.core.utils.StringUtils;
import com.taais.common.json.utils.JsonUtils;
import com.taais.common.orm.core.page.PageQuery;
import com.taais.common.orm.core.service.impl.BaseServiceImpl;
import com.taais.system.domain.SysDictData;
import com.taais.system.domain.SysOss;
import com.taais.system.domain.bo.SysDictDataBo;
import com.taais.system.domain.vo.SysDictDataVo;
import com.taais.system.domain.vo.SysOssVo;
import com.taais.system.service.ISysDictDataService;
import com.taais.system.service.ISysOssService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.File;
import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import static com.taais.biz.domain.table.DataAugmentationTableDef.DATA_AUGMENTATION;
/**
 * 数据增强Service业务层处理
 *
 * @author tanlun
 * 2024-10-11
 */
@Service
@Slf4j
public class DataAugmentationServiceImpl extends BaseServiceImpl<DataAugmentationMapper, DataAugmentation> implements IDataAugmentationService {
//    @Value("${server.video_stable_start_url}")
//    private String data_augmentation_start_url;
//
//    @Value("${server.video_stable_start_url}")
//    private String data_augmentation_stop_url;

    @Autowired
    private ISysOssService ossService;
    @Autowired
    private ISysDictDataService iSysDictDataService;
    @Resource
    private DataAugmentationMapper dataAugmentationMapper;

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

    private QueryWrapper buildQueryWrapper(DataAugmentationBo dataAugmentationBo) {
        QueryWrapper queryWrapper = super.buildBaseQueryWrapper();
        queryWrapper.and(DATA_AUGMENTATION.NAME.like
            (dataAugmentationBo.getName()));
        queryWrapper.and(DATA_AUGMENTATION.STATUS.like
            (dataAugmentationBo.getStatus()));
        queryWrapper.and(DATA_AUGMENTATION.INPUT_OSS_ID.eq
            (dataAugmentationBo.getInputOssId()));
        queryWrapper.and(DATA_AUGMENTATION.START_TIME.eq
            (dataAugmentationBo.getStartTime()));
        queryWrapper.and(DATA_AUGMENTATION.END_TIME.eq
            (dataAugmentationBo.getEndTime()));
        queryWrapper.and(DATA_AUGMENTATION.COST_SECOND.eq
            (dataAugmentationBo.getCostSecond()));
        queryWrapper.and(DATA_AUGMENTATION.LOG.like
            (dataAugmentationBo.getLog()));
        queryWrapper.and(DATA_AUGMENTATION.REMARKS.like
            (dataAugmentationBo.getRemarks()));
        queryWrapper.and(DATA_AUGMENTATION.ALGORITHM_PATH.eq
            (dataAugmentationBo.getAlgorithmPath()));
        queryWrapper.and(DATA_AUGMENTATION.HYPERPARAMETER_CONFIGURATION.like
            (dataAugmentationBo.getHyperparameterConfiguration()));
        queryWrapper.and(DATA_AUGMENTATION.TASK_TYPE.like
            (dataAugmentationBo.getTaskType()));

        return queryWrapper;
    }

    /**
     * 查询数据增强
     *
     * @param id 数据增强主键
     * @return 数据增强
     */
    @Override
    public DataAugmentationVo selectById(Long id) {
        return this.getOneAs(query().where(DATA_AUGMENTATION.ID.eq(id)), DataAugmentationVo.class);

    }

    /**
     * 查询数据增强列表
     *
     * @param dataAugmentationBo 数据增强Bo
     * @return 数据增强集合
     */
    @Override
    public List<DataAugmentationVo> selectList(DataAugmentationBo dataAugmentationBo) {
        QueryWrapper queryWrapper = buildQueryWrapper(dataAugmentationBo);
        return this.listAs(queryWrapper, DataAugmentationVo.class);
    }

    /**
     * 分页查询数据增强列表
     *
     * @param dataAugmentationBo 数据增强Bo
     * @return 分页数据增强集合
     */
    @Override
    public PageResult<DataAugmentationVo> selectPage(DataAugmentationBo dataAugmentationBo) {
        QueryWrapper queryWrapper = buildQueryWrapper(dataAugmentationBo);
        Page<DataAugmentationVo> page = this.pageAs(PageQuery.build(), queryWrapper, DataAugmentationVo.class);
        return PageResult.build(page);
    }

    /**
     * 新增数据增强
     *
     * @param dataAugmentationBo 数据增强Bo
     * @return 结果:true 操作成功，false 操作失败
     */
    @Override
    public boolean insert(DataAugmentationBo dataAugmentationBo) {
        if (ObjectUtil.isNull(dataAugmentationBo.getInputOssId())) {
            return false;
        }

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

        DataAugmentation dataAugmentation = MapstructUtils.convert(dataAugmentationBo, DataAugmentation.class);
        dataAugmentation.setStatus(BizConstant.ModelStatus.NOT_START);

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

    public static String removeFileExtension(String fileName) {
        int lastDotIndex = fileName.lastIndexOf('.');
        if (lastDotIndex == -1) {
            return fileName; // 如果没有找到'.'，则返回原文件名
        }
        return fileName.substring(0, lastDotIndex);
    }

    public static void makeDir(String path) {
        File folder = new File(path);
        if (!folder.exists()) {
            folder.mkdirs();
        } else {
            try {
                FileUtils.cleanDirectory(folder);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * 修改数据增强
     *
     * @param dataAugmentationBo 数据增强Bo
     * @return 结果:true 更新成功，false 更新失败
     */
    @Override
    public boolean update(DataAugmentationBo dataAugmentationBo) {
        DataAugmentation dataAugmentation = MapstructUtils.convert(dataAugmentationBo, DataAugmentation.class);
        if (ObjectUtil.isNotNull(dataAugmentation) && ObjectUtil.isNotNull(dataAugmentation.getId())) {
            boolean updated = this.updateById(dataAugmentation);
            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) {
        try {
            DataAugmentation dataAugmentation = getById(id);
            SysOssVo inputOssEntity = ossService.getById(dataAugmentation.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 = null;
            Path inputPath = null;
            Path outputPath = null;
            Path logPath = null;
            path = Paths.get(resourcePath);
            inputPath = path.resolveSibling(fileName_without_suffix + "_input");
            outputPath = path.resolveSibling(fileName_without_suffix + "_output");
            logPath = path.resolveSibling(fileName_without_suffix + "_log");
            makeDir(inputPath.toString());
            makeDir(outputPath.toString());
            makeDir(logPath.toString());
            logPath = Paths.get(logPath.toString(), "log.log");
            Files.createFile(logPath);
            System.out.println("inputPath:" + inputPath.toString());
            ZipUtils.unzip(resourcePath, inputPath.toString());
            dataAugmentation.setInputPath(inputPath.toString());
            dataAugmentation.setAlgorithmPath(logPath.toString());
            dataAugmentation.setOutputPath(outputPath.toString());
//            dataAugmentation.setStatus(BizConstant.ModelStatus.RUNNING);
//            updateById(dataAugmentation);
            SysDictDataBo sysDictDataBo = new SysDictDataBo();
            sysDictDataBo.setDictLabel(dataAugmentation.getTaskType() + "开始url");
            sysDictDataBo.setDictType("biz_data_augmentation");
            List<SysDictDataVo> sysDictDataVos = iSysDictDataService.selectDictDataList(sysDictDataBo);
            if (sysDictDataVos.size() == 0) {
                return CommonResult.fail("未设置" + dataAugmentation.getTaskType() + "算法推理的url！请在数据字典中设置该算法的推理url！");
            }
            String data_augmentation_start_url = sysDictDataVos.get(0).getDictValue();
            //设置传输对象
            TransmissionObject transmissionObject = new TransmissionObject();
            transmissionObject.setBizId(dataAugmentation.getId());
            transmissionObject.setBizType(dataAugmentation.getTaskType());
            transmissionObject.setLogPath(dataAugmentation.getAlgorithmPath());
            transmissionObject.setSourcePath(dataAugmentation.getInputPath());
            transmissionObject.setResultPath(dataAugmentation.getOutputPath());
            transmissionObject.setOtherParams(dataAugmentation.getHyperparameterConfiguration());
            HttpResponseEntity responseEntity = sendPostMsg(data_augmentation_start_url, transmissionObject);
            if (responseEntity != null && responseEntity.getStatus() == 200) {
                dataAugmentation.setStartTime(new Date());
                dataAugmentation.setStatus(BizConstant.ModelStatus.RUNNING);
                updateById(dataAugmentation);
                return CommonResult.success("任务开始成功，请等待完成");
            }
            else {
//                dataAugmentation.setStatus(BizConstant.ModelStatus.FAILED);
//                updateById(dataAugmentation);
                return CommonResult.fail("任务开始失败，请检查算法服务是否启动！");
            }
        } catch (IOException e) {
            e.printStackTrace();
            return CommonResult.fail("出现异常：" + e.toString());
        }

    }

    public HttpResponseEntity sendPostMsg(String url, Object obj) {
        log.info("sendMsg: {} - {}", url, JsonUtils.toJsonString(obj));

        CloseableHttpClient httpClient = HttpClients.createDefault();
        HttpPost request = new HttpPost(url);
        // 设置请求体
        StringEntity entity = new StringEntity(JsonUtils.toJsonString(obj), "UTF-8");
        entity.setContentType("application/json");
        request.setEntity(entity);

        try {
            CloseableHttpResponse response = httpClient.execute(request);
            if (response.getStatusLine().getStatusCode() != 200) {
                log.error("sendMsg failed, status code -> " + response.getStatusLine().getStatusCode());
                return null;
            }
            String responseBody = EntityUtils.toString(response.getEntity());
            log.info("responseBody -> " + responseBody);
            // 使用 JsonUtils 将响应体转换为指定的实体类
            return JsonUtils.parseObject(responseBody, HttpResponseEntity.class);
        } catch (Exception e) {
            log.error("sendMsg error", e);
            return null;
        }
    }

    private static void createZipArchive(Path sourceDir, Path zipFilePath) throws IOException {
        try (ZipOutputStream zos = new ZipOutputStream(Files.newOutputStream(zipFilePath))) {
            Files.walkFileTree(sourceDir, new SimpleFileVisitor<Path>() {
                public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                    Path targetFile = sourceDir.relativize(file);
                    ZipEntry zipEntry = new ZipEntry(targetFile.toString());
                    zipEntry.setTime(attrs.lastModifiedTime().toMillis());
                    zos.putNextEntry(zipEntry);
                    byte[] bytes = Files.readAllBytes(file);
                    zos.write(bytes, 0, bytes.length);
                    zos.closeEntry();
                    return FileVisitResult.CONTINUE;
                }

                public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
                    // 不需要为目录添加条目，除非你想记录空目录
                    return FileVisitResult.CONTINUE;
                }
            });
        }
    }
    @Override
    public CommonResult getResult(DataAugmentationResultBo dataAugmentationStartResultBo) {
        Long id = dataAugmentationStartResultBo.getId();
        String status = dataAugmentationStartResultBo.getStatus();
        String msg = dataAugmentationStartResultBo.getMsg();
        DataAugmentation dataAugmentation = getById(id);
        String outputPath = dataAugmentation.getOutputPath();
        // 要压缩的目录路径
        Path sourceDir = Paths.get(outputPath);
        // 输出的ZIP文件路径
        Path zipFilePath = sourceDir.resolveSibling(sourceDir.getFileName() + ".zip");
        dataAugmentation.setLog(msg);
        dataAugmentation.setStatus("200".equals(status) ? BizConstant.ModelStatus.END : BizConstant.ModelStatus.FAILED);
        dataAugmentation.setEndTime(new Date());
        dataAugmentation.setCostSecond((dataAugmentation.getEndTime().getTime() - dataAugmentation.getStartTime().getTime()) / 1000);
        try {
            updateById(dataAugmentation);
            createZipArchive(sourceDir, zipFilePath);
            System.out.println("Directory has been zipped successfully: " + zipFilePath);
            return CommonResult.success();
        } catch (Exception e) {
            System.out.println("Directory has not been zipped successfully: " + zipFilePath);
            return  CommonResult.fail("Directory has not been zipped successfully: " + zipFilePath);
        }
    }

    @Override
    public HashMap<String, Object> getCompareNum(Long taskId) {
        DataAugmentation dataAugmentation = getById(taskId);

        Path inputPath = null;
        Path outputPath = null;
//        String osName = System.getProperty("os.name");
        // 判断是否是Windows环境
//        if (osName.toLowerCase().contains("windows")) {
//            inputPath = Paths.get("D:", dataAugmentation.getInputPath());
//            outputPath = Paths.get("D:", dataAugmentation.getOutputPath());
//        } else {
//            inputPath = Paths.get(dataAugmentation.getInputPath());
//            outputPath = Paths.get(dataAugmentation.getOutputPath());
//        }

        inputPath = Paths.get(dataAugmentation.getInputPath());
        outputPath = Paths.get(dataAugmentation.getOutputPath());
        // 创建File对象
        File in_directory = new File(inputPath.toString());
        File out_directory = new File(outputPath.toString());

        // 调用方法获取文件数量
        int in_fileCount = countFiles(in_directory);
        int out_fileCount = countFiles(out_directory);

        // 输出文件数量
        log.info("inFileCount -> " + in_fileCount);
        log.info("outFileCount -> " + out_fileCount);

        HashMap<String, Object> map = new HashMap<>();
        map.put("inFileCount", in_fileCount);
        map.put("outFileCount", out_fileCount);
        return map;
    }

    @Override
    public CommonResult stop(Long id) {
        DataAugmentation dataAugmentation = getById(id);
        SysDictDataBo sysDictDataBo = new SysDictDataBo();
        sysDictDataBo.setDictLabel(dataAugmentation.getTaskType() + "停止url");
        sysDictDataBo.setDictType("biz_data_augmentation");
        List<SysDictDataVo> sysDictDataVos = iSysDictDataService.selectDictDataList(sysDictDataBo);
        if (sysDictDataVos.size() == 0) {
            return CommonResult.fail("未设置数据增强算法停止推理的url！请在数据字典中设置该算法的停止推理地址！");
        }
        String data_augmentation_stop_url = sysDictDataVos.get(0).getDictValue();
        TransmissionObject transmissionObject = new TransmissionObject();
        transmissionObject.setBizId(dataAugmentation.getId());
        transmissionObject.setBizType(dataAugmentation.getTaskType());
        HttpResponseEntity responseEntity = sendPostMsg(data_augmentation_stop_url, transmissionObject);
        if (ObjectUtil.isNotNull(responseEntity) && responseEntity.getStatus() == 200) {
            dataAugmentation.setStatus(BizConstant.ModelStatus.INTERRUPTED);
            updateById(dataAugmentation);
            return CommonResult.success("终止任务成功");
        } else {
            return CommonResult.fail("终止任务失败");
        }
    }

    /**
     * 递归统计文件夹中的文件数量
     *
     * @param directory 文件夹对象
     * @return 文件数量
     */
    public static int countFiles(File directory) {
        // 检查文件夹是否存在且是一个目录
        if (!directory.exists() || !directory.isDirectory()) {
            System.out.println("指定的路径不是一个有效的文件夹");
            return 0;
        }

        // 获取文件夹中的所有文件和子文件夹
        File[] files = directory.listFiles();

        // 初始化文件计数器
//        int count = 0;

        // 遍历文件和子文件夹
//        for (File file : files) {
//            if (file.isFile()) {
//                // 如果是文件，计数器加1
//                count++;
//            } else if (file.isDirectory()) {
//                // 如果是子文件夹，递归调用countFiles方法
//                count += countFiles(file);
//            }
//        }

        return files.length;
    }
}
