package com.ruoyi.project.business.service.impl;

import cn.hutool.core.io.file.FileNameUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.enums.DelFlag;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.file.FileUploadKoyoUtils;
import com.ruoyi.common.utils.file.FileUtils;
import com.ruoyi.common.utils.file.OfficeToPdfUtils;
import com.ruoyi.framework.redis.RedisCache;
import com.ruoyi.project.business.domain.dto.TempPdfFileDto;
import com.ruoyi.project.business.domain.train.TtFileDatabase;
import com.ruoyi.project.business.domain.train.TtTrain;
import com.ruoyi.project.business.domain.train.TtTrainFile;
import com.ruoyi.project.business.domain.train.TtTrainTarget;
import com.ruoyi.project.business.domain.vo.train.TrainFileDetailRes;
import com.ruoyi.project.business.mapper.TtTrainFileMapper;
import com.ruoyi.project.business.service.ITtTrainFileService;
import com.ruoyi.project.business.service.ITtTrainService;
import com.ruoyi.project.business.service.ITtTrainTargetService;
import com.ruoyi.project.business.service.train.ITtFileDatabaseService;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 培训资料Service业务层处理
 *
 * @author ruoyi
 * @date 2024-06-19
 */
@Service
@RequiredArgsConstructor
public class TtTrainFileServiceImpl extends ServiceImpl<TtTrainFileMapper, TtTrainFile> implements ITtTrainFileService {

    private final ITtFileDatabaseService fileDatabaseService;

    private final ITtTrainService ttTrainService;

    private final ITtTrainTargetService ttTrainTargetService;

    // private final RedisTemplate<String, Object> redisTemplate;
    
    private final RedisCache redisCache;


    /**
     * 查询培训资料
     *
     * @param id 培训资料主键
     * @return 培训资料
     */
    @Override
    public TtTrainFile selectTtTrainFileById(Long id)
    {
        return baseMapper.selectTtTrainFileById(id);
    }

    /**
     * 查询培训资料列表
     *
     * @param ttTrainFile 培训资料
     * @return 培训资料
     */
    @Override
    public List<TtTrainFile> selectTtTrainFileList(TtTrainFile ttTrainFile) {
        return baseMapper.selectTtTrainFileList(ttTrainFile);
    }

    /**
     * 新增培训资料
     *
     * @param ttTrainFile 培训资料
     * @return 结果
     */
    @Override
    public int insertTtTrainFile(TtTrainFile ttTrainFile) {
        // ttTrainFile.setCreateTime(DateUtils.getNowDate());
        // return baseMapper.insertTtTrainFile(ttTrainFile);
        return save(ttTrainFile) ? 1 : 0;
    }

    /**
     * 修改培训资料
     *
     * @param ttTrainFile 培训资料
     * @return 结果
     */
    @Override
    public int updateTtTrainFile(TtTrainFile ttTrainFile) {
        // ttTrainFile.setUpdateTime(DateUtils.getNowDate());
        // return baseMapper.updateTtTrainFile(ttTrainFile);
        return updateById(ttTrainFile) ? 1 : 0;
    }

    /**
     * 批量删除培训资料
     *
     * @param ids 需要删除的培训资料主键
     * @return 结果
     */
    @Override
    public int deleteTtTrainFileByIds(Long[] ids) {
        // return baseMapper.deleteTtTrainFileByIds(ids);
        return removeByIds(Arrays.asList(ids)) ? 1 : 0;
    }

    /**
     * 删除培训资料信息
     *
     * @param id 培训资料主键
     * @return 结果
     */
    @Override
    public int deleteTtTrainFileById(Long id) {
        // return baseMapper.deleteTtTrainFileById(id);
        return removeById(id) ? 1 : 0;
    }

    @Override
    public TrainFileDetailRes fileByTrainId(Long trainId) {
        // 查询 trainId 对应的 TtTrainFile ， 添加逻辑删除字段
        TtTrainFile one = getOne(
                Wrappers.lambdaQuery(TtTrainFile.class)
                        .eq(TtTrainFile::getTrainId, trainId)
                        .eq(TtTrainFile::getDelFlag, DelFlag.NO.getCode()), 
                false);
        // 查询对应的 TtTrainTarget
        TtTrainTarget target = ttTrainTargetService.getOne(Wrappers.lambdaQuery(TtTrainTarget.class)
                .eq(TtTrainTarget::getTrainId, trainId)
                .eq(TtTrainTarget::getTargetPersonId, SecurityUtils.getLoginUser().getUserId()));
        if (one == null) {
//            return null;
            return TrainFileDetailRes.from(target);
        }

        // 查询对应的 TtFileDatabase
        TtFileDatabase file = fileDatabaseService.selectTtFileDatabaseById(one.getFileId());

        // 查询对应的 TtTrain
        TtTrain train = ttTrainService.getById(trainId);



        // 如果是 office 文件，需要转换为 PDF 文件
        // if (isOffice(file)) {
        //     handleWordFileConversion(file);
        // }

        return TrainFileDetailRes.from(one, file, train,target);
    }

