/*
 * BSD 2-Clause License
 *
 * Copyright (c) 2020, Agnieszka Cicha-Cisek
 * Copyright (c) 2020, Patryk Cisek
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "totpuriparser.h"
#include <stdexcept>

#include <QDebug>
#include <QUrlQuery>
#include <QUrl>
#include <QRegularExpression>

TotpUriParser::TotpUriParser(const QString &original)
{
    validateUrl(original);
    extractAccountName(original);
    extractQuery(original);
    validateQuery();
    extractQueryValues();
}

QString TotpUriParser::getAccountName() const
{
    return user;
}

QString TotpUriParser::getSecret() const
{
    return secret;
}

QString TotpUriParser::getIssuer() const
{
    return issuer;
}

void TotpUriParser::validateUrl(QString original)
{
    if (original.isEmpty()){
        throw std::runtime_error("Empty url!");
    }

    const QString SUPPORTED_SCHEME = "otpauth://totp/";
    const int SCHEME_LEN = SUPPORTED_SCHEME.length();
    QString scheme = original.left(SCHEME_LEN);
    qDebug() << "Detected scheme: " << scheme;

    int cmp = QString::compare(SUPPORTED_SCHEME, scheme, Qt::CaseSensitive);
    if (cmp != 0) {
        throw std::runtime_error("Wrong scheme!");
    }
}

void TotpUriParser::extractQuery(QString original)
{
    query = original;
    int otpauthEnd = original.indexOf("?",0)+1;
    query.remove(0,otpauthEnd);
}

void TotpUriParser::validateQuery()
{
    if (query.isEmpty()){
        qDebug() << "Empty query!";
        throw std::runtime_error("Empty query!");
    }
    if (!query.contains("secret")){
        qDebug() << "NO secret!";
        throw std::runtime_error("NO secret!");
    }
}

void TotpUriParser::extractAccountName(QString original)
{
    QUrl url(original);
    QString path = url.path();
    qDebug() << "Detected path: " << path;

    if (path.contains(":")){
        user = path.mid(path.indexOf(":")+1, path.length());
    } else {
        user = path.remove(0,1);
    }
    qDebug() << "Detected user: " << user;

    if (user.isEmpty()){
        throw std::runtime_error("NO user!");
    }
}

void TotpUriParser::extractQueryValues()
{
    QUrlQuery url(query);
    secret = url.queryItemValue("secret");
    secret = secret.remove('-').remove(' ').trimmed().toUpper();

    QRegularExpression pattern("^[A-Z0-9]+$");
    QRegularExpressionMatch match = pattern.match(secret);
    if(!match.hasMatch()){
        throw std::runtime_error("Secret does not look like Base32-encoded value!");
    }

    issuer = url.queryItemValue("issuer");
    qDebug() << "Detected issuer: " << issuer;
}
