package com.dsk.common.utils.file;

import com.dsk.common.config.ShuZhiHuaConfig;
import com.dsk.common.core.domain.entity.BusinessFileVo;
import com.dsk.common.exception.base.BaseException;
import com.dsk.common.utils.DateUtils;
import com.dsk.common.utils.ServletUtils;
import com.dsk.common.utils.StringUtils;
import com.dsk.common.utils.uuid.IdUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;

/**
 * 文件处理工具类
 *
 * @author dsk
 */
@Slf4j
public class FileUtils
{
    public static String FILENAME_PATTERN = "[a-zA-Z0-9_\\-\\|\\.\\u4e00-\\u9fa5]+";

    /**
     * 检查目录是否存在，如果不存在，则创建目录，如果创建失败则返回false
     *
     * @param path 文件夹路径
     * @return boolean
     */
    public static boolean newFolder(String path) {
        File file = new File(path);
        if (!file.exists()) {
            boolean isSuccess = file.mkdir();
            if (!isSuccess)
                if (!file.exists()) {
                    file.mkdirs();
                }
            return isSuccess;
        } else {
            return true;
        }
    }

    /**
     *
     *
     * @param folderPath 文件夹路径
     * @return 文件夹路径
     */
    public static String newFolder1(String folderPath) {
        String filePath = folderPath;
        try {
            File myFilePath = new File(filePath);
            if (!myFilePath.exists()) {
                myFilePath.mkdirs();
            }
        } catch (Exception e) {
            log.error("创建文件夹失败");
            filePath = "";
            e.printStackTrace();
        }
        return filePath;
    }

    /**
     * 输出指定文件的byte数组
     *
     * @param filePath 文件路径
     * @param os 输出流
     * @return
     */
    public static void writeBytes(String filePath, OutputStream os) throws IOException
    {
        FileInputStream fis = null;
        try
        {
            File file = new File(filePath);
            if (!file.exists())
            {
                throw new FileNotFoundException(filePath);
            }
            fis = new FileInputStream(file);
            byte[] b = new byte[1024];
            int length;
            while ((length = fis.read(b)) > 0)
            {
                os.write(b, 0, length);
            }
        }
        catch (IOException e)
        {
            throw e;
        }
        finally
        {
            IOUtils.close(os);
            IOUtils.close(fis);
        }
    }

    /**
     * 写数据到文件中
     *
     * @param data 数据
     * @return 目标文件
     * @throws IOException IO异常
     */
    public static String writeImportBytes(byte[] data) throws IOException
    {
        return writeBytes(data, ShuZhiHuaConfig.getImportPath());
    }

    /**
     * 写数据到文件中
     *
     * @param data 数据
     * @param uploadDir 目标文件
     * @return 目标文件
     * @throws IOException IO异常
     */
    public static String writeBytes(byte[] data, String uploadDir) throws IOException
    {
        FileOutputStream fos = null;
        String pathName = "";
        try
        {
            String extension = getFileExtendName(data);
            pathName = DateUtils.datePath() + "/" + IdUtils.fastUUID() + "." + extension;
            File file = FileUploadUtils.getAbsoluteFile(uploadDir, pathName);
            fos = new FileOutputStream(file);
            fos.write(data);
        }
        finally
        {
            IOUtils.close(fos);
        }
        return FileUploadUtils.getPathFileName(uploadDir, pathName);
    }

/**
     * 删除文件
     *
     * @param filePath 文件
     * @return
     */
    public static boolean deleteFile(String filePath)
    {
        boolean flag = false;
        File file = new File(filePath);
        // 路径为文件且不为空则进行删除
        if (file.isFile() && file.exists())
        {
            flag = file.delete();
        }
        return flag;
    }
    /**
     * 删除整个文件夹或者文某个文件
     *
     * @param filePath 文件
     * @return
     */
    public static boolean delFolder(String filePath) {
        try {
            delAllFile(filePath); // 删除里面的所有文件
            File file = new File(filePath);
             return file.delete(); // 删除空文件夹
        } catch (Exception e) {
            throw new BaseException("删除文件夹失败",e.getMessage());
        }
    }

