#include "application/lifecycle/RuntimeState.h"

#include "foundation/diagnostics/Logger.h"
#include "application/lifecycle/CallbackBarrier.h"

#include <string>

namespace ta::application::runtime
{
    namespace
    {
        RuntimeState g_state;
    }

    RuntimePhase RuntimeState::phase() const noexcept
    {
        return phase_.load(std::memory_order_acquire);
    }


    bool RuntimeState::is_terminal() const noexcept
    {
        const auto current = phase();
        return current == RuntimePhase::Stopped || current == RuntimePhase::Failed;
    }

    bool RuntimeState::should_accept_callbacks() const noexcept
    {
        return phase() == RuntimePhase::Running && callback_barrier().accepts_feature_work();
    }

    void RuntimeState::mark_application_update_thread() noexcept
    {
        std::lock_guard lock(application_thread_mutex_);
        if (application_thread_id_ == std::thread::id{})
            application_thread_id_ = std::this_thread::get_id();
    }

    void RuntimeState::clear_application_update_thread() noexcept
    {
        std::lock_guard lock(application_thread_mutex_);
        application_thread_id_ = {};
    }

    bool RuntimeState::is_application_update_thread() const noexcept
    {
        std::lock_guard lock(application_thread_mutex_);
        return application_thread_id_ != std::thread::id{} &&
               application_thread_id_ == std::this_thread::get_id();
    }

    void RuntimeState::set_phase(const RuntimePhase phase) noexcept
    {
        const auto previous = phase_.exchange(phase, std::memory_order_acq_rel);
        if (previous == phase)
            return;

        try
        {
            ta::foundation::logger::info(
                "RUNTIME",
                std::string("phase changed | from=") + phase_name(previous) +
                " to=" + phase_name(phase));
        }
        catch (...)
        {
            // Logging must never break runtime state transitions.
        }
    }

    RuntimeState& state() noexcept
    {
        return g_state;
    }

    bool should_accept_callbacks() noexcept
    {
        return state().should_accept_callbacks();
    }

    void mark_application_update_thread() noexcept
    {
        state().mark_application_update_thread();
    }

    void clear_application_update_thread() noexcept
    {
        state().clear_application_update_thread();
    }

    bool is_application_update_thread() noexcept
    {
        return state().is_application_update_thread();
    }

    const char* phase_name(const RuntimePhase phase) noexcept
    {
        switch (phase)
        {
        case RuntimePhase::None:
            return "None";
        case RuntimePhase::Starting:
            return "Starting";
        case RuntimePhase::Running:
            return "Running";
        case RuntimePhase::Stopping:
            return "Stopping";
        case RuntimePhase::Stopped:
            return "Stopped";
        case RuntimePhase::Failed:
            return "Failed";
        default:
            return "Unknown";
        }
    }
}
