package zielu.gittoolbox.fetch;

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.Task.Backgroundable;
import com.intellij.openapi.project.Project;
import git4idea.GitUtil;
import git4idea.GitVcs;
import git4idea.repo.GitRepository;
import git4idea.repo.GitRepositoryManager;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import org.jetbrains.annotations.NotNull;
import zielu.gittoolbox.GitToolBoxConfigForProject;
import zielu.gittoolbox.GitToolBoxProject;
import zielu.gittoolbox.ResBundle;
import zielu.gittoolbox.compat.NotificationHandle;
import zielu.gittoolbox.compat.Notifier;
import zielu.gittoolbox.ui.util.AppUtil;

public class AutoFetchTask implements Runnable {
    private static final boolean showNotifications = false;

    private final Logger LOG = Logger.getInstance(getClass());
    private final AutoFetch myParent;
    private final Project myProject;

    private final AtomicReference<NotificationHandle> lastNotification = new AtomicReference<NotificationHandle>();

    private AutoFetchTask(AutoFetch parent) {
        myParent = parent;
        myProject = myParent.project();
    }

    public static AutoFetchTask create(AutoFetch parent) {
        return new AutoFetchTask(parent);
    }

    private void finishedNotification() {
        NotificationHandle toCancel = lastNotification.getAndSet(null);
        if (toCancel != null && myParent.isActive()) {
            toCancel.expire();
        }
        if (myParent.isActive()) {
            NotificationHandle notification = Notifier.getInstance(myProject).notifyLogOnly(
                ResBundle.getString("message.autoFetch"),
                ResBundle.getString("message.finished"));
            lastNotification.set(notification);
        }
    }

    private void finishedWithoutFetch() {
        NotificationHandle toCancel = lastNotification.getAndSet(null);
        if (toCancel != null && myParent.isActive()) {
            toCancel.expire();
        }
    }

    private List<GitRepository> reposForFetch() {
        GitRepositoryManager repositoryManager = GitUtil.getRepositoryManager(myProject);
        ImmutableList<GitRepository> allRepos = ImmutableList.copyOf(repositoryManager.getRepositories());
        AutoFetchStrategy strategy = GitToolBoxConfigForProject.getInstance(myProject).getAutoFetchStrategy();
        List<GitRepository> fetchable = strategy.fetchableRepositories(allRepos, myProject);
        List<GitRepository> toFetch = Lists.newArrayListWithCapacity(fetchable.size());
        for (GitRepository repository : fetchable) {
            if (repository.getRoot().exists() && !repository.isRebaseInProgress()) {
                toFetch.add(repository);
            }
        }
        return toFetch;
    }

    private boolean doFetch(List<GitRepository> repos, @NotNull ProgressIndicator indicator, @NotNull String title) {
        LOG.debug("Starting auto-fetch...");
        indicator.setText(title);
        indicator.setIndeterminate(true);
        indicator.startNonCancelableSection();
        boolean result = false;
        AutoFetchState state = AutoFetchState.getInstance(myProject);
        if (state.canAutoFetch()) {
            if (state.fetchStart()) {
                try {
                    doFetch(repos, indicator);
                } finally {
                    indicator.finishNonCancelableSection();
                    state.fetchFinish();
                }
            } else {
                LOG.info("Auto-fetch already in progress");
                finishedWithoutFetch();
            }
            result = true;
        } else {
            LOG.debug("Auto-fetch inactive");
            finishedWithoutFetch();
        }
        myParent.updateLastAutoFetchDate();
        return result;
    }

    private void doFetch(List<GitRepository> repos, @NotNull ProgressIndicator indicator) {
        Collection<GitRepository> fetched = GtFetcher.builder().fetchAll().build(myProject, indicator).fetchRoots(repos);
        GitToolBoxProject.getInstance(myProject).perRepoStatusCache().refresh(fetched);
        LOG.debug("Finished auto-fetch");
        if (showNotifications) {
            finishedNotification();
        }
    }

    @Override
    public void run() {
        final List<GitRepository> repos = reposForFetch();
        boolean shouldFetch = !repos.isEmpty();
        if (shouldFetch) {
            AppUtil.invokeLaterIfNeeded(() -> GitVcs.runInBackground(new Backgroundable(Preconditions.checkNotNull(myProject),
                ResBundle.getString("message.autoFetching"), false) {
                @Override
                public void run(@NotNull ProgressIndicator indicator) {
                    if (doFetch(repos, indicator, getTitle())) {
                        myParent.scheduleNextTask();
                    }
                }
            }));
        } else {
            if (LOG.isDebugEnabled()) {
                LOG.debug("Fetched skipped: shouldFetch=" + shouldFetch);
            }
            if (showNotifications) {
                AppUtil.invokeLaterIfNeeded(this::finishedWithoutFetch);
            }
        }
    }
}

@@ -3173,131 +3173,8 @@
 %22);%0A
-        indicator.setText(title);%0A        indicator.setIndeterminate(true);%0A        indicator.startNonCancelableSection();%0A
     
@@ -3323,33 +3323,221 @@
     
-if (state.fetchStart()) %7B
+LOG.debug(%22Can auto-fetch%22);%0A            if (state.fetchStart()) %7B%0A                indicator.setText(title);%0A                indicator.setIndeterminate(true);%0A                indicator.startNonCancelableSection();
 %0A   
@@ -3727,24 +3727,80 @@
 chFinish();%0A
+                    myParent.updateLastAutoFetchDate();%0A
             
@@ -4080,52 +4080,8 @@
   %7D%0A
-        myParent.updateLastAutoFetchDate();%0A
     
@@ -4190,32 +4190,71 @@
 or indicator) %7B%0A
+        LOG.debug(%22Auto-fetching...%22);%0A
         Collecti

Fix first fetch scheduling when not allowed