    /**
     * 文件夹过滤
     *
     * @param path 文件夹路径
     */
    public static void delAllFile(String path) {
        String hint = "这是一个根目录，请更换目录!";
        File file = new File(path);
        if (!file.exists()) {
            throw new BaseException("文件不存在");
        }
//        if (!file.isDirectory()) {
//            return false;
//        }
        if (file.getAbsolutePath().equalsIgnoreCase("/")) {
            throw new BaseException(hint);
        }
        if (file.getAbsolutePath().equalsIgnoreCase("/root")) {
            throw new BaseException(hint);
        }
        if (file.getAbsolutePath().equalsIgnoreCase("/usr") || file.getAbsolutePath().equalsIgnoreCase("/opt")
                || file.getAbsolutePath().equalsIgnoreCase("/bin") || file.getAbsolutePath().equalsIgnoreCase("/sbin")
                || file.getAbsolutePath().equalsIgnoreCase("/etc") || file.getAbsolutePath().equalsIgnoreCase("/selinux")
                || file.getAbsolutePath().equalsIgnoreCase("/sys") || file.getAbsolutePath().equalsIgnoreCase("/var")
                || file.getAbsolutePath().equalsIgnoreCase("/home") || file.getAbsolutePath().equalsIgnoreCase("/net")) {
            throw new BaseException(hint);
        }
        if (file.getAbsolutePath().equalsIgnoreCase("C://") || file.getAbsolutePath().equalsIgnoreCase("C:\\\\")) {
            throw new BaseException(hint);
        }
        String[] tempList = file.list();
        File temp;
        if (tempList == null) {
            return;
        }
        for (String aTempList : tempList) {
            if (path.endsWith(File.separator)) {
                temp = new File(path + aTempList);
            } else {
                temp = new File(path + File.separator + aTempList);
            }
            if (temp.isFile()) {
                temp.delete();
            }
            if (temp.isDirectory()) {
                delAllFile(path + "/" + aTempList);// 删除里面的所有文件
                delFolder(path + "/" + aTempList);// 删除空文件夹
            }
        }
    }


    /**
     * 获取文件夹中所有文件
     * @param filePath 全路径
     * @return
     */
    public static List<BusinessFileVo> getAllFiles(String filePath) {
        List<BusinessFileVo> fileList = new ArrayList<>();
        File file = new File(filePath);

        if (!file.exists()) {
            return fileList;
        }
        if (!file.isDirectory()) {
            return fileList;
        }

        // 获取当前目录下的文件和文件夹
        File[] files = file.listFiles();

        if (files != null) {
            for (File directory : files) {
                // 如果是文件夹则递归调用此方法
                if (directory.isDirectory()) {
                    fileList.add(new BusinessFileVo(directory.getPath(),new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(directory.lastModified())));
                    getAllFiles(directory.getPath());
                } else {
                    // 如果是文件则直接输出路径
                    fileList.add(new BusinessFileVo(directory.getPath(),new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(directory.lastModified())));
                }
            }
            return fileList;
        }
        return fileList;
    }

    /**
     * 获取文件夹中的所有文件,不包括文件夹
     *
     * @param path 文件夹路径
     * @return List<File>
     */
    public static List<BusinessFileVo> getAllFileNames(String path) {
        List<BusinessFileVo> fileList = new ArrayList<>();
        File file = new File(path);
        if (!file.exists()) {
            return fileList;
        }
        if (!file.isDirectory()) {
            return fileList;
        }
        String[] tempList = file.list();
        File tempFile;
        for (String fileName : tempList) {
            if (path.endsWith(File.separator)) {
                tempFile = new File(path + fileName);
            } else {
                tempFile = new File(path + File.separator + fileName);
            }
            if (tempFile.isFile()) {
                fileList.add(new BusinessFileVo(tempFile.getPath(),new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(tempFile.lastModified())));
            }
            if (tempFile.isDirectory()) {
                List<BusinessFileVo> allFiles = getAllFileNames(tempFile.getAbsolutePath());
                fileList.addAll(allFiles);
            }
        }
        return fileList;
    }

