#include "shortcut-form-view-host.hpp"
#include "builtin_icon.hpp"
#include "favicon/favicon-service.hpp"
#include "navigation-controller.hpp"
#include "view-utils.hpp"
#include "service-registry.hpp"
#include "services/app-service/app-service.hpp"
#include "services/shortcut/shortcut-service.hpp"
#include "services/shortcut/shortcut.hpp"
#include "services/toast/toast-service.hpp"
#include "ui/action-pannel/action.hpp"
#include <QFutureWatcher>
#include <QUrl>

ShortcutFormViewHost::ShortcutFormViewHost() : FormViewBase() {}

ShortcutFormViewHost::ShortcutFormViewHost(std::shared_ptr<Shortcut> shortcut, Mode mode)
    : FormViewBase(), m_mode(mode), m_initialShortcut(std::move(shortcut)) {}

QUrl ShortcutFormViewHost::qmlComponentUrl() const {
  return QUrl(QStringLiteral("qrc:/Vicinae/ShortcutFormView.qml"));
}

QVariantMap ShortcutFormViewHost::qmlProperties() {
  return {{QStringLiteral("host"), QVariant::fromValue(this)}};
}

void ShortcutFormViewHost::setPrefilledValues(const QString &link, const QString &name,
                                              const QString &application, const QString &icon) {
  m_prefilledLink = link;
  m_prefilledName = name;
  m_prefilledApp = application;
  m_prefilledIcon = icon;
}

void ShortcutFormViewHost::initialize() {
  BaseView::initialize();

  m_service = context()->services->shortcuts();
  m_appSelectorModel = new AppSelectorModel(this);

  buildIconItems();
  buildLinkCompletions();

  auto panel = std::make_unique<FormActionPanelState>();
  auto section = panel->createSection();
  auto submitAction =
      new StaticAction(tr("Submit"), ImageURL::builtin(BuiltinIcon::EnterKey), [this]() { submit(); });
  section->addAction(submitAction);
  setActions(std::move(panel));

  m_appSelectorModel->selectById(QStringLiteral("default"));
  m_selectedApp = m_appSelectorModel->currentItem();

  m_selectedIcon = m_defaultIconEntry;

  if (m_initialShortcut) {
    auto appDb = context()->services->appDb();

    if (m_mode == Mode::Duplicate) {
      m_name = tr("Copy of %1").arg(m_initialShortcut->name());
    } else {
      m_name = m_initialShortcut->name();
    }

    m_link = m_initialShortcut->url();

    auto appId = m_initialShortcut->app();
    if (!m_initialShortcut->isDefaultApp() && appDb->findById(appId)) {
      m_appSelectorModel->selectById(appId);
      m_selectedApp = m_appSelectorModel->currentItem();
    }

    if (auto item = m_iconModel.itemDataById(m_initialShortcut->icon()); !item.isEmpty()) {
      m_selectedIcon = item;
    }

    handleLinkBlurred();

    emit formChanged();
  } else if (!m_prefilledLink.isEmpty() || !m_prefilledName.isEmpty()) {
    m_name = m_prefilledName;
    m_link = m_prefilledLink;

    if (!m_prefilledApp.isEmpty()) {
      auto appDb = context()->services->appDb();
      if (appDb->findById(m_prefilledApp)) {
        m_appSelectorModel->selectById(m_prefilledApp);
        m_selectedApp = m_appSelectorModel->currentItem();
      }
    }

    if (!m_prefilledIcon.isEmpty()) {
      if (auto item = m_iconModel.itemDataById(m_prefilledIcon); !item.isEmpty()) { m_selectedIcon = item; }
    }

    if (!m_prefilledLink.isEmpty()) { handleLinkBlurred(); }

    emit formChanged();
  }

  switch (m_mode) {
  case Mode::Create:
    break;
  case Mode::Edit:
    setNavigationTitle(tr("Edit \"%1\"").arg(m_initialShortcut->name()));
    break;
  case Mode::Duplicate:
    setNavigationTitle(tr("Duplicate \"%1\"").arg(m_initialShortcut->name()));
    break;
  }
}

void ShortcutFormViewHost::buildIconItems() {
  QVariantList allIcons;

  m_resolvedDefaultIcon = ImageURL::builtin(BuiltinIcon::Link).toString();
  m_defaultIconEntry = qml::makeDropdownItem(QStringLiteral("default"), tr("Default"),
                                             qml::imageSourceFor(ImageURL::builtin(BuiltinIcon::Link)));
  allIcons.append(m_defaultIconEntry);

  for (const auto &[icon, name] : BuiltinIconService::mapping()) {
    auto url = ImageURL::builtinByName(QString::fromLatin1(name));
    allIcons.append(qml::makeDropdownItem(url.toString(), QString::fromUtf8(name), qml::imageSourceFor(url)));
  }

  m_iconModel.setItems(allIcons);
}

