#include "viz/agent_runs_timeline_helpers.h"

#include <algorithm>

// Tabler icon macros live in core/icons_tabler.h. They are short (3-byte
// utf-8) string literals like "\xee\xa9\x9e". We don't depend on that header
// here directly to keep helpers buildable without the icon font tree — but
// the values below are pulled from cpp/functions/core/icons_tabler.h
// (Tabler v3.41). If a glyph moves upstream, update this table.

namespace fn_viz {
namespace timeline {

namespace {

// Mirror of icons_tabler.h entries used by the timeline. Kept inline so the
// helpers TU compiles standalone (tests don't pull the full icon font).
constexpr const char* TI_CLOCK_         = "\xee\xa9\xb0"; // U+EA70
constexpr const char* TI_LOADER_        = "\xee\xb2\xa3"; // U+ECA3  spinning loader
constexpr const char* TI_CIRCLE_CHECK_  = "\xee\xa9\xa7"; // U+EA67
constexpr const char* TI_CHECKS_        = "\xee\xae\xaa"; // U+EBAA
constexpr const char* TI_GIT_MERGE_     = "\xee\xaa\xb5"; // U+EAB5
constexpr const char* TI_BAN_           = "\xee\xa8\xae"; // U+EA2E
constexpr const char* TI_CIRCLE_X_      = "\xee\xa9\xaa"; // U+EA6A
constexpr const char* TI_HOURGLASS_     = "\xee\xbe\x93"; // U+EF93

bool contains(const std::vector<std::string>& v, const std::string& needle) {
    for (const auto& s : v) {
        if (s == needle) return true;
    }
    return false;
}

}  // namespace

bool passes_filter(const AgentRun& r, const TimelineFilter& f) {
    if (!f.apps.empty() && !contains(f.apps, r.app)) return false;
    if (!f.statuses.empty() && !contains(f.statuses, r.status)) return false;
    if (f.since_ts > 0 && r.started_at < f.since_ts) return false;
    return true;
}

std::vector<AgentRun> filter_and_sort(const std::vector<AgentRun>& runs,
                                      const TimelineFilter& f) {
    std::vector<AgentRun> out;
    out.reserve(runs.size());
    for (const auto& r : runs) {
        if (passes_filter(r, f)) out.push_back(r);
    }
    std::sort(out.begin(), out.end(), [](const AgentRun& a, const AgentRun& b) {
        if (a.started_at != b.started_at) return a.started_at > b.started_at;
        return a.id < b.id;
    });
    return out;
}

std::string format_duration(int64_t started_at, int64_t finished_at) {
    if (finished_at == 0) return "running";
    int64_t dur = finished_at - started_at;
    if (dur < 0) return "—";

    char buf[32];
    if (dur < 60) {
        std::snprintf(buf, sizeof(buf), "%llds", (long long)dur);
    } else if (dur < 3600) {
        long long m = dur / 60;
        long long s = dur % 60;
        std::snprintf(buf, sizeof(buf), "%lldm%02llds", m, s);
    } else {
        long long h = dur / 3600;
        long long m = (dur % 3600) / 60;
        std::snprintf(buf, sizeof(buf), "%lldh%02lldm", h, m);
    }
    return std::string(buf);
}

int status_color_token(const std::string& status) {
    if (status == "pending")   return 1;  // info
    if (status == "running")   return 1;  // info
    if (status == "done")      return 2;  // success
    if (status == "validated") return 2;  // success
    if (status == "merged")    return 3;  // primary
    if (status == "aborted")   return 5;  // warning
    if (status == "failed")    return 6;  // danger
    return 0;                              // neutral
}

std::string status_icon_id(const std::string& status) {
    if (status == "pending")   return TI_CLOCK_;
    if (status == "running")   return TI_LOADER_;
    if (status == "done")      return TI_CIRCLE_CHECK_;
    if (status == "validated") return TI_CHECKS_;
    if (status == "merged")    return TI_GIT_MERGE_;
    if (status == "aborted")   return TI_BAN_;
    if (status == "failed")    return TI_CIRCLE_X_;
    return TI_HOURGLASS_;
}

std::string app_chip_hex(const std::string& app) {
    // Hardcoded palette. Keep aligned with the apps' icon.accent in app.md.
    if (app == "kanban_cpp")     return "#a855f7"; // violet
    if (app == "skill_tree")     return "#0ea5e9"; // sky
    if (app == "graph_explorer") return "#16a34a"; // green
    if (app == "shaders_lab")    return "#f97316"; // orange
    if (app == "registry_dashboard") return "#0ea5e9";
    return "#5C5F66"; // neutral gray (matches fn_tokens::border_strong)
}

}  // namespace timeline
}  // namespace fn_viz
