package info.knacki.pass.io;

import android.content.Context;

import java.io.File;
import java.util.ArrayDeque;

import info.knacki.gitdroid.callback.OnResponseListener;

public class FileMigratoryUtils {
    public interface MigratePasswordCondition {
        boolean isRelevantForMigration(File f);
    }

    private static void ListPasswords(File dir, MigratePasswordCondition condition, ArrayDeque<File> arr) {
        if (!dir.exists() || !dir.isDirectory())
            return;
        for (File f: dir.listFiles()) {
            if (f.isHidden())
                continue;
            if (f.isDirectory())
                ListPasswords(f, condition, arr);
            else if (condition != null && condition.isRelevantForMigration(f))
                arr.add(f);
        }
    }

    private static ArrayDeque<File> ListPasswords(Context ctx, MigratePasswordCondition condition) {
        ArrayDeque<File> files = new ArrayDeque<>();
        ListPasswords(new File(PathUtils.GetPassDir(ctx)), condition, files);
        return files;
    }

    public static void MigratePassword(IFileInterface from, IFileInterface to, OnResponseListener<Void> onDone) {
        from.ReadFile(new OnResponseListener<String>() {
            @Override
            public void OnResponse(String result) {
                try {
                    to.WriteFile(result, onDone);
                }
                catch (IFileInterface.InvalidPasswordException e) {
                    OnError(e.getMessage(), e);
                }
            }

            @Override
            public void OnError(String msg, Throwable e) {
                onDone.OnError(msg, e);
            }
        });
    }

    public static void MigratePassword(Context ctx, File from, File to, FileInterfaceFactory.PasswordGetter passInput, OnResponseListener<Void> onDone, boolean removePrevious) {
        MigratePassword(FileInterfaceFactory.GetFileInterface(ctx, passInput, from), FileInterfaceFactory.GetFileInterface(ctx, passInput, to), from.getAbsolutePath().equals(to.getAbsolutePath()) || !removePrevious ? onDone : new OnResponseListener<Void>() {
            @Override
            public void OnResponse(Void result) {
                FileUtils.DeleteFile(from);
                onDone.OnResponse(result);
            }

            @Override
            public void OnError(String msg, Throwable e) {
                onDone.OnError(msg, e);
            }
        });
    }

    public static void MigratePasswordToPassword(final Context ctx, FileInterfaceFactory.PasswordGetter passwordGetter) {
        final ArrayDeque<String> oldPasswords = new ArrayDeque<>();
        final ArrayDeque<File> oldPasswordFiles = ListPasswords(ctx, (file) -> file.getName().endsWith(PasswordFileInterface.PASSWORD_SUFFIX));

        final Runnable nextFile = new Runnable() {
            private void NextFile() {
                if (oldPasswordFiles.isEmpty())
                    return;
                final File currentFile = oldPasswordFiles.poll();
                final ArrayDeque<String> currentPasswordToTry = new ArrayDeque<>(oldPasswords);
                MigratePassword(ctx, currentFile, currentFile, new FileInterfaceFactory.PasswordGetter() {
                    @Override
                    public FileInterfaceFactory.PasswordGetter SetFile(File file) { return this; }

                    @Override
                    public FileInterfaceFactory.PasswordGetter GetPassword(FileInterfaceFactory.OnPasswordEnteredListener listener) {
                        if (!currentPasswordToTry.isEmpty())
                            listener.OnResponse(currentPasswordToTry.poll());
                        else
                            passwordGetter.SetFile(currentFile).GetPassword(new FileInterfaceFactory.OnPasswordEnteredListener() {
                                @Override
                                public boolean OnResponse(String pass) {
                                    oldPasswords.add(pass);
                                    return listener.OnResponse(pass);
                                }

                                @Override
                                public void OnError(String what, Throwable e) {
                                }
                            });
                        return this;
                    }
                }, new OnResponseListener<Void>() {
                    @Override
                    public void OnResponse(Void result) {
                        NextFile();
                    }

                    @Override
                    public void OnError(String msg, Throwable e) {
                        NextFile();
                    }
                }, false);
            }

            @Override
            public void run() {
                NextFile();
            }
        };
        nextFile.run();
    }
}