    private boolean isOffice(TtFileDatabase file) {
        return StringUtils.isNotBlank(file.getFileName())
                && Constants.FILE_TYPE_OFFICE.contains(FileNameUtil.getSuffix(file.getFileName()));
    }

    private void handleWordFileConversion(TtFileDatabase file) {
        // 获取缓存的临时 PDF 文件信息
            TempPdfFileDto tempPdfCache = redisCache.getCacheObject(CacheConstants.TEMP_PDF_FILE_KEY + file.getFileName());
        // 获取当前文件的 CRC32 校验码
        String localFilePath = FileUploadKoyoUtils.getLocalPath(file.getFilePath());
        String summary = getFileCrc32(localFilePath);

        if (shouldConvertToPdf(tempPdfCache, summary)) {
            convertToPdfAndUpdateCache(file, localFilePath, summary, tempPdfCache);
        } else {
            file.setFilePath(new File(tempPdfCache.getPath()).getPath());
            // expire in 7 days
            redisCache.expire(CacheConstants.TEMP_PDF_FILE_KEY + file.getFileName(), 7, TimeUnit.DAYS);
        }
    }

    /**
     * 使用 CRC32 算法获取文件的校验码 (CRC32摘要有碰撞几率)
     * 
     * @param filePath  文件路径
     */
    private String getFileCrc32(String filePath) {
        try {
            return String.valueOf(FileUtils.getFileCRC32(filePath));
        } catch (IOException e) {
            log.error("获取文件 CRC32 失败", e);
            throw new RuntimeException("获取文件 CRC32 失败", e);
        }
    }

    /**
     * 判断是否需要转换为 PDF 文件
     * 
     * @param tempPdfFileDto    缓存的临时 PDF 文件信息
     * @param summary        文件的 CRC32 校验码
     * @return
     */
    private boolean shouldConvertToPdf(TempPdfFileDto tempPdfFileDto, String summary) {
        return tempPdfFileDto == null || !summary.equals(tempPdfFileDto.getSummary());
    }

    /**
     * 转换为 PDF 文件并更新缓存
     * 
     * @param file      TtFileDatabase
     * @param localFilePath 本地文件路径
     * @param summary   文件的 CRC32 校验码
     * @param tempPdfFileDto    缓存的临时 PDF 文件信息
     */
    private void convertToPdfAndUpdateCache(TtFileDatabase file, String localFilePath, String summary, TempPdfFileDto tempPdfFileDto) {
        if (tempPdfFileDto != null && tempPdfFileDto.getPath() != null) {
            // 这里一定由于文件校验码不一致而执行；缓存信息不存在或者过期不用删除，直接覆盖
            FileUtils.deleteFileNew(FileUploadKoyoUtils.getLocalPath(tempPdfFileDto.getPath()));
            // TODO 使用定时任务处理没有被再次索引的临时文件
        }

        String fileName = file.getFileName();
        String fileType = FileNameUtil.getSuffix(fileName);

        // 保存临时文件的完整路径
        String tempPdfTarget = FileUploadKoyoUtils.getTempPath(fileName);
        // 系统内部可用路径
        String internalPath = FileUploadKoyoUtils.getInternalPath(fileName);
        // 转 pdf
        // Word2PdfConverter.word2Pdf(localFilePath, tempPdfTarget);
        OfficeToPdfUtils.office2Pdf(fileType, localFilePath, tempPdfTarget);
        
        TempPdfFileDto newTempPdfFileDto = new TempPdfFileDto(file.getId(), fileName, internalPath, summary);
        redisCache.setCacheObject(CacheConstants.TEMP_PDF_FILE_KEY + file.getFileName(), newTempPdfFileDto, 7, TimeUnit.DAYS);
        file.setFilePath(internalPath);
    }

}
