// For license of this file, see <project-root-folder>/LICENSE.md.

#include "gui/webviewers/qtwebengine/webenginepage.h"

#include "definitions/definitions.h"
#include "gui/dialogs/formaskauth.h"
#include "gui/webviewers/qtwebengine/webengineviewer.h"
#include "miscellaneous/application.h"
#include "miscellaneous/systemfactory.h"
#include "network-web/webfactory.h"
#include "qtlinq/qtlinq.h"

#include <QAuthenticator>
#include <QInputDialog>
#include <QString>
#include <QStringList>
#include <QTimer>
#include <QUrl>
#include <QUrlQuery>
#include <QWebEngineScript>

WebEnginePage::WebEnginePage(bool is_dummy_page, QObject* parent)
  : QWebEnginePage(qApp->web()->webEngineProfile(), parent), m_isDummyPage(is_dummy_page) {
  setBackgroundColor(Qt::GlobalColor::transparent);

  connect(this, &WebEnginePage::pdfPrintingFinished, this, &WebEnginePage::onPdfPrintingFinished);
  connect(this, &WebEnginePage::proxyAuthenticationRequired, this, &WebEnginePage::onProxyAuthenticationRequired);
}

WebEngineViewer* WebEnginePage::view() const {
#if QT_VERSION_MAJOR == 6
  return qobject_cast<WebEngineViewer*>(QWebEngineView::forPage(this));
#else
  return qobject_cast<WebEngineViewer*>(QWebEnginePage::view());
#endif
}

QList<QAction*> WebEnginePage::allPageActions() const {
  QList<QAction*> acts;

  for (int i = 0; i < QWebEnginePage::WebAction::WebActionCount; i++) {
    auto* act = action(QWebEnginePage::WebAction(i));

    if (act == nullptr) {
      continue;
    }

    acts << act;
  }

  acts = qlinq::from(acts)
           .orderBy([](const QAction* act) {
             return act->text().toLower();
           })
           .toList();

  return acts;
}

void WebEnginePage::onProxyAuthenticationRequired(const QUrl& request_url,
                                                  QAuthenticator* authenticator,
                                                  const QString& proxy_host) {
  qDebugNN << LOGSEC_NETWORK << "Proxy authentication for" << QUOTE_W_SPACE(request_url.toString()) << "is required!";

  if (view() != nullptr && !view()->m_selectedItem.isNull() && !view()->m_feed.isNull()) {
    auto custom_proxy = view()->m_selectedItem.data()->account()->networkProxyForItem(view()->m_feed.data());

    if (!custom_proxy.user().isEmpty() || !custom_proxy.password().isEmpty()) {
      qDebugNN << LOGSEC_NETWORK << "Proxy authentication is filled in from feed-specific proxy.";

      authenticator->setUser(custom_proxy.user());
      authenticator->setPassword(custom_proxy.password());
      return;
    }
  }

  try {
    auto user_pass = FormAskauth::getUsernamePassword(tr("Proxy '%1' authentication").arg(proxy_host));

    authenticator->setUser(user_pass.first);
    authenticator->setPassword(user_pass.second);
  }
  catch (...) {
    qWarningNN << LOGSEC_NETWORK << "Authentication for" << QUOTE_W_SPACE(request_url.toString())
               << "was not provided.";
  }
}

void WebEnginePage::onPdfPrintingFinished(const QString& file_path, bool success) {
  if (success) {
    qApp->showGuiMessage(Notification::Event::GeneralEvent,
                         GuiMessage(tr("PDF printing done"),
                                    tr("PDF printing to file '%1' has finished.").arg(file_path)),
                         GuiMessageDestination(true, true, false),
                         GuiAction(tr("Open destination folder"), this, [file_path]() {
                           SystemFactory::openFolderFile(file_path);
                         }));
  }
  else {
    qApp->showGuiMessage(Notification::Event::GeneralEvent,
                         GuiMessage(tr("PDF printing failed"),
                                    tr("PDF printing to file '%1' failed.").arg(file_path),
                                    QSystemTrayIcon::MessageIcon::Critical),
                         GuiMessageDestination(true, true, false));
  }
}

void WebEnginePage::javaScriptAlert(const QUrl& security_origin, const QString& msg) {
  qApp
    ->showGuiMessage(Notification::Event::GeneralEvent,
                     GuiMessage(tr("Website alert"),
                                tr("URL %1 reports this important message: %2").arg(security_origin.toString(), msg)));
}

bool WebEnginePage::acceptNavigationRequest(const QUrl& url, NavigationType type, bool is_main_frame) {
  if (m_isDummyPage) {
    // NOTE: This is "dummy" page, where we only need to extract the url.
    // We therefore inform about the URL and quit right away.
    emit linkMouseClicked(url);
    return false;
  }

  if (type == NavigationType::NavigationTypeLinkClicked) {
    QTimer::singleShot(500, this, [=]() {
      emit linkMouseClicked(url);
    });

    return false;
  }

  return QWebEnginePage::acceptNavigationRequest(url, type, is_main_frame);
}

void WebEnginePage::javaScriptConsoleMessage(JavaScriptConsoleMessageLevel level,
                                             const QString& message,
                                             int line_number,
                                             const QString& source_id) {
  Q_UNUSED(level)

  qWarningNN << LOGSEC_JS << message << QSL(" (source: %1:%2)").arg(source_id, QString::number(line_number));
}

QStringList WebEnginePage::chooseFiles(FileSelectionMode mode,
                                       const QStringList& old_files,
                                       const QStringList& accepted_mime_types) {
  return QWebEnginePage::chooseFiles(mode, old_files, accepted_mime_types);
}

QWebEnginePage* WebEnginePage::createWindow(WebWindowType type) {
  auto* page = new WebEnginePage(true, this);

  // NOTE: We create "dummy" page which "accepts" the request and informs
  // us about the URL, then we destroy the page and continue URL processing
  // in main page.
  connect(page, &WebEnginePage::linkMouseClicked, this, [this, page](const QUrl& url) {
    emit linkMouseClicked(url);
    page->deleteLater();
  });

  return page;
}