package com.yunyan.business.service.impl;

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.*;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.yunyan.business.document.y22.pojo.finalExcelY22.ExcelDeptInfoVo;
import com.yunyan.business.document.y23.budget.pojo.budgetY23.BudgetDataVo;
import com.yunyan.business.domain.FinalData;
import com.yunyan.business.domain.ReportExplain;
import com.yunyan.business.mapper.FinalDataMapper;
import com.yunyan.business.pojo.admin.dto.FinalDataExportDto;
import com.yunyan.business.pojo.admin.dto.ReportAttachmentDto;
import com.yunyan.business.pojo.admin.dto.ReportAttachmentItemDto;
import com.yunyan.business.pojo.admin.vo.FinalDataDownloadFileItemVo;
import com.yunyan.business.pojo.admin.vo.FinalDataDownloadFileVo;
import com.yunyan.business.pojo.admin.vo.FinalDataExportZipListVo;
import com.yunyan.business.pojo.admin.vo.ReportFileExportZipListVo;
import com.yunyan.business.service.IFinalDataService;
import com.yunyan.business.service.IReportExplainService;
import com.yunyan.common.config.YunYanConfig;
import com.yunyan.common.constant.Constants;
import com.yunyan.common.exception.ServiceException;
import com.yunyan.common.utils.DateUtils;
import com.yunyan.common.utils.StringUtils;
import com.yunyan.common.utils.file.FileUtils;
import com.yunyan.common.utils.file.ZipFileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.yunyan.business.mapper.ReportFileMapper;
import com.yunyan.business.domain.ReportFile;
import com.yunyan.business.service.IReportFileService;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import lombok.Builder;
import com.yunyan.common.core.domain.AjaxResultVo;
import org.springframework.beans.BeanUtils;
import com.yunyan.common.core.domain.model.LoginUser;
import com.yunyan.common.utils.SecurityUtils;
import com.yunyan.common.enums.DataContentType;
import lombok.extern.slf4j.Slf4j;

import java.util.stream.Collectors;
import java.util.zip.ZipOutputStream;

import com.yunyan.common.annotation.DataScope;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletResponse;

/**
 * 报告-文件相关(附件)Service业务层处理
 *
 * @author yunyan
 * @date 2023-12-21
 */
@Slf4j
@Service
public class ReportFileServiceImpl extends ServiceImpl<ReportFileMapper, ReportFile> implements IReportFileService
{
    @Autowired
    private ReportFileMapper reportFileMapper;

    @Autowired
    private IFinalDataService finalDataService;

    @Autowired
    private IReportExplainService reportExplainService;

    @Autowired
    private FinalDataMapper finalDataMapper;

    /**
     * 查询报告-文件相关(附件)列表
     *
     * @param reportFile 报告-文件相关(附件)
     * @return 报告-文件相关(附件)
     */    @Override
    public List<ReportFile> selectReportFileList(ReportFile reportFile)
    {
        return reportFileMapper.selectReportFileList(reportFile);
    }

    /**
    * 新增
    * @param
    * @return
    */
    @Override
    public AjaxResultVo add(ReportFile dto) {
        ReportFile entity = dto;

        this.save(entity);
        return AjaxResultVo.success();
    }

    /**
     * 编辑
     * @param
     * @return
     */
    @Override
    public AjaxResultVo edit(ReportFile dto) {

        ReportFile entity = this.getById(dto.getId());
        if(null == entity) {
            return AjaxResultVo.error("非法操作");
        }

        entity = new ReportFile();
        BeanUtils.copyProperties(dto, entity);

        this.updateById(entity);
        return AjaxResultVo.success();
    }

