/*
 * Copyright (c) 2017 kszatan
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package io.github.kszatan.gocd.phabricator.stagingmaterial.scm.git;

import com.thoughtworks.go.plugin.api.logging.Logger;
import io.github.kszatan.gocd.phabricator.stagingmaterial.handlers.bodies.*;
import io.github.kszatan.gocd.phabricator.stagingmaterial.scm.Scm;

import java.util.*;
import java.util.stream.Collectors;

public class Git implements Scm {
    private final ScmConfiguration configuration;
    private final JGitWrapper jgitWrapper;
    private String lastErrorMessage;

    public Git(ScmConfiguration configuration, JGitWrapper jgitWrapper) {
        this.configuration = configuration;
        this.jgitWrapper = jgitWrapper;
        this.lastErrorMessage = "";
    }

    @Override
    public Boolean canConnect() {
        Boolean connected = true;
        try {
            if (configuration.hasCredentials()) {
                jgitWrapper.lsRemote(configuration.getUrl(), configuration.getUsername(), configuration.getPassword());
            } else {
                jgitWrapper.lsRemote(configuration.getUrl());
            }
        } catch (JGitWrapperException e) {
            lastErrorMessage = e.getMessage();
            connected = false;
        }
        return connected;
    }

    @Override
    public Optional<LatestRevisionResponse> getLatestRevision(String workDirPath) {
        Revision revision = new Revision();
        try {
            Repository repository = jgitWrapper.cloneOrUpdateRepository(configuration, workDirPath, true);
            Collection<Tag> tagList = jgitWrapper.fetchTags(repository);
            Optional<Tag> lastRevisionTag = tagList.stream()
                    .filter(t -> t.getName().startsWith("refs/tags/phabricator/diff/"))
                    .sorted(Comparator.comparing(Tag::getName))
                    .reduce((a, b) -> b);
            if (lastRevisionTag.isPresent()) {
                Tag tag = lastRevisionTag.get();
                revision.revision = tag.getName().replace("refs/tags/phabricator/diff/", "");
                Collection<Commit> commits = jgitWrapper.log(repository, tag);
                Commit tip = commits.iterator().next();
                fillRevisionCommitInfo(revision, tip);
                Commit tipParent = tip.parent();
                fillRevisionModifiedFiles(revision, jgitWrapper.diff(repository, tip, tipParent));
            }
        } catch (JGitWrapperException e) {
            lastErrorMessage = e.getMessage();
            return Optional.empty();
        }
        return Optional.of(new LatestRevisionResponse(revision));
    }

    @Override
    public Optional<LatestRevisionsSinceResponse> getLatestRevisionsSince(String workDirPath, Revision latestRevision) {
        ArrayList<Revision> revisions = new ArrayList<>();
        try {
            Repository repository = jgitWrapper.cloneOrUpdateRepository(configuration, workDirPath, true);
            Collection<Tag> tagList = jgitWrapper.fetchTags(repository);
            Integer latestRevisionNum = Integer.valueOf(latestRevision.revision);
            List<Tag> latestTags = tagList.stream()
                    .filter(t -> t.getName().startsWith("refs/tags/phabricator/diff/"))
                    .filter(t -> {
                        String rev = t.getName().replace("refs/tags/phabricator/diff/", "");
                        return Integer.valueOf(rev) > latestRevisionNum;
                    })
                    .sorted(Comparator.comparing(Tag::getName))
                    .collect(Collectors.toList());
            for (Tag tag : latestTags) {
                Revision revision = new Revision();
                revision.revision = tag.getName().replace("refs/tags/phabricator/diff/", "");
                Collection<Commit> commits = jgitWrapper.log(repository, tag);
                Commit tip = commits.iterator().next();
                fillRevisionCommitInfo(revision, tip);
                Commit tipParent = tip.parent();
                fillRevisionModifiedFiles(revision, jgitWrapper.diff(repository, tip, tipParent));
                revisions.add(revision);
            }
        } catch (JGitWrapperException e) {
            lastErrorMessage = e.getMessage();
            return Optional.empty();
        }
        return Optional.of(new LatestRevisionsSinceResponse(revisions));
    }

    @Override
    public Boolean checkout(Revision revision, String checkoutDirPath) {
        try {
            Repository repository = jgitWrapper.cloneOrUpdateRepository(configuration, checkoutDirPath, false);
            jgitWrapper.checkout(repository, "refs/tags/phabricator/diff/" + revision.revision);
            return true;
        } catch (JGitWrapperException e) {
            lastErrorMessage = e.getMessage();
            return false;
        }
    }

    @Override
    public String getLastErrorMessage() {
        return lastErrorMessage;
    }

    private void fillRevisionCommitInfo(Revision revision, Commit commit) {
        revision.revisionComment = commit.comment();
        revision.timestamp = commit.commitTime();
        revision.user = commit.author();
    }

    private void fillRevisionModifiedFiles(Revision revision, Collection<DiffEntry> entries) {
        for (DiffEntry entry : entries) {
            ModifiedFile file = new ModifiedFile(entry.path(), entry.action());
            revision.modifiedFiles.add(file);
        }
    }
}

@@ -1596,16 +1596,93 @@
 ssage;%0A%0A
+    private static final String TAG_PREFIX = %22refs/tags/phabricator/diff/%22;%0A%0A
     publ
@@ -2858,37 +2858,18 @@
 ith(
-%22refs/tags/phabricator/diff/%22
+TAG_PREFIX
 ))%0A 
@@ -3131,37 +3131,18 @@
 ace(
-%22refs/tags/phabricator/diff/%22
+TAG_PREFIX
 , %22%22
@@ -4288,37 +4288,18 @@
 ith(
-%22refs/tags/phabricator/diff/%22
+TAG_PREFIX
 ))%0A 
@@ -4393,37 +4393,18 @@
 ace(
-%22refs/tags/phabricator/diff/%22
+TAG_PREFIX
 , %22%22
@@ -4772,37 +4772,18 @@
 ace(
-%22refs/tags/phabricator/diff/%22
+TAG_PREFIX
 , %22%22
@@ -5657,37 +5657,18 @@
 ry, 
-%22refs/tags/phabricator/diff/%22
+TAG_PREFIX
  + r

Replace magic tag prefix occurences with symbolic constant