#include <QtTest>
#include <QSignalSpy>
#include <QDBusConnection>
#ifdef signals
#undef signals
#endif
#include <gio/gio.h>
#define private public
#include "../../../src/main/global-settings.h"
#undef private

using namespace Sidebar;

class FakeStatusManagerService : public QObject
{
    Q_OBJECT
    Q_CLASSINFO("D-Bus Interface", "com.kylin.statusmanager.interface")

public:
    explicit FakeStatusManagerService(QObject *parent = nullptr) : QObject(parent) {}

public slots:
    bool get_current_tabletmode() const { return true; }

Q_SIGNALS:
    void mode_change_signal(bool isTabletMode);
};

class FakeSettingsDaemonService : public QObject
{
    Q_OBJECT
    Q_CLASSINFO("D-Bus Interface", "org.ukui.SettingsDaemon.GlobalSignal")

public:
    explicit FakeSettingsDaemonService(QObject *parent = nullptr) : QObject(parent) {}

public slots:
    QString getUKUILiteAnimation() const { return QStringLiteral("lite"); }

Q_SIGNALS:
    void UKUILiteChanged(const QMap<QString, QVariant> &map);
};


class globalSettings_test : public QObject
{
    Q_OBJECT

private slots:
    // GlobalSettings 测试
    void test_globalInstance();
    void test_globalInstance_returnsSameInstance();
    void test_getValue_existingKey();
    void test_getValue_nonExistingKey();
    void test_getKeys_notEmpty();
    void test_getKeys_containsExpectedKeys();
    void test_getPanelLength_invalidScreen();
    void test_updateTabletStatus_true();
    void test_updateTabletStatus_false();
    void test_updateTabletStatus_emitsSignal();
    void test_updateIsLiteMode_withAnimation();
    void test_updateIsLiteMode_withLiteAnimation();
    void test_updateIsLiteMode_emptyMap();
    void test_updateIsLiteMode_noAnimationKey();
    void test_initUSDCenterSettings_defaultValue();
    void test_getPanelLength_existingScreen_ifAvailable();

    // SettingMonitor 测试
    void test_settingMonitor_create();
    void test_settingMonitor_defaultKeys();
    void test_settingMonitor_setKeys();
    void test_settingMonitor_setKeys_emitsSignal();
    void test_settingMonitor_setKeys_sameKeys();
    void test_settingMonitor_getValue();
    void test_settingMonitor_getValue_nonExisting();
    void test_settingMonitor_valueChangedSlot_matchingKey();
    void test_settingMonitor_valueChangedSlot_nonMatchingKey();
    void test_directGlobalSettings_withFakeDbusServices();
    void test_directGlobalSettings_destructor();
};

// 辅助函数：调用 GlobalSettings 的私有槽
static void invokeUpdateTabletStatus(GlobalSettings *settings, bool isTabletMode)
{
    QMetaObject::invokeMethod(settings, "updateTabletStatus", Q_ARG(bool, isTabletMode));
}

static void invokeUpdateIsLiteMode(GlobalSettings *settings, const QMap<QString, QVariant> &map)
{
    QMetaObject::invokeMethod(settings, "updateIsLiteMode", Q_ARG(QVariantMap, map));
}

void globalSettings_test::test_globalInstance()
{
    GlobalSettings *settings = GlobalSettings::globalInstance();
    QVERIFY(settings != nullptr);
}

void globalSettings_test::test_globalInstance_returnsSameInstance()
{
    GlobalSettings *settings1 = GlobalSettings::globalInstance();
    GlobalSettings *settings2 = GlobalSettings::globalInstance();
    QCOMPARE(settings1, settings2);
}

void globalSettings_test::test_getValue_existingKey()
{
    GlobalSettings *settings = GlobalSettings::globalInstance();
    QVariant value = settings->getValue(UKUI_STYLE_NAME_KEY);
    QVERIFY(value.isValid());
}

