package com.jwipc.sys.logic.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileTypeUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Pair;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.ZipUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.google.common.collect.Lists;
import com.google.common.io.Files;
import com.jwipc.exception.DaoException;
import com.jwipc.prop.SysProp;
import com.jwipc.security.LoginUtil;
import com.jwipc.sys.convert.FileConvert;
import com.jwipc.sys.dto.FileResposeDTO;
import com.jwipc.sys.dto.FileResposePreviewDTO;
import com.jwipc.sys.entity.Attachment;
import com.jwipc.sys.logic.FileLogic;
import com.jwipc.sys.service.IAttachmentService;
import com.jwipc.util.DateUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.*;

import static com.baomidou.mybatisplus.core.toolkit.StringPool.DASH;


/**
 * <p> </p>
 *
 * @author BOGON
 * @version 1.0
 * @since JDK 1.8
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class FileLogicImpl implements FileLogic {
    private final IAttachmentService attachmentService;
    private final SysProp sysProp;
    private final FileConvert fileConvert;

    public String hash(MultipartFile file) {
        if (file == null || file.isEmpty()) {
            return StringUtils.EMPTY;
        }
        try {
            return DigestUtils.md5Hex(file.getInputStream());
        } catch (IOException e) {
            log.error("Get File Hash has error!", e);
            return StringUtils.EMPTY;
        }
    }

    public String hash(File file) {
        if (file == null || file.length() == 0) {
            return StringUtils.EMPTY;
        }
        try {
            return DigestUtils.md5Hex(new FileInputStream(file));
        } catch (IOException e) {
            log.error("Get File Hash has error!", e);
            return StringUtils.EMPTY;
        }
    }

    @Override
    public FileResposeDTO uploadFile(MultipartFile file) {
        // TODO:  允许文件重复上传  jiez
        final String hash = hash(file);
        final Attachment one = null;
//                attachmentService.getOne(new LambdaQueryWrapper<Attachment>().eq(Attachment::getHash, hash), false);
        final Long userId;
        //需要判断是否登录了，如果登录了需要用户id，如果没有登录-1
        if (StpUtil.isLogin()) {
            userId = LoginUtil.getUserId();
        } else {
            userId = -1L;
        }
        if (Objects.nonNull(one)) {
            //如果hash值存在就不用再上传
            final String originalFilename = file.getOriginalFilename();
            one.setId(null);
            one.setFileName(originalFilename);
            one.setCreateUser(userId);
            one.setDateline(LocalDateTime.now());
            attachmentService.save(one);
            return fileConvert.entity2DTO(one);
        } else {
            try {
                final String originalFilename = file.getOriginalFilename();
                final String absoluteDirPath = genDatePath().getKey();
                final String datePath = genDatePath().getValue();
                //需要重新生成文件名
                final String fileSuffix = FileUtil.getSuffix(originalFilename);
                final String fileName = StrUtil.concat(false, UUID.randomUUID().toString(), StrUtil.DOT, fileSuffix);
                final String absoluteFilePath = StrUtil.concat(false, absoluteDirPath, File.separator,
                        fileName);
                final File file1 = new File(absoluteFilePath);
                Files.createParentDirs(file1);
                file.transferTo(file1);
                //需要保存文件到数据库
                Attachment attachment = new Attachment();
                attachment.setFileSize(file.getSize());
                attachment.setFileName(originalFilename);
                attachment.setFileType(FileTypeUtil.getType(file1));
                attachment.setHash(hash);
                //这个地方是相对路径需要把
                attachment.setFilePath(StrUtil.concat(false, datePath, File.separator,
                        fileName));
                attachment.setCreateUser(userId);
                attachment.setDateline(LocalDateTime.now());
                attachmentService.save(attachment);
                return fileConvert.entity2DTO(attachment);
            } catch (IOException e) {
                e.printStackTrace();
                throw new DaoException("文件上传失败");
            }
        }
    }

    @Override
    public FileResposeDTO saveSignFile(File file) {
        final Long userId;
        //需要判断是否登录了，如果登录了需要用户id，如果没有登录-1
        if (StpUtil.isLogin()) {
            userId = LoginUtil.getUserId();
        } else {
            userId = -1L;
        }
        final String hash = hash(file);
        final String originalFilename = file.getName();
        final String datePath = genDatePath().getValue();
        //需要保存文件到数据库
        Attachment attachment = new Attachment();
        attachment.setFileSize(file.length());
        attachment.setHash(hash);
        attachment.setFileName(originalFilename);
        attachment.setFileType(FileTypeUtil.getType(file));
        //这个地方是相对路径需要把
        attachment.setFilePath(StrUtil.concat(false, datePath, File.separator,
                originalFilename));
        attachment.setCreateUser(userId);
        attachment.setDateline(LocalDateTime.now());
        attachmentService.save(attachment);
        return fileConvert.entity2DTO(attachment);
    }


    @Override
    public FileResposeDTO getForDownload(Long fileId) {
        final Attachment byId = attachmentService.getById(fileId);
        final FileResposeDTO fileResposeDTO = getAbPathFileResposeDTO(byId);
        return fileConvert.toClientDto(fileResposeDTO);
    }

    @Override
    public FileResposeDTO getForDownload1(Long fileId) {
        final Attachment byId = attachmentService.getById(fileId);
        final FileResposeDTO fileResposeDTO = getAbPathFileResposeDTO(byId);
        return fileResposeDTO;
    }

    @NotNull
    private FileResposeDTO getAbPathFileResposeDTO(Attachment byId) {
        final FileResposeDTO fileResposeDTO = fileConvert.entity2DTO(byId);
        String filePath;
        if (sysProp.getFilePath().endsWith(File.separator)) {
            filePath = StrUtil.concat(true, sysProp.getFilePath(), fileResposeDTO.getFilePath());
        } else {
            filePath = StrUtil.concat(true, sysProp.getFilePath(), File.separator, fileResposeDTO.getFilePath());
        }
        fileResposeDTO.setFilePath(filePath);
        return fileResposeDTO;
    }

    @Override
    public List<FileResposeDTO> getForDownloads(Collection<? extends Serializable> idList) {
        final List<Attachment> attachments = attachmentService.listByIds(idList);
        if (CollUtil.isNotEmpty(attachments)) {
            List<FileResposeDTO> result = Lists.newArrayList();
            for (Attachment attachment : attachments) {
                final FileResposeDTO abPathFileResposeDTO = getAbPathFileResposeDTO(attachment);
                result.add(abPathFileResposeDTO);
            }
            return result;
        }
        return Collections.emptyList();
    }

    @Override
    public List<FileResposeDTO> uploadFiles(MultipartFile[] files) {
        List<FileResposeDTO> result = Lists.newArrayList();
        for (MultipartFile file : files) {
            result.add(uploadFile(file));
        }
        return result;
    }

    @Override
    public List<FileResposeDTO> getFileListByFileIds(String fileIds) {
        if (StringUtils.isNotEmpty(fileIds)) {
            List<Attachment> attachments = attachmentService.listByIds(Arrays.asList(StringUtils.split(fileIds, ",")));
            return fileConvert.entity2DTOList(attachments);
        }
        return null;
    }

    @Override
    public String zipFiles(String zipFileName, Collection<? extends Serializable> idList) {
        final String datePath = genDatePath().getKey() + File.separator;
        if (!zipFileName.endsWith(".zip")) {

            zipFileName = datePath + zipFileName + ".zip";
        } else {
            zipFileName = datePath + zipFileName;
        }
        //需要新建一个临时目录
        String s = IdWorker.get32UUID().replace(StrUtil.DASHED, StrUtil.EMPTY);
        s = datePath + s;
        FileUtil.mkParentDirs(zipFileName);
        FileUtil.mkdir(s);

        final List<Attachment> attachments = attachmentService.listByIds(idList);
        for (Attachment attachment : attachments) {
            //拷贝文件到新的临时目录，并且重新命名
            FileUtil.copy(getFilePath() + attachment.getFilePath(), s + File.separator + attachment.getFileName(),
                    true);
        }
        try {
            ZipUtil.zip(s, zipFileName, false);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            FileUtil.del(s);
        }
        return zipFileName;
    }

    @Override
    public List<FileResposePreviewDTO> getPreviewUrl(Collection<? extends Serializable> idList)
    {
        if (CollUtil.isEmpty(idList)) {
            return Lists.newArrayList();
        }
        final List<Attachment> attachments = attachmentService.listByIds(idList);
        List<FileResposePreviewDTO> result = fileConvert.entity2DTOList1(attachments);
        //需要拼接预览地址
        for (FileResposePreviewDTO fileResposePreviewDTO : result) {
            String fileName = fileResposePreviewDTO.getFileName();
            fileName = fileName.replaceAll("[^\\w\\-.\\u4e00-\\u9fa5]", "");
            //需要把文件名的后缀取到
            final String suffix = FileUtil.getSuffix(fileName);
            fileName = fileResposePreviewDTO.getId() + StrUtil.DOT + suffix;
            final String format = StrUtil.format("{}/pc/sys/file/rest/download/{}?fullfilename={}",
                    sysProp.getWebsiteUrl(), fileResposePreviewDTO.getId(), fileName);
            fileResposePreviewDTO.setDownloadUrl(format);
            final String encodeToString = Base64.getEncoder().encodeToString(format.getBytes());
            final String encode;
            try {
                encode = URLEncoder.encode(encodeToString, StandardCharsets.UTF_8.toString());
            } catch (UnsupportedEncodingException e) {
                throw new RuntimeException(e);
            }
            fileResposePreviewDTO.setPreviewUrl(StrUtil.format("{}?url={}", sysProp.getPreviewUrl(), encode));
        }
        return result;
    }

    @Override
    public Pair<String, String> genDatePath() {
        final String yyyyMMdd = DateUtils.formatYYYYMMDDDash(new Date());
        final String datePath = StringUtils.replace(yyyyMMdd, DASH, File.separator);
        return Pair.of(getFilePath() + datePath, datePath);
    }

    @Override
    public Pair<String, String> genBarDatePath() {
        final String yyyyMMdd = DateUtils.formatYYYYMMDDDash(new Date());
        final String datePath = StringUtils.replace(yyyyMMdd, DASH, File.separator);
        return Pair.of(getFilePath() + "bar" + File.separator + datePath, datePath);
    }

    /**
     * 创建一个空文件
     *
     * @param fileName 文件名称
     * @return 文件
     */
    @Override
    public File createFile(String fileName) {
        File file = new File(StrUtil.concat(false, genDatePath().getKey(), File.separator, fileName));
        try {
            Files.createParentDirs(file);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return file;
    }

    @Override
    public File createBarFile(String fileName) {
        File file = new File(StrUtil.concat(false, genBarDatePath().getKey(), File.separator, fileName));
        try {
            Files.createParentDirs(file);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return file;
    }


    /**
     * 把系统配置的路径变成有/结尾的
     *
     * @return 修改后的路径
     */
    @Override
    public String getFilePath() {
        if (sysProp.getFilePath().endsWith(File.separator)) {
            return sysProp.getFilePath();
        } else {
            return sysProp.getFilePath() + File.separator;
        }
    }

    @Override
    public void deleteFile(List<Long> fileIds) {
        LambdaUpdateWrapper<Attachment> updateWrapper = new LambdaUpdateWrapper<Attachment>()
                .set(Attachment::getDeleteFlag, "1")
                .in(Attachment::getId, fileIds);
        attachmentService.update(updateWrapper);
    }
}