package cn.reghao.autodop.dmaster.app.service.bd.tools.repo;

import cn.reghao.autodop.dmaster.app.model.po.config.build.RepoAuthConfig;
import org.tmatesoft.svn.core.*;
import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
import org.tmatesoft.svn.core.internal.io.svn.SVNRepositoryFactoryImpl;
import org.tmatesoft.svn.core.internal.wc.DefaultSVNOptions;
import org.tmatesoft.svn.core.wc.*;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * SVN 实现
 *
 * @author reghao
 * @date 2019-09-07 16:18:46
 */
public class SvnImpl implements CodeUpdater {
    private SVNLogClient logClient;
    private SVNUpdateClient updateClient;
    private SVNStatusClient statusClient;

    public SvnImpl(RepoAuthConfig repoAuthConfig) {
        ISVNAuthenticationManager authManager =
                SVNWCUtil.createDefaultAuthenticationManager(repoAuthConfig.getUsername(), repoAuthConfig.getPassword().toCharArray());
        SVNRepositoryFactoryImpl.setup();
        DefaultSVNOptions options = SVNWCUtil.createDefaultOptions(true);
        SVNClientManager clientManager = SVNClientManager.newInstance(options, authManager);

        logClient = clientManager.getLogClient();
        updateClient = clientManager.getUpdateClient();
        updateClient.setIgnoreExternals(false);
        statusClient = clientManager.getStatusClient();
    }

    public SvnImpl(String username, String password) {
        ISVNAuthenticationManager authManager =
                SVNWCUtil.createDefaultAuthenticationManager(username, password.toCharArray());
        SVNRepositoryFactoryImpl.setup();
        DefaultSVNOptions options = SVNWCUtil.createDefaultOptions(true);
        SVNClientManager clientManager = SVNClientManager.newInstance(options, authManager);

        logClient = clientManager.getLogClient();
        updateClient = clientManager.getUpdateClient();
        updateClient.setIgnoreExternals(false);
        statusClient = clientManager.getStatusClient();
    }

    @Override
    public CommitInfo update(String remote, String branch, String local) throws SVNException {
        File localRepo = new File(local);
        if (!localRepo.exists()) {
            checkout(remote, localRepo);
        } else {
            updateClient.doUpdate(localRepo, SVNRevision.HEAD, SVNDepth.INFINITY,
                    true, false);
        }

        CommitInfo commitInfo = latestCommitInfo(local, branch);
        commitInfo.setRemote(remote);
        commitInfo.setBranch(branch);
        return commitInfo;
    }

    private List<ChangedFile> changedFileList(Map<String, SVNLogEntryPath> changedPaths) {
        List<ChangedFile> changedFiles = new ArrayList<>();
        changedPaths.forEach((k, v) -> {
            ChangedFile changedFile = new ChangedFile();
            changedFile.setChangeType(String.valueOf(v.getType()));
            // TODO 文件路径有问题
            changedFile.setOldFilePath(v.getPath());
            changedFile.setNewFilePath(v.getCopyPath());

            changedFiles.add(changedFile);
        });
        return changedFiles;
    }

    /**
     * 从远程仓库拉取代码
     *
     * @date 2020-01-06 下午3:48
     */
    private void checkout(String remote, File local) throws SVNException {
        SVNURL remoteRepo = SVNURL.parseURIEncoded(remote);
        updateClient.doCheckout(remoteRepo, local,
                SVNRevision.HEAD, SVNRevision.HEAD, SVNDepth.UNKNOWN, true);
    }

    @Override
    public CommitInfo update(String remote, String branch, String local, String commitId) throws Exception {
        // TODO 待实现
        return null;
    }

    @Override
    public CommitInfo latestCommitInfo(String local, String branch) throws SVNException {
        File localRepo = new File(local);
        if (!localRepo.exists()) {
            return null;
        }

        SVNStatus status = statusClient.doStatus(localRepo, false);
        long version = status.getCommittedRevision().getNumber();

        CommitInfo commitInfo = commitInfo(localRepo, version);
        commitInfo.setBranch(branch);
        return commitInfo;
    }

    private CommitInfo commitInfo(File localRepo, long version) throws SVNException {
        File[] paths = { localRepo };
        SVNRevision start = SVNRevision.create(version);
        SVNRevision end = SVNRevision.create(version);
        long limit = 9999L;
        CommitInfo commitInfo = new CommitInfo();
        logClient.doLog(paths, start, end, false, true, limit, logEntry -> {
            String commitAuthor = logEntry.getAuthor();
            String commitMsg = logEntry.getMessage();
            long commitTime = logEntry.getDate().toInstant().toEpochMilli();
            String commitId = String.valueOf(logEntry.getRevision());
            Map<String, SVNLogEntryPath> changedPaths = logEntry.getChangedPaths();

            commitInfo.setCommitId(commitId);
            if (commitMsg.length() > 1000) {
                commitInfo.setCommitMsg(commitMsg.substring(0, 1000));
            } else {
                commitInfo.setCommitMsg(commitMsg);
            }
            commitInfo.setCommitAuthor(commitAuthor);
            commitInfo.setMsCommitTime(commitTime);
            commitInfo.setChangedFiles(changedFileList(changedPaths));
        });

        return commitInfo;
    }
}
