/*
 * This file is part of smolbote. It's copyrighted by the contributors recorded
 * in the version control history of the file, available from its original
 * location: https://neueland.iserlohn-fortress.net/gitea/aqua/smolbote
 *
 * SPDX-License-Identifier: GPL-3.0
 */

#include "webprofilemanager.h"
#include "configuration.h"
#include "webprofile.h"
#include <QFileInfo>
#include <QWebEngineSettings>

static WebProfileManager *s_instance = nullptr;

auto WebProfileManager::instance() -> const WebProfileManager *
{
    return s_instance;
}
void WebProfileManager::setInstance(WebProfileManager *ptr)
{
    s_instance = ptr;
}

WebProfileManager::WebProfileManager(QObject *parent)
    : QObject(parent)
{
}

WebProfileManager::~WebProfileManager()
{
    for(Profile p : profiles) {
        if(p.selfDestruct && p.settings != nullptr) {
            if(!p.ptr->isOffTheRecord()) {
                if(!p.ptr->persistentStoragePath().isEmpty())
                    QDir(p.ptr->persistentStoragePath()).removeRecursively();
                if(!p.ptr->cachePath().isEmpty())
                    QDir(p.ptr->cachePath()).removeRecursively();
            }
            const QString filename = p.settings->fileName();
            delete p.settings;
            QFile::remove(filename);
        } else if(p.settings != nullptr) {
            p.settings->sync();
            delete p.settings;
        }
    }
}

WebProfile *WebProfileManager::profile(const QString &id) const
{
    // Check if profile exists
    if(profiles.contains(id)) {
        return profiles.value(id).ptr;
    }

    return nullptr;
}

WebProfile *WebProfileManager::add(const QString &id, const QString &path, bool isOffTheRecord)
{
    if(profiles.contains(id)) {
        return nullptr;
    }

    Configuration conf;
    Profile profile;

    if(!path.isEmpty())
        profile.settings = new QSettings(path, QSettings::IniFormat);
    else
        profile.settings = new QSettings;

    // QWebEngineCore cleans up profiles automatically, so no need to set parent
    if(profile.settings->value("otr", isOffTheRecord).toBool()) {
        // name parent
        profile.ptr = new WebProfile(profile.settings->value("name", id).toString(), nullptr);
    } else {
        // storageName name parent
        profile.ptr = new WebProfile(id, profile.settings->value("name", id).toString(), nullptr);
    }

    profile.settings->setParent(profile.ptr);

    connect(profile.ptr, &WebProfile::nameChanged, profile.settings, [profile](const QString &name) {
        profile.settings->setValue("name", name);
    });

    profile.ptr->setSearch(profile.settings->value("search", conf.value<QString>("profile.search").value()).toString());
    connect(profile.ptr, &WebProfile::searchChanged, profile.settings, [profile](const QString &url) {
        profile.settings->setValue("search", url);
    });

    profile.ptr->setHomepage(profile.settings->value("homepage", conf.value<QString>("profile.homepage").value()).toUrl());
    connect(profile.ptr, &WebProfile::homepageChanged, profile.settings, [profile](const QUrl &url) {
        profile.settings->setValue("homepage", url);
    });

    profile.ptr->setNewtab(profile.settings->value("newtab", conf.value<QString>("profile.newtab").value()).toUrl());
    connect(profile.ptr, &WebProfile::newtabChanged, profile.settings, [profile](const QUrl &url) {
        profile.settings->setValue("newtab", url);
    });

    if(profile.settings != nullptr) {
        profile.settings->beginGroup("properties");
        {
            const auto keys = profile.settings->childKeys();
            for(const QString &key : keys) {
                profile.ptr->setProperty(qUtf8Printable(key), profile.settings->value(key));
            }
        }
        profile.settings->endGroup(); // properties
        connect(profile.ptr, &WebProfile::propertyChanged, [profile](const QString &property, const QVariant &value) {
            profile.settings->setValue("properties/" + property, value);
        });

        profile.settings->beginGroup("attributes");
        {
            const auto keys = profile.settings->childKeys();
            auto *settings = profile.ptr->settings();
            for(const QString &key : keys) {
                auto attribute = static_cast<QWebEngineSettings::WebAttribute>(key.toInt());
                settings->setAttribute(attribute, profile.settings->value(key).toBool());
            }
        }
        profile.settings->endGroup();
        connect(profile.ptr, &WebProfile::attributeChanged, [profile](const QWebEngineSettings::WebAttribute attr, const bool value) {
            profile.settings->setValue("attributes/" + QString::number(attr), value);
        });

        // headers
        profile.settings->beginGroup("headers");
        for(const QString &key : profile.settings->childKeys()) {
            profile.ptr->setHttpHeader(key.toLatin1(), profile.settings->value(key).toString().toLatin1());
        }
        profile.settings->endGroup();
        connect(profile.ptr, &WebProfile::headerChanged, [profile](const QString &name, const QString &value) {
            profile.settings->setValue("headers/" + name, value);
        });
        connect(profile.ptr, &WebProfile::headerRemoved, [profile](const QString &name) {
            profile.settings->remove("headers/" + name);
        });

    } // profile.settings != nullptr

    profiles[id] = profile;
    return profile.ptr;
}

void WebProfileManager::deleteProfile(const QString &id)
{
    if(profiles.contains(id)) {
        profiles[id].selfDestruct = true;
    }
}

void profileMenu(QMenu *menu, const std::function<void(WebProfile *)> &callback, WebProfile *current, bool checkable)
{
    auto *group = new QActionGroup(menu);
    QObject::connect(menu, &QMenu::aboutToHide, group, &QActionGroup::deleteLater);

    for(const auto &profile : s_instance->profiles) {
        auto *action = menu->addAction(profile.ptr->name(), profile.ptr, [profile, callback]() {
            callback(profile.ptr);
        });
        action->setCheckable(checkable);
        if(profile.ptr == current)
            action->setChecked(true);
        group->addAction(action);
    }
}