/**
 * SPDX-FileCopyrightText: 2024-2025 Open Mobile Platform LLC <community@omp.ru>
 * SPDX-License-Identifier: Proprietary
 */

#ifndef AURORA_APPLICATION_MC_LIB_CONFIGURATION_MANAGER_HPP
#define AURORA_APPLICATION_MC_LIB_CONFIGURATION_MANAGER_HPP

#include "configuration.hpp"
#include "error.hpp"

#include <QObject>
#include <functional>

namespace Aurora {

namespace Application {

/**
 * @brief API of the Managed Configuration
 * @namespace ManagedConfiguration
 */
namespace ManagedConfiguration {

class ConfigurationManagerPrivate;

/**
 * @brief Manages application configuration.
 * @class ConfigurationManager configuration_manager.hpp <ManagedConfiguration/configuration_manager.hpp>
 *
 * The ConfigurationManager is a singleton class that can be used to get and
 * change application configuration.
 *
 * To work with managed configuration the developer needs to:
 *  1. Set organization name
 *  2. Set application name
 *  3. Create configuration parameter file and install it at /usr/share/<applicationId>/managed-configuration/configuration.xml
 *
 * Example of usage:
 *
 * @code
 *  #include <ManagedConfiguration/configuration_manager.hpp>
 *  #include <QtQuick>
 *  #include <auroraapp.h>
 *
 *  int main(int argc, char *argv[])
 *  {
 *      using namespace Aurora::Application::ManagedConfiguration;
 *
 *      QScopedPointer<QGuiApplication> application(Aurora::Application::application(argc, argv));
 *      application->setOrganizationName(QStringLiteral("ru.example"));
 *      application->setApplicationName(QStringLiteral("Example"));
 *
 *      auto configurationManager = ConfigurationManager::instance();
 *      QObject::connect(configurationManager,
 *                      &ConfigurationManager::configurationChanged,
 *                      [configurationManager](const Configuration &c) {
 *          qInfo() << "New managed configuration";
 *      });
 *
 *      auto callback = [](const Configuration &c, const Error &error) {
 *          if (error) {
 *              qCritical() << "Failed to get initial configuration for application" << error.message();
 *          } else {
 *              qInfo() << "Get configuration for the application!";
 *          }
 *      };
 *      configurationManager->getConfiguration(callback);
 *
 *      Configuration configuration;
 *      configuration.set("HelloString", "Hello, Aurora OS!");
 *      configuration.set("AuroraMajorVersion", 5);
 *      configuration.set("IsAuroraCool", true);
 *      configurationManager->changeConfiguration(configuration, [](const Error &error) {
 *          if (error) {
 *              qWarning() << "Failed to update configuration" << error.message();
 *          }
 *      });
 *
 *      // ...
 *      // ...
 *      // ...
 *
 *      return application->exec();
 *  }
 *
 * @endcode
 *
 */
class AURORA_APPLICATION_MC_EXPORT ConfigurationManager: public QObject
{
    Q_OBJECT

public:
    /**
     * @brief Type for callback for ConfigurationManager::getConfiguration method.
     */
    using GetConfigurationCallback = std::function<void(const Configuration &, const Error &)>;

    /**
     * @brief Type for callback for ConfigurationManager::changeConfiguration method.
     */
    using ChangeConfigurationCallback = std::function<void(const Error &)>;

    /**
     * @brief Return ConfigurationManager singleton.
     */
    static ConfigurationManager *instance();

    /**
     * @brief Return configuration.
     *
     * The configuration contains only the parameters that have been set.
     * If any configuration parameter does not have a default value and was
     * not set by the administrator or application, it will not be in the configuration.
     *
     * @param callback Handler function when initial configuration is received.
     *
     * @note The method must be used once when the application starts. All
     *       configuration updates while the application is running must be received
     *       in the ConfigurationManager::configurationChanged signal.
     */
    void getConfiguration(const GetConfigurationCallback &callback);

    /**
     * @brief Change configuration.
     *
     * @param configuration Configuration with changed parameters.
     * @param callback Handler function when configuration is changed.
     *
     * @note It's enough to specify the parameters that need to be changed.
     *       It's not necessary to send the entire configuration.
     */
    void changeConfiguration(const Configuration &configuration,
                             const ChangeConfigurationCallback &callback);

Q_SIGNALS:
    /**
     * @brief A signal to notify application of a change in the application configuration.
     *
     * Return the configuration that would have been returned
     * by calling the ConfigurationManager::getConfiguration method.
     *
     * @param configuration Update application configuration.
     */
    void configurationChanged(const Configuration &configuration);

private:
    ConfigurationManager();
    ~ConfigurationManager();

    Q_DECLARE_PRIVATE(ConfigurationManager)
    QScopedPointer<ConfigurationManagerPrivate> d_ptr;
};

} /* namespace ManagedConfiguration */

} /* namespace Application */

} /* namespace Aurora */

#endif /* AURORA_APPLICATION_MC_LIB_CONFIGURATION_MANAGER_HPP */
