#include "core/process_runner.h"
#include "core/process_state_machine.h"
#include "core/tokens.h"
#include <imgui.h>
#include <cmath>

namespace fn_ui {

// Helpers para puentear el RunnerState (publico) con la SM pura.
// RunnerState y ProcessState comparten orden y valores enteros (0..3).
namespace {
ProcessState to_ps(RunnerState s) { return static_cast<ProcessState>(static_cast<int>(s)); }
RunnerState  to_rs(ProcessState s) { return static_cast<RunnerState>(static_cast<int>(s)); }

// Aplica un evento al estado actual del runner via process_transition.
void apply_event(ProcessRunner& r, std::atomic<int>& state, ProcessEvent ev) {
    (void)r;
    int cur = state.load();
    ProcessState ns = process_transition(to_ps(static_cast<RunnerState>(cur)), ev);
    state.store(static_cast<int>(to_rs(ns)));
}
} // namespace

ProcessRunner::ProcessRunner() = default;

ProcessRunner::~ProcessRunner() {
    if (th_.joinable()) th_.join();
}

bool ProcessRunner::is_busy() const {
    return state_.load() == static_cast<int>(RunnerState::Running);
}

RunnerState ProcessRunner::state() const {
    return static_cast<RunnerState>(state_.load());
}

std::string ProcessRunner::message() const {
    std::lock_guard<std::mutex> lk(mu_);
    return message_;
}

void ProcessRunner::reset() {
    if (is_busy()) return;
    // Reset event: Success/Error -> Idle (no-op si ya en Idle).
    apply_event(*this, state_, ProcessEvent::Reset);
    std::lock_guard<std::mutex> lk(mu_);
    message_.clear();
}

void runner_trigger(ProcessRunner& r,
                    std::function<bool(std::string&)> task) {
    if (r.is_busy()) return;
    if (r.th_.joinable()) r.th_.join();

    // Si venimos de Success/Error, primero Reset -> Idle, luego Spawned -> Running.
    apply_event(r, r.state_, ProcessEvent::Reset);
    apply_event(r, r.state_, ProcessEvent::Spawned);
    {
        std::lock_guard<std::mutex> lk(r.mu_);
        r.message_.clear();
    }

    r.th_ = std::thread([&r, task = std::move(task)]() {
        std::string out;
        bool ok = false;
        try {
            ok = task(out);
        } catch (...) {
            ok = false;
            out = "exception";
        }
        {
            std::lock_guard<std::mutex> lk(r.mu_);
            r.message_ = std::move(out);
        }
        // Running -> Success/Error via SM pura.
        apply_event(r, r.state_, ok ? ProcessEvent::Finished : ProcessEvent::Failed);
    });
}

void runner_status(const ProcessRunner& r, const char* running_label) {
    using namespace fn_tokens;
    const RunnerState s = r.state();
    if (s == RunnerState::Idle) return;

    if (s == RunnerState::Running) {
        // Spinner manual: un punto que rota
        const float t = static_cast<float>(ImGui::GetTime());
        const float radius = 6.0f;
        const ImVec2 cur = ImGui::GetCursorScreenPos();
        const ImVec2 center(cur.x + radius + 2.0f,
                            cur.y + ImGui::GetFrameHeight() * 0.5f);
        ImDrawList* dl = ImGui::GetWindowDrawList();
        const ImU32 col_fg = ImGui::ColorConvertFloat4ToU32(colors::primary);
        const ImU32 col_bg = ImGui::ColorConvertFloat4ToU32(colors::border);
        dl->AddCircle(center, radius, col_bg, 16, 2.0f);
        const float a = t * 3.0f;
        const ImVec2 dot(center.x + std::cos(a) * radius,
                         center.y + std::sin(a) * radius);
        dl->AddCircleFilled(dot, 2.5f, col_fg);
        ImGui::Dummy(ImVec2(radius * 2.0f + 8.0f, ImGui::GetFrameHeight()));
        ImGui::SameLine();
        ImGui::TextUnformatted(running_label ? running_label : "working...");
        return;
    }

    const ImVec4 col = (s == RunnerState::Success)
        ? colors::success : colors::error;
    ImGui::PushStyleColor(ImGuiCol_Text, col);
    const char* glyph = (s == RunnerState::Success)
        ? "\xe2\x9c\x93 " : "\xe2\x9c\x97 "; // ✓ / ✗
    ImGui::TextUnformatted(glyph);
    ImGui::SameLine();
    ImGui::TextWrapped("%s", r.message().c_str());
    ImGui::PopStyleColor();
}

} // namespace fn_ui
