package com.agentdid127.resourcepack.library.utilities;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Comparator;
import java.util.Objects;

public final class FileUtil {
    public static void moveIfExists(Path in, Path out) throws IOException {
        if (in.toFile().exists()) {
            ensureParentExists(out);
            Files.move(in, out);
        }
    }

    public static void ensureParentExists(Path path) {
        Path parentPath = path.getParent();
        if (!parentPath.toFile().exists()) {
            parentPath.toFile().mkdirs();
        }
    }

    /**
     * Copies Directory
     *
     * @param src  directory Source
     * @param dest Directory Destination
     * @throws IOException
     */
    public static void copyDirectory(Path src, Path dest) throws IOException {
        Files.walk(src).forEach(path -> {
            try {
                if (dest.resolve(src.relativize(path)).toFile().exists())
                    Files.delete(dest.resolve(src.relativize(path)));
                Files.copy(path, dest.resolve(src.relativize(path)));
            } catch (Throwable e) {
                throw Util.propagate(e);
            }
        });
    }

    /**
     * Deletes full directory
     *
     * @param dirPath Path of Directory to delete
     * @throws IOException
     */
    public static void deleteDirectoryAndContents(Path dirPath) throws IOException {
        if (dirPath.toFile().exists()) {
            // noinspection ResultOfMethodCallIgnored
            Files.walk(dirPath).sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(file -> {
                file.delete();
            });
        }
    }

    /**
     * If file exists, return file with correct casing.
     *
     * @param path
     * @return
     * @throws IOException
     */
    public static boolean fileExistsCorrectCasing(Path path) throws IOException {
        return path.toFile().exists() && path.toString().equals(path.toFile().getCanonicalPath());
    }

    /**
     * @return null if file doesn't exist, {@code true} if successfully renamed,
     *         {@code false} if failed
     */
    public static Boolean renameFile(Path file, Path newFile) {
        if (!file.toFile().exists()) {
            return null;
        }
        try {
            Files.move(file, newFile);
            return true;
        } catch (IOException e) {
            return false;
        }
    }

    /**
     * @return null if file doesn't exist, {@code true} if successfully renamed,
     *         {@code false} if failed
     */
    public static Boolean renameFile(Path file, String newName) {
        if (!file.toFile().exists()) {
            return null;
        }
        try {
            Files.move(file, file.getParent().resolve(newName));
            return true;
        } catch (IOException e) {
            return false;
        }
    }

    /**
     * Takes dir2 and merges it into dir1
     *
     * @param dir1
     * @param dir2
     */
    public static void mergeDirectories(File dir1, File dir2) throws IOException {
        if (!dir1.exists() && !dir2.exists()) {
            return;
        }

        String targetDirPath = dir1.getAbsolutePath();
        File[] files = dir2.listFiles();
        assert files != null;
        for (File file : files) {
            if (file.isDirectory()) {
                Logger.debug(targetDirPath + File.separator + file.getName());
                File file3 = new File(targetDirPath + File.separator + file.getName());
                file3.mkdirs();
                Logger.debug("Created" + file3.getName());
                mergeDirectories(file3, file);
            } else {
                Logger.debug(targetDirPath + File.separator + file.getName());
                file.renameTo(new File(targetDirPath + File.separator + file.getName()));
            }
        }

        if (Objects.requireNonNull(dir2.list()).length == 0) {
            Files.delete(dir2.toPath());
        }
    }
}