    /**
     * 根据文件路径下载文件
     * @param filePath 要下载的文件路径
     * @param response 返回的响应
     */
    public static void downloadByFilePath(String filePath, HttpServletResponse response) {
        try {
            // path是指想要下载的文件的路径
            File file = new File(filePath);
//            log.info(file.getPath());
            if (!file.exists()) throw new BaseException("文件不存在!");
            // 获取文件名
            String filename = file.getName();
//            log.info("文件名: "+filename);
            // 获取文件后缀名
//            String ext = filename.substring(filename.lastIndexOf(".") + 1).toLowerCase();
//            log.info("文件后缀名：" + ext);

            // 将文件写入输入流
            FileInputStream fileInputStream = new FileInputStream(file);
            InputStream fis = new BufferedInputStream(fileInputStream);
            byte[] buffer = new byte[fis.available()];
            fis.read(buffer);

            // 清空response
            response.reset();
            // 设置response的Header
            response.setCharacterEncoding("UTF-8");
            //文件类型
            response.setContentType("application/octet-stream");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));
            //文件名称
            response.addHeader("Download-filename", URLEncoder.encode(filename, "UTF-8"));
            // 告知浏览器文件的大小
            response.addHeader("Content-Length", "" + file.length());

            OutputStream outputStream = new BufferedOutputStream(response.getOutputStream());

            outputStream.write(buffer);
            fis.close();
            outputStream.flush();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

