#include "application/config/ConfigService.h"

#include <algorithm>
#include <utility>

#include "application/config/ConfigSnapshotBuilder.h"
#include "foundation/diagnostics/Logger.h"

namespace ta::application::config
{
    ConfigService::ConfigService(const std::span<ConfigOwner* const> owners,
                                 ta::application::IConfigPersistence& persistence,
                                 ta::application::ILocaleController& locale,
                                 ta::application::UiActionDispatcher& actions)
        : ConfigOwner("config", "Config", ConfigCategory::Config),
          menu_key_(*this, "menu_key", 0x2D, "Menu Key", false),
          language_(*this, "language", locale.default_index(), locale.option_labels(), "Language"),
          owners_(owners.begin(), owners.end()),
          locale_(locale),
          registry_(*this, owners_),
          command_processor_(registry_, diagnostics_, actions),
          persistence_(registry_, persistence, locale_, menu_key_.get(), language_.get())
    {
    }

    void ConfigService::initialize()
    {
        persistence_.load();
        apply_language();
        saved_language_index_ = language_->get_value();
        if (persistence_.save())
        {
            saved_revision_ = registry_.revision();
            persistence_save_error_logged_ = false;
        }
        else
        {
            persistence_save_error_logged_ = true;
            ta::foundation::logger::warn("CONFIG", "state creation failed");
        }
        publish_settings();
        publish_ui();
    }

    void ConfigService::update()
    {
        const int language_index = language_->get_value();
        if (language_index != saved_language_index_)
            apply_language();
        saved_language_index_ = language_index;

        const std::uint64_t revision = registry_.revision();
        if (saved_revision_ != revision)
        {
            if (persistence_.save())
            {
                saved_revision_ = revision;
                persistence_save_error_logged_ = false;
            }
            else if (!persistence_save_error_logged_)
            {
                persistence_save_error_logged_ = true;
                ta::foundation::logger::error("CONFIG", "state save failed");
            }
        }
        publish_settings();
        publish_ui();
    }

    void ConfigService::submit(ta::ui::ConfigCommand command)
    {
        diagnostics_.mark_command_requested();
        commands_.push(std::move(command));
    }

    void ConfigService::apply_pending_commands()
    {
        while (std::optional<ta::ui::ConfigCommand> command = commands_.try_pop())
            command_processor_.apply(std::move(*command));
    }

    std::optional<ConfigSettingsSnapshot> ConfigService::latest_settings() const
    {
        return settings_snapshot_.latest();
    }

    std::shared_ptr<const ta::ui::ConfigUiSnapshot> ConfigService::latest_ui_snapshot() const noexcept
    {
        return ui_snapshot_.latest();
    }

    void ConfigService::apply_language()
    {
        if (!locale_.select_index(language_->get_value()))
            ta::foundation::logger::warn("RESOURCE", "selected UI locale could not be applied");
    }

    void ConfigService::publish_settings()
    {
        const int current = menu_key_->get_value();
        if (published_menu_key_ && *published_menu_key_ == current)
            return;
        settings_snapshot_.publish({current});
        published_menu_key_ = current;
    }

    void ConfigService::publish_ui()
    {
        const std::uint64_t revision = registry_.revision();
        if (ui_snapshot_published_ && revision == published_ui_revision_)
            return;
        ui_snapshot_.publish(snapshot_builder::build_snapshot(*this, owners_));
        published_ui_revision_ = revision;
        ui_snapshot_published_ = true;
    }
}
