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

package com.taais.biz.service.impl;

import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
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.HttpResponseEntity;
import com.taais.biz.domain.bo.VideoStableStartResultBo;
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.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 com.taais.system.domain.vo.SysOssVo;
import com.taais.system.service.ISysOssService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
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 com.taais.biz.mapper.VideoStableMapper;
import com.taais.biz.domain.VideoStable;
import com.taais.biz.domain.bo.VideoStableBo;
import com.taais.biz.domain.vo.VideoStableVo;
import com.taais.biz.service.IVideoStableService;

import static com.taais.biz.domain.table.VideoStableTableDef.VIDEO_STABLE;
import static com.taais.biz.service.impl.DataSeqServiceImpl.isImageFile;

/**
 * 视频去抖动Service业务层处理
 *
 * @author 0
 * 2024-08-30
 */
@Service
@Slf4j
public class VideoStableServiceImpl extends BaseServiceImpl<VideoStableMapper, VideoStable> implements IVideoStableService {
    @Value("${server.video_stable_start_url}")
    private String video_stable_start_url;

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

    @Autowired
    private ISysOssService ossService;

    @Resource
    private VideoStableMapper videoStableMapper;

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

    private QueryWrapper buildQueryWrapper(VideoStableBo videoStableBo) {
        QueryWrapper queryWrapper = super.buildBaseQueryWrapper();
        queryWrapper.and(VIDEO_STABLE.NAME.like(videoStableBo.getName()));
        queryWrapper.and(VIDEO_STABLE.STATUS.like(videoStableBo.getStatus()));
        queryWrapper.and(VIDEO_STABLE.INPUT_OSS_ID.eq(videoStableBo.getInputOssId()));
        queryWrapper.and(VIDEO_STABLE.START_TIME.eq(videoStableBo.getStartTime()));
        queryWrapper.and(VIDEO_STABLE.END_TIME.eq(videoStableBo.getEndTime()));
        queryWrapper.and(VIDEO_STABLE.COST_SECOND.eq(videoStableBo.getCostSecond()));
        queryWrapper.and(VIDEO_STABLE.LOG.like(videoStableBo.getLog()));
        queryWrapper.and(VIDEO_STABLE.REMARKS.like(videoStableBo.getRemarks()));

        return queryWrapper;
    }

    /**
     * 查询视频去抖动
     *
     * @param id 视频去抖动主键
     * @return 视频去抖动
     */
    @Override
    public VideoStableVo selectById(Long id) {
        return this.getOneAs(query().where(VIDEO_STABLE.ID.eq(id)), VideoStableVo.class);

    }

    /**
     * 查询视频去抖动列表
     *
     * @param videoStableBo 视频去抖动Bo
     * @return 视频去抖动集合
     */
    @Override
    public List<VideoStableVo> selectList(VideoStableBo videoStableBo) {
        QueryWrapper queryWrapper = buildQueryWrapper(videoStableBo);
        return this.listAs(queryWrapper, VideoStableVo.class);
    }

    /**
     * 分页查询视频去抖动列表
     *
     * @param videoStableBo 视频去抖动Bo
     * @return 分页视频去抖动集合
     */
    @Override
    public PageResult<VideoStableVo> selectPage(VideoStableBo videoStableBo) {
        QueryWrapper queryWrapper = buildQueryWrapper(videoStableBo);
        Page<VideoStableVo> page = this.pageAs(PageQuery.build(), queryWrapper, VideoStableVo.class);
        return PageResult.build(page);
    }

