#include "application/config/ConfigPersistence.h"

#include <string>
#include <utility>
#include <vector>

#include "foundation/diagnostics/Logger.h"

namespace ta::application::config
{
    ConfigPersistence::ConfigPersistence(ConfigRegistry& registry,
                                         ta::application::IConfigPersistence& persistence,
                                         ta::application::ILocaleController& locale,
                                         KeybindValue& menu_key,
                                         SelectValue& language) noexcept
        : registry_(registry), persistence_(persistence), locale_(locale),
          menu_key_(menu_key), language_(language)
    {
    }

    void ConfigPersistence::load()
    {
        for (ConfigValue* value : registry_.values())
        {
            if (!value)
                continue;
            const ApplyResult applied = value->apply_serialized(value->get_default());
            if (!applied)
                ta::foundation::logger::error("CONFIG", "invalid compiled default | key=" + value->get_config_key());
        }

        const ta::application::ConfigStateLoadResult result = persistence_.load_state();
        if (result.status != ta::application::ConfigStateLoadStatus::Loaded)
            return;

        for (const auto& [key, serialized] : result.state.values)
        {
            ConfigValue* value = registry_.find_value(key);
            if (!value)
            {
                ta::foundation::logger::warn("CONFIG", "unknown persisted key ignored | key=" + key);
                continue;
            }

            const ApplyResult applied = value->apply_serialized(serialized);
            if (!applied)
            {
                ta::foundation::logger::warn(
                    "CONFIG",
                    "persisted value rejected | key=" + key + " | error=" + std::string(to_string(applied.error)));
            }
        }

        if (result.state.menu_key != 0)
        {
            const ApplyResult applied = menu_key_.apply(ConfigValueData{result.state.menu_key});
            if (!applied)
                ta::foundation::logger::warn("CONFIG", "persisted menu key rejected");
        }

        const int language_index = locale_.index_for_id(result.state.language_id);
        if (language_index >= 0)
        {
            const ApplyResult applied = language_.apply(ConfigValueData{language_index});
            if (!applied)
                ta::foundation::logger::warn("CONFIG", "persisted language rejected");
        }
    }

    bool ConfigPersistence::save() const
    {
        ta::application::ConfigApplicationState state{};
        state.schema_version = ta::application::current_config_schema_version;
        state.menu_key = menu_key_.get_value();
        state.language_id = locale_.id_for_index(language_.get_value());
        state.values.reserve(registry_.values().size());
        for (ConfigValue* value : registry_.values())
        {
            if (value)
                state.values.emplace(value->get_config_key(), value->get());
        }
        return persistence_.save_state(state);
    }
}
