/*
 * 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
 */

#ifndef SMOLBOTE_WEBENGINEPROFILE_H
#define SMOLBOTE_WEBENGINEPROFILE_H

#include <QMap>
#include <QNetworkCookie>
#include <QSettings>
#include <QString>
#include <QUrl>
#include <QVariant>
#include <QVector>
#include <QWebEngineCookieStore>
#include <QWebEngineProfile>
#include <QWebEngineSettings>

class UrlRequestInterceptor;
class WebProfile : public QWebEngineProfile
{
    friend class UrlRequestInterceptor;

    Q_OBJECT

    Q_PROPERTY(QString id READ getId CONSTANT)
    Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
    Q_PROPERTY(QString search READ search WRITE setSearch NOTIFY searchChanged)
    Q_PROPERTY(QUrl homepage READ homepage WRITE setHomepage NOTIFY homepageChanged)
    Q_PROPERTY(QUrl newtab READ newtab WRITE setNewtab NOTIFY newtabChanged)

    // QWebEngineProfile should-be properties
    Q_PROPERTY(QString cachePath READ cachePath WRITE setCachePath NOTIFY propertyChanged)
    Q_PROPERTY(QString persistentStoragePath READ persistentStoragePath WRITE setPersistentStoragePath NOTIFY propertyChanged)
    Q_PROPERTY(int persistentCookiesPolicy READ persistentCookiesPolicy WRITE setPersistentCookiesPolicy NOTIFY propertyChanged)

    Q_PROPERTY(QString httpAcceptLanguage READ httpAcceptLanguage WRITE setHttpAcceptLanguage NOTIFY propertyChanged)
    Q_PROPERTY(int httpCacheMaximumSize READ httpCacheMaximumSize WRITE setHttpCacheMaximumSize NOTIFY propertyChanged)
    Q_PROPERTY(int httpCacheType READ httpCacheType WRITE setHttpCacheType NOTIFY propertyChanged)
    Q_PROPERTY(QString httpUserAgent READ httpUserAgent WRITE setHttpUserAgent NOTIFY propertyChanged)

    Q_PROPERTY(bool spellCheckEnabled READ isSpellCheckEnabled WRITE setSpellCheckEnabled NOTIFY propertyChanged)

    // more custom properties
    Q_PROPERTY(QList<QVariant> cookies READ cookies WRITE setCookies NOTIFY cookiesChanged)
    Q_PROPERTY(QMap<QString, QVariant> headers READ headers WRITE setHeaders NOTIFY headersChanged)

signals:
    void nameChanged(const QString &name);
    void searchChanged(const QString &url);
    void homepageChanged(const QUrl &url);
    void newtabChanged(const QUrl &url);

    void propertyChanged(const QString &name, const QVariant &value);
    void attributeChanged(QWebEngineSettings::WebAttribute attribute, bool value);

    void cookiesChanged();
    void headersChanged();
    void headerChanged(const QString &name, const QString &value);
    void headerRemoved(const QString &name);

public:
    [[nodiscard]] static QSettings *load(const QString &path, const QString &search = QString(), const QUrl &homepage = QUrl(), const QUrl &newtab = QUrl());
    [[nodiscard]] static WebProfile *load(const QString &id, QSettings *settings, bool isOffTheRecord = true);

    WebProfile(const WebProfile &) = delete;
    WebProfile &operator=(const WebProfile &) = delete;
    WebProfile(WebProfile &&) = delete;
    WebProfile &operator=(WebProfile &&) = delete;

    static WebProfile *defaultProfile();
    static void setDefaultProfile(WebProfile *profile);

    ~WebProfile() override = default;

    [[nodiscard]] QString getId() const
    {
        return m_id;
    }
    [[nodiscard]] QString name() const
    {
        return m_name;
    }
    void setName(const QString &name)
    {
        m_name = name;
        emit nameChanged(name);
    }

    [[nodiscard]] QList<QVariant> cookies() const
    {
        QList<QVariant> r;
        for(const auto &cookie : m_cookies) {
            r.append(cookie.toRawForm());
        }
        return r;
    }

    [[nodiscard]] QString search() const
    {
        return m_search;
    }
    void setSearch(const QString &url)
    {
        m_search = url;
        emit searchChanged(m_search);
    }