    /**
     * 详情
     * @param id
     * @return
     */
    @Override
    public AjaxResultVo info(Long id) {
        ReportFile entity = this.getById(id);
        if(null == entity) {
            return AjaxResultVo.error("数据查询异常~");
        }

        return AjaxResultVo.success(entity);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public AjaxResultVo reportAttachmentImport(ReportAttachmentDto dto) {

        Long reportId = dto.getReportId();

        //判断 报告是否是预算
        FinalData finalData = finalDataService.getById(dto.getReportId());
        if(null == finalData) {
            return AjaxResultVo.error("报告查询异常, 请稍后再试~");
        }

        /*if(2 != finalData.getType()) {
            return AjaxResultVo.error("只有预算报告有附件上传, 请稍后再试~");
        }*/

        //String urlOld = "";

        /*ReportFile reportFile = getByReportId(dto.getReportId());
        if(null != reportFile && null == dto.getId()) {
            return AjaxResultVo.error("已存在附件, 请传附件ID~");
        }*/

        /*if(null != reportFile && null != dto.getId() && !dto.getId().equals(reportFile.getId())) {
            return AjaxResultVo.error("已存在附件, 传的附件ID不一致~");
        }*/

        /*if(null != reportFile) {
            urlOld = reportFile.getFileUrl();
        }*/

        List<ReportAttachmentItemDto> list = dto.getList();

        //删除旧的
        //删除 源文件
        //查询旧文件
        List<ReportFile> reportFileList = this.lambdaQuery().eq(ReportFile::getReportId, reportId).list();
        if(null != reportFileList && !reportFileList.isEmpty()) {
            for (ReportFile item : reportFileList ) {
                if(null != list && !list.isEmpty()) {
                    Optional<ReportAttachmentItemDto> newAddObj = list.stream().filter(i -> StringUtils.isNotBlank(item.getFileUrl()) && item.getFileUrl().equals(i.getUrl())).findFirst();
                    if(newAddObj.isPresent()) {
                        //不删除, 现在保存的文件
                        continue;
                    }
                }
                try {
                    String absolutePathName = YunYanConfig.getProfile() + StringUtils.substringAfter(item.getFileUrl(), Constants.RESOURCE_PREFIX);
                    FileUtils.deleteFile(absolutePathName);
                } catch (Exception e) {
                    log.error("删除文件异常: [{}] - [{}]", e.getMessage(), e);
                }
            }
        }
        this.lambdaUpdate().eq(ReportFile::getReportId, reportId).remove();

        //新增
        List<ReportFile> addList = new ArrayList<>();
        if(null != list && !list.isEmpty()) {
            addList = list.stream().map(i -> {
                ReportFile entity = new ReportFile();
                entity.setType(1L);
                entity.setFileName(i.getName());
                entity.setFileUrl(i.getUrl());
                entity.setReportId(reportId);
                return entity;
            }).collect(Collectors.toList());
        }
        if(!addList.isEmpty()) {
            this.saveBatch(addList);
        }

        //更新 附件
        FinalData updateEntity = new FinalData();
        updateEntity.setId(finalData.getId());
        if(addList.size() > 0) {
            updateEntity.setAttachStatus(1);
        } else {
            updateEntity.setAttachStatus(0);
        }
        finalDataService.updateById(updateEntity);
        //updateEntity.set(finalData.getId());


        /*ReportFile reportFileUpdate = new ReportFile();
        BeanUtils.copyProperties(dto, reportFileUpdate);
        this.saveOrUpdate(reportFileUpdate);*/

        //更新保存 文件名 去掉, 不用
        //changeReportExplainFile(reportId, dto.getFileName());
        //更新下载的文件 现在不用和报告导出的一样, 单独的附件下载
        //changeFinalDataFile(reportId, dto.getFileName(), dto.getFileUrl(), false);



        return AjaxResultVo.success();
    }

    /**
     * 更新 报告中的json的 file文件名
     * @param reportId
     * @param fileName
     */
    boolean changeReportExplainFile(Long reportId, String fileName) {
        //查询 报告的数据
        ReportExplain reportExplain = reportExplainService.getReportExplainByReportId(reportId);
        if(null == reportExplain || StringUtils.isBlank(reportExplain.getJsonContent())) {
            throw new ServiceException("数据还未读取, 请联系管理员~");
        }

        BudgetDataVo dataVo = JSONUtil.toBean(reportExplain.getJsonContent(), BudgetDataVo.class);
        dataVo.getExplainVo().getP3p10Vo().getP4Vo().setF1(fileName);

        String jsonTableContent = JSONUtil.toJsonStr(dataVo);
        ReportExplain reportExplainUpdate = new ReportExplain();
        reportExplainUpdate.setId(reportExplain.getId());
        reportExplainUpdate.setJsonContent(jsonTableContent);
        boolean updateReportExplainRes = reportExplainService.updateById(reportExplainUpdate);
        return updateReportExplainRes;
    }

    /**
     * 更新 下载文件中的 附件
     * @param reportId
     * @param fileName
     * @return
     */
    boolean changeFinalDataFile(Long reportId, String fileName, String url, boolean delFlag) {
        FinalData finalData = finalDataService.lambdaQuery().eq(FinalData::getId, reportId)
                .select(FinalData::getJsonFileContent, FinalData::getGenerateStatus, FinalData::getId).last("and rownum = 1 order by create_time desc").one();
        if(null == finalData) {
            throw new ServiceException("报告数据查询失败, 请联系管理员~");
        }

        //报告生成 状态失败时, 不用更新 下载的文件
        if(1 != finalData.getGenerateStatus() || StringUtils.isBlank(finalData.getJsonFileContent())) {
            return false;
        }
        FinalDataDownloadFileVo fileVo = JSONUtil.toBean(finalData.getJsonFileContent(), FinalDataDownloadFileVo.class);

        if(!delFlag && StringUtils.isNotBlank(fileName) && StringUtils.isNotBlank(url) ) {
            FinalDataDownloadFileItemVo item3Vo = new FinalDataDownloadFileItemVo();
            item3Vo.setName(fileName.substring(0, fileName.lastIndexOf(".")));
            item3Vo.setUrl(url);
            item3Vo.setFileExtension(fileName.substring(fileName.lastIndexOf(".") + 1));
            item3Vo.setType(4);
            fileVo.getList().add(item3Vo);
        }

        //当时删除时, 更新下载的文件
        if(delFlag) {
            List<FinalDataDownloadFileItemVo> list = fileVo.getList().stream().filter(i-> null == i.getType() || 4 != i.getType()).collect(Collectors.toList());
            fileVo.setList(list);
        }

        //更新 附件的
        String jsonTableContent = JSONUtil.toJsonStr(fileVo);
        FinalData finalDataUpdate = new FinalData();
        finalDataUpdate.setId(reportId);
        finalDataUpdate.setJsonFileContent(jsonTableContent);
        boolean updateFinalRes = finalDataService.updateById(finalDataUpdate);
        return updateFinalRes;
    }

    @Override
    public AjaxResultVo<List<ReportAttachmentItemDto>> reportAttachmentInfo(Long reportId) {

        //判断 报告是否是预算
        FinalData finalData = finalDataService.getById(reportId);
        if(null == finalData) {
            return AjaxResultVo.error("报告查询异常, 请稍后再试~");
        }

        /*if(2 != finalData.getType()) {
            return AjaxResultVo.error("只有预算报告有附件上传, 请稍后再试~");
        }*/

        List<ReportFile> reportFileList = this.lambdaQuery().eq(ReportFile::getReportId, reportId).list();
        if(null == reportFileList || reportFileList.isEmpty()) {
            return AjaxResultVo.success(null);
        }
        /*ReportFile reportFile = getByReportId(reportId);
        if(null == reportFile) {
            return AjaxResultVo.success(null);
        }*/
        //转化
        List<ReportAttachmentItemDto> vo = toChangeDto(reportFileList);

        return AjaxResultVo.success(vo);
    }

    /**
     * 转换 返回对象
     * @param reportFileList
     * @return
     */
    List<ReportAttachmentItemDto> toChangeDto(List<ReportFile> reportFileList) {
        List<ReportAttachmentItemDto> list = new ArrayList<>();
        if(null != reportFileList && !reportFileList.isEmpty()) {
            list = reportFileList.stream().map(i-> {
                ReportAttachmentItemDto dto = new ReportAttachmentItemDto();
                dto.setName(i.getFileName());
                dto.setUrl(i.getFileUrl());
                dto.setCreateTime(i.getCreateTime());
                return dto;
            }).collect(Collectors.toList());
        }

        return list;
    }


    /**
     * 查询 报告的附件
     * @param reportId
     * @return
     */
    @Override
    public ReportFile getByReportId(Long reportId) {
        LambdaQueryWrapper<ReportFile> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ReportFile::getReportId, reportId);
        queryWrapper.last("and rownum = 1 order by create_time desc");
        ReportFile reportFile = this.getOne(queryWrapper);
        return reportFile;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public AjaxResultVo remove(Long[] ids) {

        //更新 json中的 文件名
        for (Long id : ids) {
            ReportFile reportFile = this.getById(id);
            if(null == reportFile) {
                throw new ServiceException("附件查询失败, 请稍后再试~");
            }

            //更新 报告中的附件名
            changeReportExplainFile(reportFile.getReportId(), "");
            //更新下载的文件名
            changeFinalDataFile(reportFile.getReportId(), reportFile.getFileName(), reportFile.getFileUrl(),  true);
            //删除 源文件
            String absolutePathName = YunYanConfig.getProfile() + StringUtils.substringAfter(reportFile.getFileUrl(), Constants.RESOURCE_PREFIX);
            FileUtils.deleteFile(absolutePathName);
        }

        boolean res = this.removeByIds(Arrays.asList(ids));
        return res? AjaxResultVo.success() : AjaxResultVo.error();
    }

    @Override
    public void exportZip(HttpServletResponse response, FinalDataExportDto dto) {
        String zipName = dto.getZipName();
        if(StringUtils.isBlank(zipName)) {
            zipName = "上传附件_" + DateUtils.dateTimeNow() ;
        }

        //1.查询 文件名, 限制区域等
        List<ReportFileExportZipListVo> list = finalDataMapper.selectReportFileExportZipList(dto);

        if(list.isEmpty()) {
            throw new ServiceException("未有符合导出的数据");
        }

        //2. 压缩文件名

        //文件路径
        ZipOutputStream zipOutputStream = null;
        //设置响应头
        response.reset();
        response.setCharacterEncoding("utf-8");
        response.setHeader("Content-Disposition", "attachment;filename=" + URLUtil.encode(zipName , StandardCharsets.UTF_8) + ".zip");
        response.setContentType("application/zip");
        //设置文件名称
        // response.setHeader("Content-Length", String.valueOf(count)); //传出文件长度，这样前端可以实现下载进度条
        try {
            zipOutputStream = new ZipOutputStream(response.getOutputStream());
        } catch (IOException e) {
            log.debug("zipOutputStream异常: [{}], [{}]", e.getMessage(), e);
            throw new ServiceException("zipOutputStream流异常");
        }

        // 本机地址的文件路径
        String localPath = YunYanConfig.getProfile();

        //记录 压缩文件名
        Map<String, Integer> fineNameMap = new HashMap<>();

        int fileZipNum = 0;
        for(ReportFileExportZipListVo reportFileItem : list ) {
            //如果没有文件的话, 跳过
            if( StringUtils.isBlank(reportFileItem.getFileUrl()) || StringUtils.isBlank(reportFileItem.getFileName())) {
                log.info("[{}]文件不存在", reportFileItem.getId());
                continue;
            }
            String fileNameNotType = reportFileItem.getFileName().substring(0, reportFileItem.getFileName().lastIndexOf(".") );
            String innerFileName = String.format("%s_%s.%s", reportFileItem.getDeptCode(), reportFileItem.getDeptName(), fileNameNotType);

            try {
                //生成 打包文件
                String absolutePathName = localPath + StringUtils.substringAfter(reportFileItem.getFileUrl(), Constants.RESOURCE_PREFIX);
                File file = new File(absolutePathName);
                if(!file.exists()) {
                    log.info("[{}]文件不存在", absolutePathName);
                    //todo 记录
                    continue;
                }

                String zipFileName = innerFileName;
                if(fineNameMap.containsKey(innerFileName)) {
                    zipFileName += ".(" + fineNameMap.get(innerFileName).intValue() + ")";
                }
                boolean zipResult = ZipFileUtils.toZip(zipOutputStream, file, zipFileName);
                if(zipResult) {
                    fileZipNum++;
                    if(fineNameMap.containsKey(innerFileName)) {
                        fineNameMap.put(innerFileName, fineNameMap.get(innerFileName).intValue() + 1);
                    } else {
                        fineNameMap.put(innerFileName, 2);
                    }
                }
            } catch (Exception e) {
                //todo 记录 异常的文件
                log.error("文件压缩打包异常: [{}], [{}]", e.getMessage(), e);
            }
        }

        if(0 == fileZipNum) {
            throw new ServiceException("无符合的数据");
        }


        //关闭资源
        if (null != zipOutputStream) {
            try {
                zipOutputStream.close();
            } catch (IOException e) {
                log.error("文件IO关闭异常: [{}], [{}]", e.getMessage(), e);
            }
        }
    }
}
