package com.atlassian.bitbucket.mesh.io;

import javax.annotation.Nonnull;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;

/**
 * Callback used by {@link MoreFiles#syncFileTree(SyncFileTreeParameters)}
 */
public interface SyncFileTreeCallback {

    /**
     * Callback method that is invoked while walking the <em>destination</em> file tree when checking for files that
     * exist in the destination tree, but are {@link SyncFileTreeParameters#isDetectMissing() missing} in the source
     * tree. This method is called for both the leaf nodes (the files to be synced) and the intermediate directories.
     * Returning {@code false} for a directory means that the whole subtree is skipped.
     *
     * @param destination the destination path
     * @param destAttributes the attributes
     * @return {@code true} if {@code path} should be included in the sync operation, otherwise {@code false}
     */
    default boolean isDestinationIncluded(@Nonnull Path destination, @Nonnull BasicFileAttributes destAttributes) {
        return true;
    }

    /**
     * Callback method that is invoked while walking the file tree to decide whether to include a path or not. This
     * method is called for both the leaf nodes (the files to be synced) and the intermediate directories. Returning
     * {@code false} for a directory means that the whole subtree is skipped.
     *
     * @param source the source path
     * @param sourceAttributes the attributes
     * @return {@code true} if {@code path} should be included in the sync operation, otherwise {@code false}
     */
    default boolean isSourceIncluded(@Nonnull Path source, @Nonnull BasicFileAttributes sourceAttributes) {
        return true;
    }

    /**
     *  Callback method that is invoked when a file exists in {@code destination} and {@link SyncFileTreeParameters}
     *  does not allow replacing it. Callback implementations can provide custom logic here, for instance to merge
     *  the source and destination files, or pick the newest one.
     *
     * @param source the source file
     * @param sourceAttributes the source file attributes
     * @param destination the destination file.
     * @throws IOException if an I/O error occurs
     */
    default void onConflict(@Nonnull Path source, @Nonnull BasicFileAttributes sourceAttributes,
                            @Nonnull Path destination) throws IOException {
    }

    /**
     *  Callback method that is invoked when a file could not be synced by
     *  {@link MoreFiles#syncFileTree(SyncFileTreeParameters)}.
     *
     * @param source the source file
     * @param sourceAttributes the source file attributed
     * @param destination the destination file
     * @param exception the I/O exception that was thrown when attempting to sync {@code source} to {@code destination},
     *                  or by thrown by {@link #onConflict(Path, BasicFileAttributes, Path)}
     *                  or {@link #onSynced(Path, BasicFileAttributes, Path)}
     * @throws IOException if an I/O error occurs
     */
    default void onError(@Nonnull Path source, @Nonnull BasicFileAttributes sourceAttributes,
                         @Nonnull Path destination, @Nonnull IOException exception) throws IOException {
        throw exception;
    }

    /**
     * Callback method that is invoked when a file is found in {@code destination} that does not or no longer exists
     * in the {@code source} location.
     *
     * @param source the source file (which does not exist)
     * @param destination the destination file (which does exist)
     * @throws IOException if an I/O error occurs
     */
    default void onMissingInSource(@Nonnull Path source, @Nonnull Path destination) throws IOException {
    }

    /**
     * Callback method that is invoked when a file was successfully synced by
     * {@link MoreFiles#syncFileTree(SyncFileTreeParameters)}.
     *
     * @param source the source file
     * @param sourceAttributes the source file attributes
     * @param destination the destination file
     * @throws IOException if an I/O error occurs
     */
    default void onSynced(@Nonnull Path source, @Nonnull BasicFileAttributes sourceAttributes,
                          @Nonnull Path destination) throws IOException {
    }
}
