// SPDX-FileCopyrightText: 2014-2023 Anne Jan Brouwer <brouwer@annejan.com>
// SPDX-FileCopyrightText: 2016-2017 tezeb <tezeb+github@outoftheblue.pl>
// SPDX-FileCopyrightText: 2018 Lukas Vogel <lukedirtwalker@gmail.com>
// SPDX-FileCopyrightText: 2018 Claudio Maradonna <penguyman@stronzi.org>
// SPDX-FileCopyrightText: 2019 Maciej S. Szmigiero <mail@maciej.szmigiero.name>
// SPDX-FileCopyrightText: 2023 g10 Code GmbH
// SPDX-FileContributor: Sune Stolborg Vuorela <sune@vuorela.dk>
// SPDX-License-Identifier: GPL-3.0-or-later

#include "passwordviewer.h"
#include "clipboardhelper.h"
#include "passentry.h"
#include "qpushbuttonfactory.h"
#include "settings.h"
#include "util.h"

#include <QDialog>
#include <QFormLayout>
#include <QLabel>
#include <QMainWindow>
#include <QPushButton>
#include <QTextBrowser>
#include <QVBoxLayout>

#include <KLocalizedString>
#include <KPasswordLineEdit>
#include <KTitleWidget>
#if QT_VERSION > QT_VERSION_CHECK(6, 0, 0)
#include <Prison/Barcode>
#else
#include <Prison/AbstractBarcode>
#include <Prison/prison.h>
#endif

namespace
{
std::unique_ptr<QTextBrowser> createTextBrowserForNotes()
{
    auto textBrowser = std::make_unique<QTextBrowser>();
    if (Settings::isNoLineWrapping()) {
        textBrowser->setLineWrapMode(QTextBrowser::NoWrap);
    }
    textBrowser->setOpenExternalLinks(true);
    textBrowser->setContextMenuPolicy(Qt::DefaultContextMenu);
    textBrowser->document()->setDocumentMargin(0);
    return textBrowser;
}
}

PasswordViewer::PasswordViewer(ClipboardHelper *clipboardHelper, QWidget *parent)
    : QWidget(parent)
    , m_titleWidget(new KTitleWidget(this))
    , m_clipboardHelper(clipboardHelper)
    , m_contentLayout(new QFormLayout)
    , m_copyPasswordNameButton(new QPushButton(QIcon::fromTheme(QStringLiteral("edit-copy-symbolic")), {}, this))
{
    auto layout = new QVBoxLayout(this);

    // Setup ui
    m_copyPasswordNameButton->setAccessibleName(i18nc("@action:button", "Copy to clipboard"));
    m_copyPasswordNameButton->setToolTip(i18nc("@info:tooltip", "Copy title to clipboard"));

    auto titleLayout = new QHBoxLayout;
    titleLayout->addWidget(m_titleWidget);
    titleLayout->insertStretch(1);
    titleLayout->addWidget(m_copyPasswordNameButton);

    connect(m_copyPasswordNameButton, &QPushButton::clicked, this, [this]() {
        m_clipboardHelper->copyTextToClipboard(m_titleWidget->text().trimmed());
    });
    layout->addLayout(titleLayout);
    layout->addLayout(m_contentLayout);

    setPanelTimer();
    clearPanelTimer.setSingleShot(true);
    connect(&clearPanelTimer, &QTimer::timeout, this, &PasswordViewer::clear);
}

void PasswordViewer::setPassEntry(const PassEntry &entry, const QString &content)
{
    QString password = entry.password();
    QString output = content;

    m_clipboardHelper->setClippedText(password);

    // first clear the current view:
    clear();

    m_titleWidget->setText(entry.name());
    m_titleWidget->show();
    m_copyPasswordNameButton->show();

    // show what is needed:
    if (!Settings::isDisplayAsIs()) {
        if (!password.isEmpty()) {
            // set the password, it is hidden if needed in addToGridLayout
            addToGridLayout(i18n("Password:"), password);
        }

        const auto namedValues = entry.namedValues();
        for (const auto &nv : namedValues) {
            addToGridLayout(i18nc("Field label", "%1:", nv.name), nv.value);
        }

        output = entry.remainingDataForDisplay();
    }

    if (Settings::isUseAutoclearPanel()) {
        clearPanelTimer.start();
    }

    if (!output.isEmpty()) {
        output = output.toHtmlEscaped();
        output.replace(Util::protocolRegex(), QStringLiteral(R"(<a href="\1">\1</a>)"));
        output.replace(QLatin1Char('\n'), QStringLiteral("<br />"));
        auto textBrowser = createTextBrowserForNotes();
        textBrowser->setHtml(output);
        m_contentLayout->addRow(new QLabel(i18nc("@label", "Notes:")), textBrowser.release());
    }
}