void globalSettings_test::test_getValue_nonExistingKey()
{
    GlobalSettings *settings = GlobalSettings::globalInstance();
    QVariant value = settings->getValue("nonexistent-key-12345");
    QVERIFY(!value.isValid());
}

void globalSettings_test::test_getKeys_notEmpty()
{
    GlobalSettings *settings = GlobalSettings::globalInstance();
    QStringList keys = settings->getKeys();
    QVERIFY(!keys.isEmpty());
}

void globalSettings_test::test_getKeys_containsExpectedKeys()
{
    GlobalSettings *settings = GlobalSettings::globalInstance();
    QStringList keys = settings->getKeys();
    QVERIFY(keys.contains(UKUI_STYLE_NAME_KEY));
    QVERIFY(keys.contains(UKUI_PANEL_POSITION_KEY));
    QVERIFY(keys.contains(UKUI_PANEL_SIZE_KEY));
    QVERIFY(keys.contains(TABLET_MODE));
    QVERIFY(keys.contains(CONTROL_CENTER_EFFECT));
    QVERIFY(keys.contains(CONTROL_CENTER_TRANSPARENCY_KEY));
}

void globalSettings_test::test_getPanelLength_invalidScreen()
{
    GlobalSettings *settings = GlobalSettings::globalInstance();
    int length = settings->getPanelLength("nonexistent-screen");
    QCOMPARE(length, -1);
}

void globalSettings_test::test_updateTabletStatus_true()
{
    GlobalSettings *settings = GlobalSettings::globalInstance();
    invokeUpdateTabletStatus(settings, true);
    QCOMPARE(settings->getValue(TABLET_MODE).toBool(), true);
}

void globalSettings_test::test_updateTabletStatus_false()
{
    GlobalSettings *settings = GlobalSettings::globalInstance();
    invokeUpdateTabletStatus(settings, false);
    QCOMPARE(settings->getValue(TABLET_MODE).toBool(), false);
}

void globalSettings_test::test_updateTabletStatus_emitsSignal()
{
    GlobalSettings *settings = GlobalSettings::globalInstance();
    QSignalSpy spy(settings, &GlobalSettings::valueChanged);
    invokeUpdateTabletStatus(settings, true);
    QCOMPARE(spy.count(), 1);
    QCOMPARE(spy.at(0).at(0).toString(), QString(TABLET_MODE));

    // 恢复
    invokeUpdateTabletStatus(settings, false);
}

void globalSettings_test::test_updateIsLiteMode_withAnimation()
{
    GlobalSettings *settings = GlobalSettings::globalInstance();
    QSignalSpy spy(settings, &GlobalSettings::valueChanged);

    QMap<QString, QVariant> map;
    map.insert("animation", "normal");
    invokeUpdateIsLiteMode(settings, map);

    QCOMPARE(settings->getValue(IS_LITE_MODE).toBool(), false);
    QCOMPARE(spy.count(), 1);
}

void globalSettings_test::test_updateIsLiteMode_withLiteAnimation()
{
    GlobalSettings *settings = GlobalSettings::globalInstance();
    QSignalSpy spy(settings, &GlobalSettings::valueChanged);

    QMap<QString, QVariant> map;
    map.insert("animation", "lite");
    invokeUpdateIsLiteMode(settings, map);

    QCOMPARE(settings->getValue(IS_LITE_MODE).toBool(), true);
    QCOMPARE(spy.count(), 1);
}

void globalSettings_test::test_updateIsLiteMode_emptyMap()
{
    GlobalSettings *settings = GlobalSettings::globalInstance();
    QSignalSpy spy(settings, &GlobalSettings::valueChanged);

    QMap<QString, QVariant> map;
    invokeUpdateIsLiteMode(settings, map);

    QCOMPARE(spy.count(), 0);
}

