// SPDX-FileCopyrightText: 2019 KeePassXC Team <team@keepassxc.org>
// SPDX-License-Identifier: GPL-2.0-only or GPL-3.0-only

#include <KLocalizedString>
#include <QString>

#include "passwordhealth.h"
#include "zxcvbn/zxcvbn.h"

namespace
{
const static int ZXCVBN_ESTIMATE_THRESHOLD = 256;
} // namespace

PasswordHealth::PasswordHealth(double entropy)
{
    init(entropy);
}

PasswordHealth::PasswordHealth(const QString &pwd)
{
    auto entropy = 0.0;
    entropy += ZxcvbnMatch(pwd.left(ZXCVBN_ESTIMATE_THRESHOLD).toUtf8().data(), nullptr, nullptr);
    if (pwd.length() > ZXCVBN_ESTIMATE_THRESHOLD) {
        // Add the average entropy per character for any characters above the estimate threshold
        auto average = entropy / ZXCVBN_ESTIMATE_THRESHOLD;
        entropy += average * (pwd.length() - ZXCVBN_ESTIMATE_THRESHOLD);
    }
    init(entropy);
}

void PasswordHealth::init(double entropy)
{
    m_score = m_entropy = entropy;

    switch (quality()) {
    case Quality::Bad:
    case Quality::Poor:
        m_scoreReasons << i18n("Very weak password");
        m_scoreDetails << i18n("Password entropy is %1 bits", QString::number(m_entropy, 'f', 2));
        break;

    case Quality::Weak:
        m_scoreReasons << i18n("Weak password");
        m_scoreDetails << i18n("Password entropy is %1 bits", QString::number(m_entropy, 'f', 2));
        break;

    default:
        // No reason or details for good and excellent passwords
        break;
    }
}

void PasswordHealth::setScore(int score)
{
    m_score = score;
}

void PasswordHealth::adjustScore(int amount)
{
    m_score += amount;
}

QString PasswordHealth::scoreReason() const
{
    return m_scoreReasons.join(u'\n');
}

void PasswordHealth::addScoreReason(QString reason)
{
    m_scoreReasons << reason;
}

QString PasswordHealth::scoreDetails() const
{
    return m_scoreDetails.join(u'\n');
}

void PasswordHealth::addScoreDetails(QString details)
{
    m_scoreDetails.append(details);
}

PasswordHealth::Quality PasswordHealth::quality() const
{
    if (m_score <= 0) {
        return Quality::Bad;
    } else if (m_score < 40) {
        return Quality::Poor;
    } else if (m_score < 75) {
        return Quality::Weak;
    } else if (m_score < 100) {
        return Quality::Good;
    }
    return Quality::Excellent;
}
