package io.bisq.common.storage;

import com.google.common.io.Files;
import io.bisq.common.util.Utilities;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

public class FileUtil {
    private static final Logger log = LoggerFactory.getLogger(FileUtil.class);

    public static void rollingBackup(File dir, String fileName, int numMaxBackupFiles) {
        if (dir.exists()) {
            File backupDir = new File(Paths.get(dir.getAbsolutePath(), "backup").toString());
            if (!backupDir.exists())
                if (!backupDir.mkdir())
                    log.warn("make dir failed.\nBackupDir=" + backupDir.getAbsolutePath());

            File origFile = new File(Paths.get(dir.getAbsolutePath(), fileName).toString());
            if (origFile.exists()) {
                String dirName = "backups_" + fileName;
                if (dirName.contains("."))
                    dirName = dirName.replace(".", "_");
                File backupFileDir = new File(Paths.get(backupDir.getAbsolutePath(), dirName).toString());
                if (!backupFileDir.exists())
                    if (!backupFileDir.mkdir())
                        log.warn("make backupFileDir failed.\nBackupFileDir=" + backupFileDir.getAbsolutePath());

                File backupFile = new File(Paths.get(backupFileDir.getAbsolutePath(), new Date().getTime() + "_" + fileName).toString());

                try {
                    Files.copy(origFile, backupFile);

                    pruneBackup(backupFileDir, numMaxBackupFiles);
                } catch (IOException e) {
                    log.error("Backup key failed: " + e.getMessage());
                    e.printStackTrace();
                }
            }
        }
    }

    private static void pruneBackup(File backupDir, int numMaxBackupFiles) {
        if (backupDir.isDirectory()) {
            File[] files = backupDir.listFiles();
            if (files != null) {
                List<File> filesList = Arrays.asList(files);
                if (filesList.size() > numMaxBackupFiles) {
                    filesList.sort((o1, o2) -> o1.getName().compareTo(o2.getName()));
                    File file = filesList.get(0);
                    if (file.isFile()) {
                        if (!file.delete())
                            log.error("Failed to delete file: " + file);
                        else
                            pruneBackup(backupDir, numMaxBackupFiles);

                    } else {
                        pruneBackup(new File(Paths.get(backupDir.getAbsolutePath(), file.getName()).toString()), numMaxBackupFiles);
                    }
                }
            }
        }
    }

    public static void deleteDirectory(File file) throws IOException {
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            if (files != null)
                for (File c : files)
                    deleteDirectory(c);
        }
        deleteFileIfExists(file);
    }

    public static void deleteDirectory(File file, File exclude) throws IOException {
        boolean excludeFileFound = false;
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            if (files != null)
                for (File f : files) {
                    if (!excludeFileFound)
                        excludeFileFound = f.equals(exclude);
                    if (!f.equals(exclude))
                        deleteDirectory(f);
                }
        }
        // Finally delete main file/dir if exclude file was not found in directory
        if (!excludeFileFound && !file.equals(exclude))
            deleteFileIfExists(file);
    }

    public static void deleteFileIfExists(File file) throws IOException {
        if (file.exists() && !file.delete())
            throw new FileNotFoundException("Failed to delete file: " + file);
    }

    public static void resourceToFile(String resourcePath, File destinationFile) throws ResourceNotFoundException, IOException {
        InputStream inputStream = ClassLoader.getSystemClassLoader().getResourceAsStream(resourcePath);
        if (inputStream == null)
            throw new ResourceNotFoundException(resourcePath);

        try (FileOutputStream fileOutputStream = new FileOutputStream(destinationFile)) {
            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                fileOutputStream.write(buffer, 0, bytesRead);
            }
        }
    }

    public static void renameFile(File oldFile, File newFile) throws IOException {
        if (Utilities.isWindows()) {
            // Work around an issue on Windows whereby you can't rename over existing files.
            final File canonical = newFile.getCanonicalFile();
            if (canonical.exists() && !canonical.delete()) {
                throw new IOException("Failed to delete canonical file for replacement with save");
            }
            if (!oldFile.renameTo(canonical)) {
                throw new IOException("Failed to rename " + oldFile + " to " + canonical);
            }
        } else if (!oldFile.renameTo(newFile)) {
            throw new IOException("Failed to rename " + oldFile + " to " + newFile);
        }
    }

    public static void copyDirectory(File source, File destination) throws IOException {
        FileUtils.copyDirectory(source, destination);
    }
}

@@ -216,9 +216,101 @@
 .io.
-*
+File;%0Aimport java.io.FileOutputStream;%0Aimport java.io.IOException;%0Aimport java.io.InputStream
 ;%0Aim
@@ -3999,24 +3999,156 @@
         
-if (file
+try %7B%0A            if (Utilities.isWindows()) %7B%0A                final File canonical = file.getCanonicalFile();%0A                if (canonical
 .exists(
@@ -4153,20 +4153,25 @@
 s() && !
-file
+canonical
 .delete(
@@ -4164,33 +4164,37 @@
 onical.delete())
-%0A
+ %7B%0A  
             thro
@@ -4181,32 +4181,38 @@
 %7B%0A              
+      
 throw new FileNo
@@ -4209,64 +4209,359 @@
 new 
-FileNotFoundException(%22Failed to delete file: %22 + file);
+IOException(%22Failed to delete canonical file%22);%0A                %7D%0A            %7D else if (file.exists() && !file.delete()) %7B%0A                throw new IOException(%22Failed to delete file%22);%0A            %7D%0A        %7D catch (Throwable t) %7B%0A            log.error(t.toString());%0A            t.printStackTrace();%0A            throw new IOException(t);%0A        %7D
 %0A   

Add handling for canonical at Windows at file delete.