void globalSettings_test::test_updateIsLiteMode_noAnimationKey()
{
    GlobalSettings *settings = GlobalSettings::globalInstance();
    QSignalSpy spy(settings, &GlobalSettings::valueChanged);

    QMap<QString, QVariant> map;
    map.insert("other-key", "value");
    invokeUpdateIsLiteMode(settings, map);

    QCOMPARE(spy.count(), 0);
}

void globalSettings_test::test_initUSDCenterSettings_defaultValue()
{
    GlobalSettings *settings = GlobalSettings::globalInstance();
    settings->initUSDCenterSettings();
    const QVariant value = settings->getValue(USD_SCALING_FACTOR_KEY);
    QVERIFY(value.isValid());
}

void globalSettings_test::test_getPanelLength_existingScreen_ifAvailable()
{
    GlobalSettings *settings = GlobalSettings::globalInstance();
    if (!settings->isKeysContain("panellength")) {
        QSKIP("panellength key is not available in current runtime schema.");
    }

    const QMap<QString, QVariant> panelLengthMap = settings->getPanelLengthMap();
    if (panelLengthMap.isEmpty()) {
        QSKIP("panellength map is empty in current runtime environment.");
    }

    const QString screenName = panelLengthMap.firstKey();
    QVERIFY(settings->getPanelLength(screenName) >= 0);
}

void globalSettings_test::test_settingMonitor_create()
{
    SettingMonitor monitor;
    QVERIFY(true);
}

void globalSettings_test::test_settingMonitor_defaultKeys()
{
    SettingMonitor monitor;
    QVERIFY(monitor.keys().isEmpty());
}

void globalSettings_test::test_settingMonitor_setKeys()
{
    SettingMonitor monitor;
    QStringList keys;
    keys << "key1" << "key2";
    monitor.setKeys(keys);
    QCOMPARE(monitor.keys(), keys);
}

void globalSettings_test::test_settingMonitor_setKeys_emitsSignal()
{
    SettingMonitor monitor;
    QSignalSpy spy(&monitor, &SettingMonitor::keysChanged);
    QStringList keys;
    keys << "key1";
    monitor.setKeys(keys);
    QCOMPARE(spy.count(), 1);
}

void globalSettings_test::test_settingMonitor_setKeys_sameKeys()
{
    SettingMonitor monitor;
    QStringList keys;
    keys << "key1" << "key2";
    monitor.setKeys(keys);
    QSignalSpy spy(&monitor, &SettingMonitor::keysChanged);
    monitor.setKeys(keys);
    QCOMPARE(spy.count(), 0);
}

void globalSettings_test::test_settingMonitor_getValue()
{
    SettingMonitor monitor;
    QVariant value = monitor.getValue(UKUI_STYLE_NAME_KEY);
    QVERIFY(value.isValid());
}

void globalSettings_test::test_settingMonitor_getValue_nonExisting()
{
    SettingMonitor monitor;
    QVariant value = monitor.getValue("nonexistent-key-12345");
    QVERIFY(!value.isValid());
}

void globalSettings_test::test_settingMonitor_valueChangedSlot_matchingKey()
{
    SettingMonitor monitor;
    QStringList keys;
    keys << TABLET_MODE;
    monitor.setKeys(keys);

    QSignalSpy spy(&monitor, &SettingMonitor::valueChanged);

    invokeUpdateTabletStatus(GlobalSettings::globalInstance(), true);

    QCOMPARE(spy.count(), 1);
    QCOMPARE(spy.at(0).at(0).toString(), QString(TABLET_MODE));

    // 恢复
    invokeUpdateTabletStatus(GlobalSettings::globalInstance(), false);
}

void globalSettings_test::test_settingMonitor_valueChangedSlot_nonMatchingKey()
{
    SettingMonitor monitor;
    QStringList keys;
    keys << "some-other-key";
    monitor.setKeys(keys);

    QSignalSpy spy(&monitor, &SettingMonitor::valueChanged);

    invokeUpdateTabletStatus(GlobalSettings::globalInstance(), true);

    QCOMPARE(spy.count(), 0);

    // 恢复
    invokeUpdateTabletStatus(GlobalSettings::globalInstance(), false);
}