    [[nodiscard]] QUrl homepage() const
    {
        return m_homepage;
    }
    void setHomepage(const QUrl &url)
    {
        m_homepage = url;
        emit homepageChanged(m_homepage);
    }

    [[nodiscard]] QUrl newtab() const
    {
        return m_newtab;
    }
    void setNewtab(const QUrl &url)
    {
        m_newtab = url;
        emit newtabChanged(m_newtab);
    }

    void setCachePath(const QString &path)
    {
        QWebEngineProfile::setCachePath(path);
        emit propertyChanged("cachePath", path);
    }
    void setPersistentStoragePath(const QString &path)
    {
        QWebEngineProfile::setPersistentStoragePath(path);
        emit propertyChanged("persistentStoragePath", path);
    }
    void setPersistentCookiesPolicy(int policy)
    {
        QWebEngineProfile::setPersistentCookiesPolicy(static_cast<QWebEngineProfile::PersistentCookiesPolicy>(policy));
        emit propertyChanged("persistentCookiesPolicy", policy);
    }

    void setHttpAcceptLanguage(const QString &httpAcceptLanguage)
    {
        QWebEngineProfile::setHttpAcceptLanguage(httpAcceptLanguage);
        emit propertyChanged("httpAcceptLanguage", httpAcceptLanguage);
    }
    void setHttpCacheMaximumSize(int maxSize)
    {
        QWebEngineProfile::setHttpCacheMaximumSize(maxSize);
        emit propertyChanged("httpCacheMaximumSize", maxSize);
    }
    void setHttpCacheType(int type)
    {
        QWebEngineProfile::setHttpCacheType(static_cast<QWebEngineProfile::HttpCacheType>(type));
        emit propertyChanged("httpCacheType", type);
    }
    void setHttpUserAgent(const QString &userAgent)
    {
        QWebEngineProfile::setHttpUserAgent(userAgent);
        emit propertyChanged("httpUserAgent", userAgent);
    }
    void setHttpHeader(const QString &name, const QString &value)
    {
        m_headers[name.toLatin1()] = value.toLatin1();
        emit headerChanged(name, value);
    }
    void removeHttpHeader(const QString &name)
    {
        if(m_headers.contains(name.toLatin1())) {
            m_headers.remove(name.toLatin1());
            emit headerRemoved(name);
        }
    }
    [[nodiscard]] QMap<QString, QVariant> headers() const
    {
        QMap<QString, QVariant> r;
        auto it = m_headers.constBegin();
        while(it != m_headers.constEnd()) {
            r.insert(QString(it.key()), QVariant(it.value()));
            ++it;
        }
        return r;
    }
    void setSpellCheckEnabled(bool enable)
    {
        QWebEngineProfile::setSpellCheckEnabled(enable);
        emit propertyChanged("spellCheckEnabed", enable);
    }

    void setUrlRequestInterceptor(QWebEngineUrlRequestInterceptor *interceptor)
    {
        m_filters.append(interceptor);
    }

public slots:
    void setCookies(const QList<QVariant> &cookies)
    {
        auto *store = cookieStore();
        store->deleteAllCookies();
        for(const auto &data : cookies) {
            for(auto &cookie : QNetworkCookie::parseCookies(data.toByteArray())) {
                store->setCookie(cookie);
            }
        }
        emit cookiesChanged();
    }
    void setHeaders(const QMap<QString, QVariant> &headers)
    {
        m_headers.clear();
        auto it = headers.constBegin();
        while(it != headers.constEnd()) {
            m_headers.insert(it.key().toLatin1(), it.value().toByteArray());
            ++it;
        }
        emit headersChanged();
    }

protected:
    // off-the-record constructor
    explicit WebProfile(const QString &id, QObject *parent = nullptr);
    // default constructor
    explicit WebProfile(const QString &id, const QString &storageName, QObject *parent = nullptr);

    const QString m_id;
    QString m_name;
    QString m_search = QString("about:blank");
    QUrl m_homepage = QUrl("about:blank");
    QUrl m_newtab = QUrl("about:blank");

    QVector<QWebEngineUrlRequestInterceptor *> m_filters;
    QList<QNetworkCookie> m_cookies;
    QMap<QByteArray, QByteArray> m_headers;
};

#endif // SMOLBOTE_WEBENGINEPROFILE_H