/*
 * Copyright (c) 2022 MiLaboratories Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.milaboratory.util;

import org.apache.commons.io.FileUtils;

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

public interface TempFileDest {
    /**
     * Resolves the path as file given the string name, and registers it for deletion on JVM termination
     * (if was set at creation).
     */
    default File resolveFile(String name) {
        return resolvePath(name).toFile();
    }

    /**
     * Resolves the path given the string name, and registers it for deletion on JVM termination
     * (if was set at creation).
     */
    Path resolvePath(String name);

    /**
     * Returns another TempFileDest with additional string that will be added to the right of the prefix added to each
     * file returned by the resolveFile / resolvePath metods.
     */
    TempFileDest addSuffix(String suffix);

    /**
     * Deletes all the temp data that might be left from the previous run with the same parameters.
     * Ensures the folder exist if this object represents the temp folder.
     */
    void reset();

    /** Deletes all the data this object represents. */
    void delete();

    static TempFileDest folder(File folder, boolean registerForDeletion) {
        return folder(folder.toPath(), false);
    }

    static TempFileDest folder(Path path, boolean registerForDeletion) {
        Folder folder = new Folder(path, registerForDeletion);
        folder.reset();
        return folder;
    }

    static TempFileDest filesWithPrefix(Path path, String prefix, boolean registerForDeletion) {
        FilesWithPrefix folder = new FilesWithPrefix(path, prefix, registerForDeletion);
        folder.reset();
        return folder;
    }

    class Folder implements TempFileDest {
        private final Path path;
        private final boolean registerForDeletion;

        Folder(Path path, boolean registerForDeletion) {
            Objects.requireNonNull(path);
            this.path = path;
            this.registerForDeletion = registerForDeletion;
        }

        @Override
        public Path resolvePath(String name) {
            Path file = path.resolve(name);
            TempFileManager.register(path.toFile());
            return file;
        }

        @Override
        public TempFileDest addSuffix(String suffix) {
            return new FilesWithPrefix(path, suffix, registerForDeletion);
        }

        @Override
        public void reset() {
            try {
                FileUtils.deleteDirectory(path.toFile());
                Files.createDirectory(path);
                if (registerForDeletion)
                    TempFileManager.register(path.toFile());
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }

        @Override
        public void delete() {
            try {
                FileUtils.deleteDirectory(path.toFile());
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    class FilesWithPrefix implements TempFileDest {
        private final Path path;
        private final String prefix;
        private final boolean registerForDeletion;

        FilesWithPrefix(Path path, String prefix, boolean registerForDeletion) {
            Objects.requireNonNull(path);
            Objects.requireNonNull(prefix);
            if (!Files.isDirectory(path))
                throw new IllegalArgumentException("Path must be a folder: " + path);
            this.path = path;
            this.prefix = prefix;
            this.registerForDeletion = registerForDeletion;
        }

        public Path resolvePath(String name) {
            Path result = path.resolve(prefix + name);
            if (registerForDeletion)
                TempFileManager.register(result.toFile());
            return result;
        }

        @Override
        public TempFileDest addSuffix(String suffix) {
            return new FilesWithPrefix(path, prefix + suffix, registerForDeletion);
        }

        @Override
        public void reset() {
            delete();
        }

        @Override
        public void delete() {
            try (DirectoryStream<Path> st = Files.newDirectoryStream(path,
                    p -> p.getFileName().toString().startsWith(prefix))) {
                for (Path p : st)
                    if (Files.isDirectory(p))
                        FileUtils.deleteDirectory(p.toFile());
                    else
                        Files.delete(p);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
