#include "dod_evidence_panel.h"
#include "core/icons_tabler.h"
#include "core/tokens.h"

#include <imgui.h>

#include <cstdio>
#include <fstream>
#include <sstream>
#include <string>

#ifdef _WIN32
#  ifndef WIN32_LEAN_AND_MEAN
#    define WIN32_LEAN_AND_MEAN
#  endif
#  include <windows.h>
#  include <shellapi.h>
#endif

namespace fn_viz {
namespace {

// Mapea icon_id (helpers) -> macro TI_*.
const char* ti_for(const std::string& icon_id) {
    if (icon_id == "circle-dot")   return TI_CIRCLE_DOT;
    if (icon_id == "circle-check") return TI_CIRCLE_CHECK;
    if (icon_id == "circle-x")     return TI_CIRCLE_X;
    return TI_CIRCLE_DASHED;
}

// Color por token (0=neutral, 1=info, 2=success, 3=danger).
ImVec4 color_for_token(int tok) {
    using namespace fn_tokens;
    switch (tok) {
        case 1: return colors::info;
        case 2: return colors::success;
        case 3: return colors::error;
        default: return colors::text_muted;
    }
}

// Abre una URL en el browser por defecto. Stub en plataformas no soportadas.
void open_url(const std::string& url) {
#ifdef _WIN32
    ShellExecuteA(nullptr, "open", url.c_str(), nullptr, nullptr, SW_SHOWNORMAL);
#else
    // xdg-open con argumento quoteado minimo (sin caracteres especiales no
    // se gana mucho con quoting completo). Si el caller pasa una URL
    // controlada por usuario, esto deberia validarse antes.
    std::string cmd = "xdg-open '" + url + "' >/dev/null 2>&1 &";
    int rc = std::system(cmd.c_str());
    (void)rc;
#endif
}

// Lee primeras N lineas de un archivo log. Devuelve "" si no se puede abrir.
std::string read_first_lines(const std::string& path, int max_lines) {
    std::ifstream f(path);
    if (!f.is_open()) return "";
    std::ostringstream out;
    std::string line;
    int n = 0;
    while (n < max_lines && std::getline(f, line)) {
        out << line << "\n";
        ++n;
    }
    return out.str();
}

// Carga entero el archivo. Truncado a 64KiB para popup.
std::string read_full_capped(const std::string& path, size_t cap = 64 * 1024) {
    std::ifstream f(path, std::ios::binary);
    if (!f.is_open()) return "";
    std::string s;
    f.seekg(0, std::ios::end);
    std::streamsize sz = f.tellg();
    if (sz < 0) return "";
    size_t n = static_cast<size_t>(sz);
    if (n > cap) n = cap;
    s.resize(n);
    f.seekg(0, std::ios::beg);
    f.read(&s[0], static_cast<std::streamsize>(n));
    return s;
}

void render_preview_screenshot(const DodEvidence& ev) {
    // Sin GL context cacheado ni stb_image load aqui: stub textual. La carga
    // real requiere mantener un map<string, GLuint> que el panel no
    // posee. Las apps consumidoras pueden override esto sustituyendo la
    // funcion (issue futura: callback render_preview).
    ImGui::TextDisabled("[screenshot] %s", ev.payload_path.c_str());
}

void render_preview_log(const DodEvidence& ev, int row_idx) {
    if (ev.payload_path.empty()) {
        ImGui::TextDisabled("(no log path)");
        return;
    }
    std::string head = read_first_lines(ev.payload_path, 5);
    if (head.empty()) {
        ImGui::TextDisabled("(log not readable)");
    } else {
        ImGui::TextWrapped("%s", head.c_str());
    }
    char btn_id[64];
    std::snprintf(btn_id, sizeof(btn_id), "Show full##log_%d", row_idx);
    if (ImGui::SmallButton(btn_id)) {
        ImGui::OpenPopup(btn_id);
    }
    if (ImGui::BeginPopup(btn_id)) {
        std::string full = read_full_capped(ev.payload_path);
        ImGui::InputTextMultiline("##log_full", full.data(), full.size() + 1,
                                  ImVec2(600, 400), ImGuiInputTextFlags_ReadOnly);
        ImGui::EndPopup();
    }
}

void render_preview_url(const DodEvidence& ev) {
    ImGui::TextUnformatted(TI_EXTERNAL_LINK);
    ImGui::SameLine();
    if (ImGui::SmallButton(ev.payload_url.c_str())) {
        open_url(ev.payload_url);
    }
}

void render_preview_cmd(const DodEvidence& ev, const DodItem& item) {
    using namespace fn_tokens;
    const bool match = (ev.payload_text == item.expected);
    ImVec4 ok = colors::success;
    ImVec4 bad = colors::error;
    if (ImGui::BeginTable("##cmd_diff", 2, ImGuiTableFlags_SizingStretchSame)) {
        ImGui::TableNextRow();
        ImGui::TableSetColumnIndex(0);
        ImGui::TextDisabled("expected");
        ImGui::TextWrapped("%s", item.expected.c_str());
        ImGui::TableSetColumnIndex(1);
        ImGui::TextDisabled("actual");
        ImGui::PushStyleColor(ImGuiCol_Text, match ? ok : bad);
        ImGui::TextWrapped("%s", ev.payload_text.c_str());
        ImGui::PopStyleColor();
        ImGui::EndTable();
    }
}

}  // namespace

void render_dod_evidence_panel(DodPanelState& state) {
    using namespace fn_tokens;
    namespace P = ::fn_viz::dod_panel;

    P::StatusCounts c = P::count_status(state);

    // Header.
    ImGui::PushStyleColor(ImGuiCol_Text, colors::text_muted);
    ImGui::Text("run_id:");
    ImGui::PopStyleColor();
    ImGui::SameLine();
    ImGui::TextUnformatted(state.run_id.empty() ? "(none)" : state.run_id.c_str());

    ImGui::SameLine();
    ImGui::TextDisabled(" | ");
    ImGui::SameLine();
    ImGui::Text("total %d  ", c.total);
    ImGui::SameLine();
    ImGui::PushStyleColor(ImGuiCol_Text, colors::info);
    ImGui::Text("done %d  ", c.done);
    ImGui::PopStyleColor();
    ImGui::SameLine();
    ImGui::PushStyleColor(ImGuiCol_Text, colors::success);
    ImGui::Text("validated %d  ", c.validated);
    ImGui::PopStyleColor();
    ImGui::SameLine();
    ImGui::PushStyleColor(ImGuiCol_Text, colors::error);
    ImGui::Text("failed %d", c.failed);
    ImGui::PopStyleColor();

    if (c.missing_required > 0) {
        ImGui::SameLine();
        ImGui::TextDisabled(" | ");
        ImGui::SameLine();
        ImGui::PushStyleColor(ImGuiCol_Text, colors::error);
        ImGui::Text("required missing: %d", c.missing_required);
        ImGui::PopStyleColor();
    }

    ImGui::Separator();

    // Tabla principal.
    const ImGuiTableFlags flags =
        ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg |
        ImGuiTableFlags_Resizable | ImGuiTableFlags_SizingStretchProp;
    if (!ImGui::BeginTable("##dod_table", 6, flags)) return;

    ImGui::TableSetupColumn("",         ImGuiTableColumnFlags_WidthFixed, 24.0f);
    ImGui::TableSetupColumn("id",       ImGuiTableColumnFlags_WidthFixed, 120.0f);
    ImGui::TableSetupColumn("kind",     ImGuiTableColumnFlags_WidthFixed, 80.0f);
    ImGui::TableSetupColumn("expected", ImGuiTableColumnFlags_WidthStretch);
    ImGui::TableSetupColumn("evidence", ImGuiTableColumnFlags_WidthStretch);
    ImGui::TableSetupColumn("actions",  ImGuiTableColumnFlags_WidthFixed, 130.0f);
    ImGui::TableHeadersRow();

    int row_idx = 0;
    for (const auto& item : state.items) {
        ImGui::TableNextRow();
        ImGui::PushID(row_idx);

        // Col 0: status icon.
        ImGui::TableSetColumnIndex(0);
        const std::string icon_id = P::status_icon_id(item.status);
        const int tok = P::status_color_token(item.status);
        ImGui::PushStyleColor(ImGuiCol_Text, color_for_token(tok));
        ImGui::TextUnformatted(ti_for(icon_id));
        ImGui::PopStyleColor();

        // Col 1: id (+ required badge si missing).
        ImGui::TableSetColumnIndex(1);
        ImGui::TextUnformatted(item.id.c_str());
        const DodEvidence* ev = P::find_evidence(state, item.id);
        const bool resolved = (item.status == "done" || item.status == "validated");
        if (item.required && ev == nullptr && !resolved) {
            ImGui::PushStyleColor(ImGuiCol_Text, colors::error);
            ImGui::TextUnformatted("required, missing");
            ImGui::PopStyleColor();
        }

        // Col 2: kind.
        ImGui::TableSetColumnIndex(2);
        ImGui::TextUnformatted(item.kind.c_str());

        // Col 3: expected.
        ImGui::TableSetColumnIndex(3);
        ImGui::TextWrapped("%s", item.expected.c_str());

        // Col 4: evidence preview.
        ImGui::TableSetColumnIndex(4);
        if (ev == nullptr) {
            ImGui::TextDisabled("(no evidence)");
        } else if (item.kind == "screenshot") {
            render_preview_screenshot(*ev);
        } else if (item.kind == "log") {
            render_preview_log(*ev, row_idx);
        } else if (item.kind == "url") {
            render_preview_url(*ev);
        } else if (item.kind == "cmd") {
            render_preview_cmd(*ev, item);
        } else {
            ImGui::TextDisabled("(unknown kind: %s)", item.kind.c_str());
        }

        // Col 5: actions.
        ImGui::TableSetColumnIndex(5);
        if (ev != nullptr) {
            ImGui::PushStyleColor(ImGuiCol_Text, colors::success);
            const bool clicked_v = ImGui::SmallButton(TI_CHECK " Validate");
            ImGui::PopStyleColor();
            if (clicked_v && state.on_validate) state.on_validate(item.id);

            ImGui::SameLine();
            ImGui::PushStyleColor(ImGuiCol_Text, colors::error);
            const bool clicked_r = ImGui::SmallButton(TI_X " Reject");
            ImGui::PopStyleColor();
            if (clicked_r && state.on_reject) state.on_reject(item.id);
        } else {
            ImGui::TextDisabled("—");
        }

        ImGui::PopID();
        ++row_idx;
    }

    ImGui::EndTable();
}

}  // namespace fn_viz