void PasswordViewer::clearTemplateWidgets()
{
    while (m_contentLayout->count() > 0) {
        m_contentLayout->removeRow(m_contentLayout->rowCount() - 1);
    }
}

void PasswordViewer::addToGridLayout(const QString &field, const QString &value)
{
    QString trimmedField = field.trimmed();
    QString trimmedValue = value.trimmed();

    // Combine the Copy button and the line edit in one widget
    auto rowLayout = new QHBoxLayout();
    rowLayout->setContentsMargins(0, 2, 0, 2);

    if (trimmedField == i18n("Password:")) {
#if QT_VERSION > QT_VERSION_CHECK(6, 0, 0)
        auto *line = new KPasswordLineEdit();
        line->setRevealPasswordMode(KPassword::RevealMode::Always);
        line->setReadOnly(true);
        line->setPassword(trimmedValue);
#else
        auto *line = new QLineEdit();
        line->setText(trimmedValue);
        auto iconOn = QIcon::fromTheme(QStringLiteral("password-show-on"));
        auto iconOff = QIcon::fromTheme(QStringLiteral("password-show-off"));
        auto action = line->addAction(iconOn, QLineEdit::TrailingPosition);
        action->setCheckable(true);
        action->setText(i18n("Toggle password visibility"));
        connect(action, &QAction::triggered, this, [line, action, iconOn, iconOff]() {
            if (line->echoMode() == QLineEdit::Password) {
                line->setEchoMode(QLineEdit::Normal);
                action->setIcon(iconOff);
            } else {
                line->setEchoMode(QLineEdit::Password);
                action->setIcon(iconOn);
            }
        });
#endif
        line->setObjectName(trimmedField);
        line->setContentsMargins(0, 0, 0, 0);
        line->setEchoMode(QLineEdit::Password);
        rowLayout->addWidget(line);
    } else {
        auto *line = new QLabel();
        line->setOpenExternalLinks(true);
        line->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse | Qt::LinksAccessibleByKeyboard);
        line->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum));
        line->setObjectName(trimmedField);
        trimmedValue.replace(Util::protocolRegex(), QStringLiteral(R"(<a href="\1">\1</a>)"));
        line->setText(trimmedValue);
        line->setContentsMargins(5, 0, 0, 0);
        rowLayout->addWidget(line);
    }

    auto fieldName = trimmedField;
#if QT_VERSION > QT_VERSION_CHECK(6, 0, 0)
    fieldName.removeLast(); // remove ':' from the end of the label
#else
    fieldName.remove(fieldName.count() - 1, 1); // remove ':' from the end of the label
#endif

    auto fieldLabel =
        createPushButton(QIcon::fromTheme(QStringLiteral("edit-copy")), i18n("Copy '%1' to clipboard", fieldName), m_clipboardHelper, [this, trimmedValue] {
            m_clipboardHelper->copyTextToClipboard(trimmedValue);
        });

    rowLayout->addWidget(fieldLabel.release());

    auto qrButton =
        createPushButton(QIcon::fromTheme(QStringLiteral("view-barcode-qr")), i18n("View '%1' QR Code", fieldName), m_clipboardHelper, [this, trimmedValue]() {

#if QT_VERSION > QT_VERSION_CHECK(6, 0, 0)
            auto barcode = Prison::Barcode::create(Prison::QRCode);
#else
            auto barcode = Prison::createBarcode(Prison::BarcodeType::QRCode);
#endif
            if (!barcode) {
                return;
            }
            barcode->setData(trimmedValue);
            auto image = barcode->toImage(barcode->preferredSize(window()->devicePixelRatioF()));

            QDialog popup(nullptr, Qt::Popup | Qt::FramelessWindowHint);
            QVBoxLayout *layout = new QVBoxLayout;
            QLabel *popupLabel = new QLabel();
            layout->addWidget(popupLabel);
            popupLabel->setPixmap(QPixmap::fromImage(image));
            popupLabel->setScaledContents(true);
            popupLabel->show();
            popup.setLayout(layout);
            popup.move(QCursor::pos());
            popup.exec();
        });
    rowLayout->addWidget(qrButton.release());

    // set into the layout
    m_contentLayout->addRow(trimmedField, rowLayout);
}

/**
 * @brief MainWindow::clearPanel hide the information from shoulder surfers
 */
void PasswordViewer::clear()
{
    clearTemplateWidgets();
    m_titleWidget->setText(QString{});
    m_copyPasswordNameButton->hide();
}

void PasswordViewer::setPanelTimer()
{
    clearPanelTimer.setInterval(1000 * Settings::getAutoclearPanelSeconds());
}
