#include "jira-agent.h"

#include "branchitem.h"
#include "mainwindow.h"
#include "vymmodel.h"

#include <QHash>
#include <QMessageBox>
#include <QSslSocket>

extern Main *mainWindow;
extern QDir vymBaseDir;
extern Settings settings;
extern QTextStream vout;
extern bool debug;

bool JiraAgent::available()
{
    if (!QSslSocket::supportsSsl())
        return false;
    if ( settings.value("/atlassian/jira/servers/size", 0).toInt() < 1)
        return false;

    return true;
}

JiraAgent::JiraAgent()
{
    //qDebug ()<< "Constr. JiraAgent";

    init();
}

JiraAgent::~JiraAgent()
{
    //qDebug() << "Destr. JiraAgent";

    if (killTimer)
        delete killTimer;
}

void JiraAgent::init()
{
    jobType = Undefined;
    jobStep = -1;
    abortJob = false;

    killTimer = nullptr;

    networkManager = new QNetworkAccessManager(this);

    modelID = 0;    // invalid ID

    killTimer = new QTimer(this);
    killTimer->setInterval(15000);
    killTimer->setSingleShot(true);

    QObject::connect(killTimer, SIGNAL(timeout()), this, SLOT(timeout()));

    // Reset credentials, these are server specific beginning in 2.9.18
    authUsingPATInt = true;
    personalAccessTokenInt = QString();
    userNameInt = QString();
    passwordInt = QString();
    serverNameInt = QString();

    // Set API rest point. baseUrlInt later on depends on different JIRA system
    apiUrl = "/rest/api/2";

}

void JiraAgent::setJobType(JobType jt)
{
    jobType = jt;
}

bool JiraAgent::setBranch(BranchItem *bi)
{
    if (!bi) {
        abortJob = true;
        return false;
    } else {
        branchID = bi->getID();
        modelID = bi->getModel()->getModelID();
        return true;
    }
}


bool JiraAgent::setTicket(const QString &id)
{
    // Find ID part in parameter:
    QRegExp re("(\\w+[-|\\s]\\d+)");
    if (re.indexIn(id) < 0) {
        qWarning() << "JiraAgent::setTicket invalid ID: " << id;
        abortJob = true;
        return false;
    }

    ticketID = re.cap(1);
    ticketID.replace(" ", "-");

    bool foundPattern = false;

    settings.beginGroup("/atlassian/jira");

    // Try to find baseUrl of server by looking through patterns in ticket IDs:
    int size = settings.beginReadArray("servers");
    for (int i = 0; i < size; ++i) {
        settings.setArrayIndex(i);
        foreach (QString p, settings.value("pattern").toString().split(",")) {
            if (ticketID.contains(p)) {
                foundPattern = true;

                baseUrlInt = settings.value("baseUrl","-").toString();
                serverNameInt = settings.value("name","-").toString();

                // Read credentials for this server   
                authUsingPATInt = 
                    settings.value("authUsingPAT", true).toBool();
                if (authUsingPATInt)
                    personalAccessTokenInt =
                        settings.value("PAT", "undefined").toString();
                else {
                    userNameInt =
                        settings.value("username", "user_johnDoe").toString();
                    passwordInt = 
                        settings.value("password", "").toString();
                }
                break;
            }
        }
    }
    settings.endArray();
    settings.endGroup();

    return foundPattern;
}

QString JiraAgent::serverName()
{
    if (baseUrlInt.isEmpty())
        return QString();
    else
        return serverNameInt;
}

QString JiraAgent::url()
{
    return baseUrlInt + "/browse/" + ticketID;
}

void JiraAgent::startJob()
{
    if (jobStep > 0) {
        unknownStepWarning();
        finishJob();
    } else {
        jobStep = 0;
        continueJob();
    }
}

void JiraAgent::continueJob()
{
    if (abortJob) {
        finishJob();
        return;
    }

    jobStep++;

    // qDebug() << "JA::contJob " << jobType << " Step: " << jobStep << "TicketID: " << ticketID;

    switch(jobType) {
        case GetTicketInfo:
            switch(jobStep) {
                case 1:
                    // if (!requestedURL.toString().startsWith("http"))
                    //    requestedURL.setPath("https://" + requestedURL.path());
                    startGetTicketRequest();
                    break;
                case 2: {
                    QJsonDocument jsdoc = QJsonDocument (jsobj);

                    // Insert references to original branch and model
                    jsobj["vymBranchId"] = QJsonValue(branchID);
                    jsobj["vymTicketUrl"] = QJsonValue(url());

                    emit (jiraTicketReady(QJsonObject(jsobj)));
                    finishJob();
                    }
                    break;
                default:
                    unknownStepWarning();
                    break;
            };
            break;
        default:
            qWarning() << "JiraAgent::continueJob   unknown jobType " << jobType;
    }
}

void JiraAgent::finishJob()
{
    deleteLater();
}

void JiraAgent::unknownStepWarning()
{
    qWarning() << "JA::contJob  unknow step in jobType = " 
        << jobType 
        << "jobStep = " << jobStep;
}

void JiraAgent::startGetTicketRequest()
{
    QUrl u = QUrl(baseUrlInt + apiUrl + "/issue/" + ticketID);

    QNetworkRequest request = QNetworkRequest(u);

    // Basic authentication in header
    QString headerData;
    if (authUsingPATInt)
        headerData = QString("Bearer %1").arg(personalAccessTokenInt);
    else {
        QString concatenated = userNameInt + ":" + passwordInt;
        QByteArray data = concatenated.toLocal8Bit().toBase64();
        headerData = "Basic " + data;
    }

    request.setRawHeader("Authorization", headerData.toLocal8Bit());

    if (debug)
        qDebug() << "JA::startGetTicketRequest: url = " + request.url().toString();

    killTimer->start();

    connect(networkManager, &QNetworkAccessManager::finished,
        this, &JiraAgent::ticketReceived);

    networkManager->get(request);
}

void JiraAgent::ticketReceived(QNetworkReply *reply)
{
    // qDebug() << "JA::ticketReceived";

    killTimer->stop();

    networkManager->disconnect();

    QString r = reply->readAll();

    if (reply->error()) {
        if (reply->error() == QNetworkReply::AuthenticationRequiredError)
            QMessageBox::warning(
                nullptr, tr("Warning"),
                tr("Authentication problem when contacting JIRA"));

        qWarning() << "JiraAgent::ticketRReveived reply error";
        qWarning() << "Error: " << reply->error();
        vout << "reply: " << Qt::endl << r << Qt::endl;
        finishJob();
        return;
    }

    QJsonDocument jsdoc;
    jsdoc = QJsonDocument::fromJson(r.toUtf8());
    jsobj = jsdoc.object();
    continueJob();
}

void JiraAgent::timeout() 
{
    qWarning() << "JiraAgent timeout!!   jobType = " << jobType;
}

#ifndef QT_NO_SSL
void JiraAgent::sslErrors(QNetworkReply *reply, const QList<QSslError> &errors)
{
    QString errorString;
    foreach (const QSslError &error, errors) {
        if (!errorString.isEmpty())
            errorString += '\n';
        errorString += error.errorString();
    }

    reply->ignoreSslErrors();
    qWarning() << "JiraAgent: One or more SSL errors has occurred: " << errorString;
    qWarning() << "Errors ignored.";
}
#endif