    /**
     * 新增视频去抖动
     *
     * @param videoStableBo 视频去抖动Bo
     * @return 结果:true 操作成功，false 操作失败
     */
    @Override
    public boolean insert(VideoStableBo videoStableBo) {
        // 检查input_oss_id是否存在
        if (ObjectUtil.isNull(videoStableBo.getInputOssId())) {
            return false;
        }

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

        VideoStable videoStable = MapstructUtils.convert(videoStableBo, VideoStable.class);
        videoStable.setStatus(BizConstant.VideoStatus.NOT_START);

        return this.save(videoStable);// 使用全局配置的雪花算法主键生成器生成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 videoStableBo 视频去抖动Bo
     * @return 结果:true 更新成功，false 更新失败
     */
    @Override
    public boolean update(VideoStableBo videoStableBo) {
        VideoStable videoStable = MapstructUtils.convert(videoStableBo, VideoStable.class);
        if (ObjectUtil.isNotNull(videoStable) && ObjectUtil.isNotNull(videoStable.getId())) {
            boolean updated = this.updateById(videoStable);
            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) {
        VideoStable videoStable = getById(id);

        SysOssVo inputOssEntity = ossService.getById(videoStable.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.STABLE_SUFFIX);

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

        ZipUtils.unzip(resourcePath, inputPath.toString());

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

        videoStable.setStartTime(new Date());

        videoStable.setStatus(BizConstant.VideoStatus.RUNNING);
        updateById(videoStable);

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

    public static 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;
        }
    }

    @Override
    public CommonResult getResult(VideoStableStartResultBo videoStableStartResultBo) {
        Long id = videoStableStartResultBo.getId();
        String status = videoStableStartResultBo.getStatus();
        String msg = videoStableStartResultBo.getMsg();
        VideoStable videoStable = getById(id);
        videoStable.setLog(msg);
        videoStable.setStatus("200".equals(status) ? BizConstant.VideoStatus.END : BizConstant.VideoStatus.FAILED);
        videoStable.setEndTime(new Date());
        try {
            videoStable.setCostSecond((videoStable.getEndTime().getTime() - videoStable.getStartTime().getTime()) / 1000);
        } catch (Exception e) {
            videoStable.setCostSecond(null);
        }
        updateById(videoStable);
        return CommonResult.success();
    }

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

        Path inputPath = null;
        Path outputPath = null;
//        String osName = System.getProperty("os.name");
        // 判断是否是Windows环境
//        if (osName.toLowerCase().contains("windows")) {
//            inputPath = Paths.get("C:", videoStable.getInputPath());
//            outputPath = Paths.get("C:", videoStable.getOutputPath());
//        } else {
        inputPath = Paths.get(videoStable.getInputPath());
        outputPath = Paths.get(videoStable.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) {
        VideoStable videoStable = getById(id);

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

    @Override
    public List<Map<String, String>> getImages(Long inputOdssId) {
        // 检查input_oss_id是否存在
        if (ObjectUtil.isNull(inputOdssId)) {
            return null;
        }
        SysOssVo ossEntity = ossService.getById(inputOdssId);
        if (ObjectUtil.isNull(ossEntity)) {
            return null;
        }

        String filePath = ossEntity.getFileName();
        String localPath = TaaisConfig.getProfile();
        String resourcePath = localPath + StringUtils.substringAfter(filePath, Constants.RESOURCE_PREFIX);
        String unzipPath = resourcePath.substring(0, resourcePath.lastIndexOf(".")) + BizConstant.UNZIP_SUFFIX;
        String resultPath = resourcePath.substring(0, resourcePath.lastIndexOf(".")) + BizConstant.STABLE_SUFFIX;

        try {
            List<String> inputImageList = getImageUrls(ossEntity, unzipPath, BizConstant.UNZIP_SUFFIX);
            List<String> outputImageList = getImageUrls(ossEntity, resultPath, BizConstant.STABLE_SUFFIX);

            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 resultList;
        } catch (Exception e) {
            System.out.println("getImages error: " + e.getMessage());
            return null;
        }
    }

    public List<String> getImageUrls(SysOssVo ossEntity, String path, String suffix) {
        String urlPrefix = ossEntity.getUrl().substring(0, ossEntity.getUrl().lastIndexOf(".")) + suffix;
        // 列出unzipPath下所有图片文件
        File directory = new File(path);
        File[] files = directory.listFiles();
        List<String> imageList = Arrays.asList(files).stream().filter(file -> isImageFile(file)).map(file -> urlPrefix + "/" + file.getName()).toList();
        return imageList;
    }

    /**
     * 递归统计文件夹中的文件数量
     *
     * @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 count;
    }
}
