// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later

#ifndef APPLICATIONMANAGER1SERVICE_H
#define APPLICATIONMANAGER1SERVICE_H

#include <QObject>
#include <QDBusObjectPath>
#include <QDBusUnixFileDescriptor>
#include <QSharedPointer>
#include <memory>
#include <QMap>
#include <QHash>
#include <QFileSystemWatcher>
#include <QTimer>
#include "applicationmanagerstorage.h"
#include "dbus/jobmanager1service.h"
#include "dbus/mimemanager1service.h"
#include "desktopentry.h"
#include "identifier.h"
#include "compatibilitymanager.h"
#include "prelaunchsplashhelper.h"

Q_DECLARE_LOGGING_CATEGORY(DDEAM)

class ApplicationService;

class UnitResultWatcher : public QObject
{
    Q_OBJECT
public:
    explicit UnitResultWatcher(const QDBusObjectPath &unitPath, QObject *parent = nullptr)
        : QObject(parent), m_unitPath(unitPath) {}

public Q_SLOTS:
    void handlePropertyChanged(const QString &interface, const QVariantMap &changed, const QStringList &invalidated)
    {
        if ((interface == QStringLiteral("org.freedesktop.systemd1.Unit")
             || interface == QStringLiteral("org.freedesktop.systemd1.Service"))
            && changed.contains(QStringLiteral("Result"))) {
            emit resultReady(m_unitPath, changed.value(QStringLiteral("Result")).toString());
        }
    }

Q_SIGNALS:
    void resultReady(const QDBusObjectPath &unitPath, const QString &result);

private:
    QDBusObjectPath m_unitPath;
};

class ApplicationManager1Service final : public QObject, protected QDBusContext
{
    Q_OBJECT
public:
    explicit ApplicationManager1Service(std::unique_ptr<Identifier> ptr,
                                        std::weak_ptr<ApplicationManager1Storage> storage) noexcept;
    ~ApplicationManager1Service() override;
    ApplicationManager1Service(const ApplicationManager1Service &) = delete;
    ApplicationManager1Service(ApplicationManager1Service &&) = delete;
    ApplicationManager1Service &operator=(const ApplicationManager1Service &) = delete;
    ApplicationManager1Service &operator=(ApplicationManager1Service &&) = delete;

    Q_PROPERTY(QList<QDBusObjectPath> List READ list NOTIFY listChanged)
    [[nodiscard]] QList<QDBusObjectPath> list() const;

    void initService(QDBusConnection &connection) noexcept;
    void reloadMimeInfos() noexcept;
    QSharedPointer<ApplicationService> addApplication(DesktopFile desktopFileSource) noexcept;
    void removeOneApplication(const QString &appId) noexcept;
    void removeAllApplication() noexcept;
    [[nodiscard]] QHash<QDBusObjectPath, QSharedPointer<ApplicationService>>
    findApplicationsByIds(const QStringList &appIds) const noexcept;
    void updateApplication(const QSharedPointer<ApplicationService> &destApp, DesktopFile desktopFile) noexcept;

    [[nodiscard]] const auto &Applications() const noexcept { return m_applicationList; }
    [[nodiscard]] JobManager1Service &jobManager() noexcept { return *m_jobManager; }
    [[nodiscard]] const JobManager1Service &jobManager() const noexcept { return *m_jobManager; }
    [[nodiscard]] const QStringList &applicationHooks() const noexcept { return m_hookElements; }
    [[nodiscard]] MimeManager1Service &mimeManager() noexcept { return *m_mimeManager; }
    [[nodiscard]] const MimeManager1Service &mimeManager() const noexcept { return *m_mimeManager; }
    [[nodiscard]] const QStringList &systemdPathEnv() const noexcept { return m_systemdPathEnv; }
    [[nodiscard]] QSharedPointer<CompatibilityManager> getCompatibilityManager() const noexcept { return m_compatibilityManager; }
    [[nodiscard]] PrelaunchSplashHelper *splashHelper() const noexcept { return m_splashHelper.get(); }
    [[nodiscard]] bool isNewSession() const noexcept { return m_isNewSession; }
    [[nodiscard]] bool isStartupPhase() const noexcept { return m_startupPhase; }

public Q_SLOTS:
    QDBusObjectPath executeCommand(const QString &program,
                                   const QStringList &arguments,
                                   const QString &type,
                                   const QString &runId,
                                   const QMap<QString, QString> &envVars,
                                   const QString &workdir) noexcept;
    QString Identify(const QDBusUnixFileDescriptor &pidfd,
                     QDBusObjectPath &instance,
                     ObjectInterfaceMap &application_instance_info) const noexcept;
    void ReloadApplications();
    QString addUserApplication(const QVariantMap &desktop_file, const QString &name) noexcept;
    void deleteUserApplication(const QString &app_id) noexcept;
    [[nodiscard]] ObjectMap GetManagedObjects() const;

Q_SIGNALS:
    void InterfacesAdded(const QDBusObjectPath &object_path, const ObjectInterfaceMap &interfaces);
    void InterfacesRemoved(const QDBusObjectPath &object_path, const QStringList &interfaces);
    void listChanged();

private Q_SLOTS:
    void doReloadApplications();
    void onUnitResultReady(const QDBusObjectPath &unitPath, const QString &result);

private:
    bool m_startupPhase{true};
    bool m_isNewSession{false};
    std::unique_ptr<Identifier> m_identifier;
    std::weak_ptr<ApplicationManager1Storage> m_storage;
    std::unique_ptr<MimeManager1Service> m_mimeManager;
    std::unique_ptr<JobManager1Service> m_jobManager;
    QStringList m_hookElements;
    QStringList m_systemdPathEnv;
    QFileSystemWatcher m_watcher;
    QTimer m_reloadTimer;
    bool m_isReloading{false};
    bool m_pendingReload{false};
    QHash<QString, QSharedPointer<ApplicationService>> m_applicationList;
    QHash<QString, QString> m_unitResults;
    QSharedPointer<CompatibilityManager> m_compatibilityManager;
    std::unique_ptr<PrelaunchSplashHelper> m_splashHelper;

    void scanMimeInfos() noexcept;
    void scanApplications() noexcept;
    void scanInstances() noexcept;
    void updateAutostartStatus() noexcept;
    void loadHooks() noexcept;
    void addInstanceToApplication(const QString &unitName, const QDBusObjectPath &systemdUnitPath) noexcept;
    void addInstanceToApplication(UnitInfo info, const QDBusObjectPath &systemdUnitPath) noexcept;
    void removeInstanceFromApplication(const QString &unitName, const QDBusObjectPath &systemdUnitPath) noexcept;
    QSharedPointer<ApplicationService> addApplication(DesktopFile desktopFileSource, std::unique_ptr<DesktopEntry> entry) noexcept;
};

#endif
