#include "git_client.h"
#include "update.h"

#include <QRegularExpression>
#include <QDebug>
#include <QDir>

GitClient::GitClient(QString const &workingDirectory, QString const &branchName)
  : m_workingDirectory(workingDirectory)
  , m_branchName(branchName) {
}

void GitClient::setWorkingDirectory(QString const &workingDirectory) {
    m_workingDirectory = workingDirectory;
}

QString GitClient::workingDirectory() const {
    return m_workingDirectory;
}

void GitClient::setBranchName(QString const &branchName) {
    m_branchName = branchName;
}

QString GitClient::branchName() const {
    return m_branchName;
}

std::optional<QString> GitClient::gitCloneRepository(QString const &repPath) {
    QString gitCommand("git clone ");
    gitCommand += repPath;
    Command c(gitCommand);
    if (c.execute(m_workingDirectory)) {
        QString result = c.getCommandResult();
        if (!result.isEmpty()) {
            // Cloning into 'customer_281'...\n
            static QRegularExpression re("(^\\s*Cloning\\s+into\\s+[']\\s*)(.*)(\\s*['].*$)");
            QRegularExpressionMatch match = re.match(result);
            if (match.hasMatch()) {
                if (re.captureCount() == 3) { // start with full match (0), then the other 3 matches
                    return match.captured(2);
                }
            }
        }
    }
    return std::nullopt;
}

bool GitClient::gitCheckout(QString const &branchName) {
    QString gitCommand("git checkout ");
    gitCommand += branchName;
    Command c(gitCommand);
    return c.execute(m_workingDirectory);
}

std::optional<QString> GitClient::gitCloneBranch(QString const &repPath,
                                                 QString const &branchName) {
    if (std::optional<QString> rep = gitCloneRepository(repPath)) {
        QDir wd(m_workingDirectory);
        if (wd.cd(rep.value())) {
            m_workingDirectory = wd.absolutePath();
            if (gitCheckout(branchName)) {
                return branchName;
            }
        }
    }
    return std::nullopt;
}

std::optional<QStringList> GitClient::gitDiff(QString const &commits) {
    // 409f198..6c22726
    QString gitCommand("git diff --compact-summary ");
    gitCommand += commits;

    Command c(gitCommand);
    if (c.execute(m_workingDirectory)) {
        QString s = c.getCommandResult().trimmed();
        QStringList lines = Update::split(s, '\n');
        QStringList fileNames;
        // each line has the format "etc/psa_config/DC2C_print01.json | 1 +
        // or the format            "etc/psa_config/DC2C_print01.json (new) | 1 +
        // the filenames are relativ to the repository
        for (int i = 0; i < lines.size(); ++i) {
            int newIndex = lines.at(i).indexOf("(new)");
            if (newIndex != -1) {
                QString fileName = lines.at(i).mid(0, newIndex).trimmed();
                fileNames << fileName;
            } else {
                int pipeIndex = lines.at(i).indexOf('|');
                if (pipeIndex != -1) {
                    QString fileName = lines.at(i).mid(0, pipeIndex).trimmed();
                    fileNames << fileName;
                }
            }
        }
        if (!fileNames.isEmpty()) {
            return fileNames;
        }
    }
    return std::nullopt;
}

std::optional<QString> GitClient::gitFetch() {
    Command c("git fetch");
    if (c.execute(m_workingDirectory)) {
        QString s = c.getCommandResult().trimmed();
        QStringList lines = Update::split(s, '\n');
        if (!lines.empty()) {
            // 409f198..6c22726  zg1/zone1  -> origin/zg1/zone1
            static QRegularExpression re("(^\\s*)([0-9A-Fa-f]+..[0-9A-Fa-f]+)(.*$)");
            QRegularExpressionMatch match = re.match(lines.last());
            if (match.hasMatch()) {
                if (re.captureCount() == 3) { // start with full match (0), then the other 3 matches
                    return match.captured(2);
                }
            }
        }
    }
    return std::nullopt;
}

bool GitClient::gitFetchAndDiff() {
    if (gitFetch()) {
        QString gitCommand("git diff --compact-summary HEAD..FETCH_HEAD");
        Command c(gitCommand);
        return c.execute(m_workingDirectory);
    }
    return false;
}

bool GitClient::gitPull() {
    Command c("git pull");
    return c.execute(m_workingDirectory);
}

std::optional<QStringList> GitClient::gitMerge() {
    Command c("git merge");
    if (c.execute(m_workingDirectory)) {
        QString s = c.getCommandResult();
        QStringList lst = Update::split(s, '\n');
        return lst;
    }
    return std::nullopt;
}