void globalSettings_test::test_directGlobalSettings_withFakeDbusServices()
{
    if (!QDBusConnection::sessionBus().isConnected()) {
        QSKIP("Session bus is not available.");
    }

    FakeStatusManagerService statusService;
    FakeSettingsDaemonService settingsService;

    if (!QDBusConnection::sessionBus().registerService(DBUS_STATUS_MANAGER_IF)) {
        QSKIP("Status manager service name is already owned in current session.");
    }
    if (!QDBusConnection::sessionBus().registerObject("/",
                                                      &statusService,
                                                      QDBusConnection::ExportAllSlots | QDBusConnection::ExportAllSignals)) {
        QDBusConnection::sessionBus().unregisterService(DBUS_STATUS_MANAGER_IF);
        QSKIP("Status manager object path is already occupied in current session.");
    }
    if (!QDBusConnection::sessionBus().registerService("org.ukui.SettingsDaemon")) {
        QDBusConnection::sessionBus().unregisterObject("/");
        QDBusConnection::sessionBus().unregisterService(DBUS_STATUS_MANAGER_IF);
        QSKIP("Settings daemon service name is already owned in current session.");
    }
    if (!QDBusConnection::sessionBus().registerObject("/GlobalSignal",
                                                      &settingsService,
                                                      QDBusConnection::ExportAllSlots | QDBusConnection::ExportAllSignals)) {
        QDBusConnection::sessionBus().unregisterObject("/");
        QDBusConnection::sessionBus().unregisterService(DBUS_STATUS_MANAGER_IF);
        QDBusConnection::sessionBus().unregisterService("org.ukui.SettingsDaemon");
        QSKIP("Settings daemon object path is already occupied in current session.");
    }

    auto *settings = new GlobalSettings();
    QCOMPARE(settings->getValue(TABLET_MODE).toBool(), true);
    QCOMPARE(settings->getValue(IS_LITE_MODE).toBool(), true);

    QSignalSpy spy(settings, &GlobalSettings::valueChanged);

    emit statusService.mode_change_signal(false);
    QTRY_VERIFY(spy.count() >= 1);
    QCOMPARE(settings->getValue(TABLET_MODE).toBool(), false);

    QMap<QString, QVariant> map;
    map.insert("animation", "normal");
    emit settingsService.UKUILiteChanged(map);
    QTRY_COMPARE(settings->getValue(IS_LITE_MODE).toBool(), false);

    delete settings;

    QDBusConnection::sessionBus().unregisterObject("/");
    QDBusConnection::sessionBus().unregisterService(DBUS_STATUS_MANAGER_IF);
    QDBusConnection::sessionBus().unregisterObject("/GlobalSignal");
    QDBusConnection::sessionBus().unregisterService("org.ukui.SettingsDaemon");
}

void globalSettings_test::test_directGlobalSettings_destructor()
{
    auto *settings = new GlobalSettings();

    GSettingsSchemaSource *source = g_settings_schema_source_get_default();
    GSettingsSchema *schema = g_settings_schema_source_lookup(source, "org.ukui.panel.settings", true);
    if (schema != nullptr) {
        GSettings *panelSettings = g_settings_new_with_path("org.ukui.panel.settings", "/org/ukui/panel/settings/");
        GVariantBuilder builder;
        g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}"));
        g_variant_builder_add(&builder, "{sv}", "TestScreen", g_variant_new_uint32(88));
        GVariant *value = g_variant_builder_end(&builder);
        g_settings_set_value(panelSettings, "panellength", value);
        QCOMPARE(settings->getPanelLength("TestScreen"), 88);
        g_object_unref(panelSettings);
        g_settings_schema_unref(schema);
    }

    delete settings;
    QVERIFY(true);
}

QTEST_MAIN(globalSettings_test)

#include "global-settings-test.moc"