void ShortcutFormViewHost::buildLinkCompletions() {
  m_linkCompletions = QVariantList{
      QVariantMap{
          {QStringLiteral("iconSource"), qml::imageSourceFor(ImageURL::builtin(BuiltinIcon::TextCursor))},
          {QStringLiteral("title"), tr("Selected Text")},
          {QStringLiteral("value"), QStringLiteral("selected")},
      },
      QVariantMap{
          {QStringLiteral("iconSource"), qml::imageSourceFor(ImageURL::builtin(BuiltinIcon::CopyClipboard))},
          {QStringLiteral("title"), tr("Clipboard Text")},
          {QStringLiteral("value"), QStringLiteral("clipboard")},
      },
      QVariantMap{
          {QStringLiteral("iconSource"), qml::imageSourceFor(ImageURL::builtin(BuiltinIcon::TextCursor))},
          {QStringLiteral("title"), tr("Argument")},
          {QStringLiteral("value"), QStringLiteral("argument")},
          {QStringLiteral("template"), QStringLiteral("{argument name=\"\"}")},
          {QStringLiteral("cursorOffset"), 16},
      },
      QVariantMap{
          {QStringLiteral("iconSource"), qml::imageSourceFor(ImageURL::builtin(BuiltinIcon::Fingerprint))},
          {QStringLiteral("title"), QStringLiteral("UUID")},
          {QStringLiteral("value"), QStringLiteral("uuid")},
      },
  };
}

void ShortcutFormViewHost::updateDefaultIconInItems() { m_iconModel.updateItem(m_defaultIconEntry); }

void ShortcutFormViewHost::submit() {
  auto toast = context()->services->toastService();

  m_linkError.clear();
  m_appError.clear();
  m_iconError.clear();

  bool valid = true;

  if (m_link.isEmpty()) {
    m_linkError = tr("Required");
    valid = false;
  }

  if (m_selectedApp.isEmpty()) {
    m_appError = tr("Required");
    valid = false;
  }

  if (m_selectedIcon.isEmpty()) {
    m_iconError = tr("Required");
    valid = false;
  }

  emit errorsChanged();

  if (!valid) {
    toast->failure(tr("Validation failed"));
    return;
  }

  auto appId = m_selectedApp[QStringLiteral("id")].toString();
  auto iconId = m_selectedIcon[QStringLiteral("id")].toString();

  if (iconId == QStringLiteral("default")) { iconId = m_resolvedDefaultIcon; }

  if (m_mode == Mode::Edit) {
    bool const updated = m_service->updateShortcut(m_initialShortcut->id(), m_name, iconId, m_link, appId);
    if (!updated) {
      toast->failure(tr("Failed to update shortcut"));
      return;
    }
    toast->success(tr("Shortcut updated"));
  } else {
    bool const created = m_service->createShortcut(m_name, iconId, m_link, appId);
    if (!created) {
      toast->failure(tr("Failed to create shortcut"));
      return;
    }
    toast->success(tr("Shortcut created"));
  }

  popSelf();
}

void ShortcutFormViewHost::handleLinkBlurred() {
  if (m_link == m_lastDetectedLink) return;
  m_lastDetectedLink = m_link;

  auto appDb = context()->services->appDb();
  QUrl const url(m_link);

  if (auto app = appDb->findDefaultOpener(m_link)) {
    m_appSelectorModel->updateDefaultApp(app);
    m_selectedApp = m_appSelectorModel->currentItem();

    m_resolvedDefaultIcon = app->iconUrl().toString();
    m_defaultIconEntry[QStringLiteral("iconSource")] = qml::imageSourceFor(app->iconUrl());
    m_defaultIconEntry[QStringLiteral("displayName")] = tr("Default");
    updateDefaultIconInItems();

    if (m_selectedIcon[QStringLiteral("id")].toString() == QStringLiteral("default")) {
      m_selectedIcon = m_defaultIconEntry;
    }
    emit formChanged();
  }

  if (url.scheme().startsWith("http")) {
    using Watcher = QFutureWatcher<FaviconService::FaviconResponse>;

    auto watcher = std::make_unique<Watcher>();
    auto ptr = watcher.get();

    watcher->setFuture(FaviconService::instance()->makeRequest(url.host()));
    connect(ptr, &Watcher::finished, this, [this, url, watcher = std::move(watcher)]() {
      auto icon = ImageURL::favicon(url.host()).withFallback(ImageURL::builtin(BuiltinIcon::Image));
      m_resolvedDefaultIcon = icon.toString();
      m_defaultIconEntry[QStringLiteral("iconSource")] = qml::imageSourceFor(icon);
      m_defaultIconEntry[QStringLiteral("displayName")] = url.host();
      updateDefaultIconInItems();

      if (m_selectedIcon[QStringLiteral("id")].toString() == QStringLiteral("default")) {
        m_selectedIcon = m_defaultIconEntry;
        emit formChanged();
      }
    });
  }
}

void ShortcutFormViewHost::selectApp(const QVariantMap &item) {
  m_appSelectorModel->select(item);
  m_selectedApp = item;

  if (!m_link.isEmpty()) {
    auto resolvedApp = ShortcutService::resolveApp(*context()->services->appDb(),
                                                   item[QStringLiteral("id")].toString(), m_link);

    if (resolvedApp) m_resolvedDefaultIcon = resolvedApp->iconUrl().toString();

    auto iconSource = item[QStringLiteral("iconSource")].toString();
    m_defaultIconEntry[QStringLiteral("iconSource")] = iconSource;
    m_defaultIconEntry[QStringLiteral("displayName")] = tr("Default");
    updateDefaultIconInItems();

    if (m_selectedIcon[QStringLiteral("id")].toString() == QStringLiteral("default")) {
      m_selectedIcon = m_defaultIconEntry;
    }
  }

  emit formChanged();
}

void ShortcutFormViewHost::selectIcon(const QVariantMap &item) {
  m_selectedIcon = item;
  emit formChanged();
}