    /**
     * 根据文件url下载文件
     * @param filePath 要下载的文件链接
     * @param response 响应体
     * @return
     * @throws IOException
     */
    public static void downloadByUrl(String filePath, HttpServletResponse response){
        //        String fileUrl = request.getParameter("fileUrl"); // 获取文件链接

        try {
            URL url = new URL(filePath);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("GET");

            // 设置响应头，告诉浏览器下载文件
            String contentType = connection.getContentType(); // 获取文件类型
            response.setContentType(contentType);
            String filename = extractFilenameFromUrl(filePath); // 从链接中提取文件名
            response.setHeader("Content-Disposition", "attachment; filename=" + filename + "");

            // 将文件流写入响应输出流
            InputStream inputStream = connection.getInputStream();
            byte[] buffer = new byte[4096];
            int bytesRead = -1;
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                response.getOutputStream().write(buffer, 0, bytesRead);
            }
            inputStream.close();

            connection.disconnect();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 从文件链接中提取文件名
    public static String extractFilenameFromUrl(String url) {
        int slashIndex = url.lastIndexOf('/');
        int dotIndex = url.lastIndexOf('.');
        if (dotIndex == -1 || dotIndex < slashIndex) {
            return "download";
        }
        return url.substring(slashIndex + 1);
    }

    /**
     * 上传文件
     * @param url 上传链接
     * @param folderPath 文件路径
     * @return
     * @throws IOException
     */
    public static boolean uploadFolder(String url, String folderPath) throws IOException {
        File folder = new File(folderPath);
        if (!folder.exists() || !folder.isDirectory()) {
            throw new IllegalArgumentException("文件夹路径无效: " + folderPath);
        }

        List<File> files = new ArrayList<>();
        listFiles(folder, files);

        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            HttpPost httpPost = new HttpPost(url);
            MultipartEntityBuilder builder = MultipartEntityBuilder.create();
            for (File file : files) {
                FileBody fileBody = new FileBody(file, ContentType.DEFAULT_BINARY);
                builder.addPart("file", fileBody);
            }
            HttpEntity multipart = builder.build();
            httpPost.setEntity(multipart);
            HttpResponse response = httpClient.execute(httpPost);
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode != 200) {
                throw new RuntimeException("上传文件夹失败: " + response.getStatusLine().getReasonPhrase());
            }
        }
        return true;
    }

    private static void listFiles(File folder, List<File> files) {
        File[] subFiles = folder.listFiles();
        for (File subFile : subFiles) {
            if (subFile.isDirectory()) {
                listFiles(subFile, files);
            } else {
                files.add(subFile);
            }
        }
    }


    /**
     * 文件名称验证
     *
     * @param filename 文件名称
     * @return true 正常 false 非法
     */
    public static boolean isValidFilename(String filename)
    {
        return filename.matches(FILENAME_PATTERN);
    }

    /**
     * 检查文件是否可下载
     *
     * @param resource 需要下载的文件
     * @return true 正常 false 非法
     */
    public static boolean checkAllowDownload(String resource)
    {
        // 禁止目录上跳级别
        if (StringUtils.contains(resource, ".."))
        {
            return false;
        }

        // 检查允许下载的文件规则
        if (ArrayUtils.contains(MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION, FileTypeUtils.getFileType(resource)))
        {
            return true;
        }

        // 不在允许下载的文件规则
        return false;
    }

    /**
     * 下载文件名重新编码
     *
     * @param request 请求对象
     * @param fileName 文件名
     * @return 编码后的文件名
     */
    public static String setFileDownloadHeader(HttpServletRequest request, String fileName) throws UnsupportedEncodingException
    {
        final String agent = request.getHeader("USER-AGENT");
        String filename = fileName;
        if (agent.contains("MSIE"))
        {
            // IE浏览器
            filename = URLEncoder.encode(filename, "utf-8");
            filename = filename.replace("+", " ");
        }
        else if (agent.contains("Firefox"))
        {
            // 火狐浏览器
            filename = new String(fileName.getBytes(), "ISO8859-1");
        }
        else if (agent.contains("Chrome"))
        {
            // google浏览器
            filename = URLEncoder.encode(filename, "utf-8");
        }
        else
        {
            // 其它浏览器
            filename = URLEncoder.encode(filename, "utf-8");
        }
        return filename;
    }

    /**
     * 下载文件名重新编码
     *
     * @param response 响应对象
     * @param realFileName 真实文件名
     */
    public static void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) throws UnsupportedEncodingException
    {
        String percentEncodedFileName = percentEncode(realFileName);

        StringBuilder contentDispositionValue = new StringBuilder();
        contentDispositionValue.append("attachment; filename=")
                .append(percentEncodedFileName)
                .append(";")
                .append("filename*=")
                .append("utf-8''")
                .append(percentEncodedFileName);

        response.addHeader("Access-Control-Expose-Headers", "Content-Disposition,download-filename");
        response.setHeader("Content-disposition", contentDispositionValue.toString());
        response.setHeader("download-filename", percentEncodedFileName);
    }

    /**
     * 百分号编码工具方法
     *
     * @param s 需要百分号编码的字符串
     * @return 百分号编码后的字符串
     */
    public static String percentEncode(String s) throws UnsupportedEncodingException
    {
        String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString());
        return encode.replaceAll("\\+", "%20");
    }

    /**
     * 获取图像后缀
     *
     * @param photoByte 图像数据
     * @return 后缀名
     */
    public static String getFileExtendName(byte[] photoByte)
    {
        String strFileExtendName = "jpg";
        if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56)
                && ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97))
        {
            strFileExtendName = "gif";
        }
        else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70))
        {
            strFileExtendName = "jpg";
        }
        else if ((photoByte[0] == 66) && (photoByte[1] == 77))
        {
            strFileExtendName = "bmp";
        }
        else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71))
        {
            strFileExtendName = "png";
        }
        return strFileExtendName;
    }

    /**
     * 获取文件名称 /profile/upload/2022/04/16/dsk.png -- dsk.png
     *
     * @param fileName 路径名称
     * @return 没有文件路径的名称
     */
    public static String getName(String fileName)
    {
        if (fileName == null)
        {
            return null;
        }
        int lastUnixPos = fileName.lastIndexOf('/');
        int lastWindowsPos = fileName.lastIndexOf('\\');
        int index = Math.max(lastUnixPos, lastWindowsPos);
        return fileName.substring(index + 1);
    }

    /**
     * 获取不带后缀文件名称 /profile/upload/2022/04/16/dsk.png -- dsk
     *
     * @param fileName 路径名称
     * @return 没有文件路径和后缀的名称
     */
    public static String getNameNotSuffix(String fileName)
    {
        if (fileName == null)
        {
            return null;
        }
        String baseName = FilenameUtils.getBaseName(fileName);
        return baseName;
    }

    /**
     * 获取本地服务的域名，端口
     * @return
     */
    public static String getUrl()
    {
        HttpServletRequest request = ServletUtils.getRequest();
        StringBuffer url = request.getRequestURL();
        String contextPath = request.getServletContext().getContextPath();
        return url.delete(url.length() - request.getRequestURI().length(), url.length()).append(contextPath).toString();

    }
}
