package kr.xit.core.support.utils;

import static java.nio.file.Files.probeContentType;

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import kr.xit.core.exception.BizRuntimeException;

import java.io.*;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItem;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.commons.CommonsMultipartFile;

/**
 * <pre>
 * description : File Utils
 *
 * packageName : kr.xit.core.support.utils
 * fileName    : FileUtil
 * author      : limju
 * date        : 2023-07-20
 * ======================================================================
 * 변경일         변경자        변경 내용
 * ----------------------------------------------------------------------
 * 2023-07-20    limju       최초 생성
 *
 * </pre>
 */
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class FileUtil {

    public static byte[] getFileBytesFrom(String path) {

        // 파일 객체 생성
        File file = new File(path);
        byte[] fileBytes = new byte[(int)file.length()];

        try(FileInputStream fis = new FileInputStream(file);){
            fis.read(fileBytes);

        } catch (IOException e) {
            throw BizRuntimeException.create(e.getMessage());
        }
        return fileBytes;
    }

    public static void saveFile(String path, String fileName, byte[] fileData) {
        Path p = Paths.get(path);

        if(!Files.isDirectory(p)){
            try {
                Files.createDirectory(p);
            } catch (IOException e) {
                throw BizRuntimeException.create(e.getMessage());
            }
        }
        try(FileOutputStream fos = new FileOutputStream(path + "/" + fileName);){
            fos.write(fileData);
        } catch (IOException e) {
            throw BizRuntimeException.create(e.getMessage());
        }
    }

    /**
     * 파일명 + 파일경로를 받아 MultipartFile 생성 return
     * @param fileNm
     * @param filePath
     * @return MultipartFile
     */
    @SuppressWarnings("DuplicatedCode")
    public static MultipartFile createMutipartFile(final String fileNm, final String filePath){
        File file = new File(filePath+"/"+fileNm);
        try {
            FileItem fileItem = new DiskFileItem(fileNm, probeContentType(file.toPath()), false, file.getName(), (int) file.length(), file.getParentFile());

            try(InputStream input = Files.newInputStream(file.toPath());
                OutputStream os = fileItem.getOutputStream()) {
                IOUtils.copy(input, os);
            }catch(IOException e){
                throw BizRuntimeException.create(e.getMessage());
            }
            return new CommonsMultipartFile(fileItem);

        } catch (IOException ex) {
            throw BizRuntimeException.create(ex.getMessage());
        }
    }

    /**
     * binary(byte) 파일객체 에서 MultipartFile 객체 GET
     * @param fileNm 원본파일 명
     * @param filePath 업로드 경로 - MultipartFile 객체 생성을 위한 임시 경로
     * @param data byte 파일 객체
     * @param isFileDelete 임시 파일 삭제 여부
     * @return MultipartFile 생성된 MultipartFile
     */
    @SuppressWarnings("DuplicatedCode")
    public static MultipartFile createMutipartFileFromBytes(final String fileNm, final String filePath, final byte[] data, boolean isFileDelete){
        File file = new File(filePath+"/"+fileNm);
        try {
            FileUtils.writeByteArrayToFile(file, data);

            FileItem fileItem = new DiskFileItem(fileNm, probeContentType(file.toPath()), false, file.getName(), (int) file.length(), file.getParentFile());

            try(InputStream input = Files.newInputStream(file.toPath());
                OutputStream os = fileItem.getOutputStream()) {
                IOUtils.copy(input, os);
            }catch(IOException e){
                throw BizRuntimeException.create(e.getMessage());
            }
            if(isFileDelete && file.exists()) {
                //noinspection ResultOfMethodCallIgnored
                file.delete();
            }
            return new CommonsMultipartFile(fileItem);
        } catch (IOException ex) {
            throw BizRuntimeException.create(ex.getMessage());
        }
    }

    /**
     * zip 파일 생성
     * @param filePath source file path
     * @param fileNameList source file name list
     * @param zipFilePath zip file folder
     * @param zipFileName zip file name
     */
    public static String compressZip(final String filePath, final List<String> fileNameList, final String zipFilePath, final String zipFileName) {
        List<File> fileList = new ArrayList<>();

        fileNameList.forEach(s -> fileList.add(new File(filePath, s)));
        return compressZip(fileList, zipFilePath, zipFileName);
    }

    /**
     * Zip 파일 생성
     * @param fileList zip file name list
     * @param zipFilePath zip file folder
     * @param zipFileName zip file name
     */
    public static String compressZip(final List<File> fileList, final String zipFilePath, final String zipFileName) {
        File zipFile = new File(zipFilePath, zipFileName);

        byte[] buff = new byte[4096];
        // 압축파일 생성
        try(ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipFile))){

            // 파일 압축
            fileList.forEach(f -> {
                try(FileInputStream fis = new FileInputStream(f)){
                    // 압축파일 지정
                    ZipEntry ze = new ZipEntry(f.getName());
                    zos.putNextEntry(ze);

                    // 압축 파일에 추가
                    int len;
                    while ((len = fis.read(buff)) > 0)  zos.write(buff, 0, len);

                    zos.closeEntry();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }

            });
            return zipFilePath + "/" + zipFileName;

        } catch (FileNotFoundException e) {
            log.error("zip파일 생성 오류::{}", e.getLocalizedMessage());
            throw BizRuntimeException.create("zip 파일생성 오류[대상파일 미존재]");
        } catch (IOException e) {
            log.error("zip파일 생성 오류::{}", e.getLocalizedMessage());
            throw BizRuntimeException.create("zip 파일생성 오류[대상파일 미존재]");
        }
    }

    /**
     * <pre>
     * path 경로의 모든 파일 삭제(directory 포함)
     * @param path String 삭제할 경로
     * </pre>
     */
    public static void removeDirectyAndFiles(String path){
        try {
            // 작업 파일 삭제
            Path dir = Paths.get(path);
            Files.walk(dir) // Traverse the file tree in depth-first order
                .sorted(Comparator.reverseOrder())
                .forEach(f -> {
                    try {
                        Files.delete(f);  //delete each file or directory
                    } catch (IOException e) {
                        log.error(e.getLocalizedMessage());
                        e.printStackTrace();
                    }
                });
        }catch (IOException ie){
            log.error(ie.getLocalizedMessage());
            ie.printStackTrace();
        }
    }
}
