// test_fn_table_viz_smoke.cpp — Linker smoke test for fn_table_viz static lib.
// Issue 0081-I. Verifies that all 11 .cpp files in fn_table_viz resolve symbols
// at link time. Does NOT call data_table::render (requires ImGui context).
//
// Build: cmake --build cpp/build/linux --target test_fn_table_viz_smoke
// Run:   ./cpp/build/linux/tests/test_fn_table_viz_smoke

#include "core/compute_stage.h"
#include "core/compute_pipeline.h"
#include "core/tql_emit.h"
#include "core/tql_apply.h"
#include "core/lua_engine.h"
#include "core/join_tables.h"
#include "core/auto_detect_type.h"
#include "core/compute_column_stats.h"
#include "viz/viz_render.h"
#include "data_table/data_table.h"

#include <cassert>
#include <cstdio>
#include <string>
#include <vector>

using namespace data_table;

// ---------------------------------------------------------------------------
// Minimal input: 3 rows x 2 cols (string + numeric).
// ---------------------------------------------------------------------------
static const char* g_cells[] = {
    "Alice", "10",
    "Bob",   "20",
    "Carol", "30",
};

// ---------------------------------------------------------------------------
// Test 1: compute_stage with trivial Stage (no filters, no agg, no sort).
// ---------------------------------------------------------------------------
static void test_compute_stage_passthrough() {
    Stage s;  // empty stage = passthrough
    std::vector<std::string> hdrs = {"Name", "Value"};
    std::vector<ColumnType>  types = {ColumnType::String, ColumnType::Float};

    StageOutput out = compute_stage(g_cells, 3, 2, hdrs, types, s);
    assert(out.rows == 3 && "compute_stage: rows must be 3");
    assert(out.cols == 2 && "compute_stage: cols must be 2");
    std::printf("PASS: compute_stage passthrough (rows=%d cols=%d)\n",
                out.rows, out.cols);
}

// ---------------------------------------------------------------------------
// Test 2: auto_detect_type on the Value column (all numeric).
// ---------------------------------------------------------------------------
static void test_auto_detect_type() {
    std::vector<std::string> hdrs = {"Name", "Value"};
    std::vector<ColumnType>  types = {ColumnType::String, ColumnType::Float};
    // Detect type for column 1 (Value: "10","20","30" -> Float or Int)
    ColumnType t = auto_detect_type(g_cells, 3, 2, /*col=*/1);
    assert((t == ColumnType::Float || t == ColumnType::Int) &&
           "auto_detect_type: Value col should be Float or Int");
    std::printf("PASS: auto_detect_type numeric (%s)\n",
                t == ColumnType::Float ? "Float" : "Int");
}

// ---------------------------------------------------------------------------
// Test 3: compute_column_stats on the Value column.
// ---------------------------------------------------------------------------
static void test_compute_column_stats() {
    ColStats s = compute_column_stats(g_cells, 3, 2, /*col=*/1);
    assert(s.numeric && "compute_column_stats: Value col should be numeric");
    assert(s.numeric_count == 3 && "compute_column_stats: 3 numeric values");
    assert(s.min < s.max && "compute_column_stats: min < max");
    std::printf("PASS: compute_column_stats (min=%.1f max=%.1f mean=%.1f)\n",
                s.min, s.max, s.mean);
}

// ---------------------------------------------------------------------------
// Test 4: tql_emit -> tql_apply round-trip on a trivial State.
// ---------------------------------------------------------------------------
static void test_tql_roundtrip() {
    State st;
    st.stages.push_back(Stage{});
    st.active_stage = 0;
    st.display = ViewMode::Table;

    std::vector<std::string> hdrs = {"Name", "Value"};
    std::vector<ColumnType>  types = {ColumnType::String, ColumnType::Float};

    std::string lua_text = tql::emit(st, hdrs, types);
    assert(!lua_text.empty() && "tql_emit: must produce non-empty Lua text");

    auto res = tql::apply(lua_text, hdrs);
    assert(res.ok && "tql_apply: round-trip must succeed");
    assert(!res.state.stages.empty() && "tql_apply: state must have stages");
    std::printf("PASS: tql_emit+tql_apply round-trip (ok=%s warnings=%zu)\n",
                res.ok ? "true" : "false", res.warnings.size());
}

// ---------------------------------------------------------------------------
// Test 5: tql_apply extended overload (playground-compat bool signature).
// ---------------------------------------------------------------------------
static void test_tql_apply_extended() {
    State st;
    st.stages.push_back(Stage{});
    std::vector<std::string> hdrs = {"Name", "Value"};
    std::vector<ColumnType>  types = {ColumnType::String, ColumnType::Float};
    std::string lua_text = tql::emit(st, hdrs, types);

    std::string err;
    State out_st;
    bool ok = tql::apply(lua_text, out_st, hdrs, types, g_cells, 3, 2, &err);
    assert(ok && "tql_apply extended: must succeed");
    assert(!out_st.stages.empty() && "tql_apply extended: state must have stages");
    std::printf("PASS: tql_apply extended overload (ok=%s)\n",
                ok ? "true" : "false");
}

// ---------------------------------------------------------------------------
// Test 6: lua_engine get + compile + release (verifies Lua 5.4 links).
// ---------------------------------------------------------------------------
static void test_lua_engine_compile() {
    lua_engine::Engine* e = lua_engine::get();
    assert(e && "lua_engine::get must return non-null");
    std::string err;
    int id = lua_engine::compile(e, "return row['Value'] * 2", &err);
    assert(id >= 0 && "lua_engine::compile must succeed");
    lua_engine::release(e, id);
    lua_engine::shutdown();
    std::printf("PASS: lua_engine compile + release (id=%d)\n", id);
}

// ---------------------------------------------------------------------------
// Test 7: join_tables trivial self-join.
// ---------------------------------------------------------------------------
static void test_join_tables_trivial() {
    std::vector<std::string> hdrs = {"Name", "Value"};
    std::vector<ColumnType>  types = {ColumnType::String, ColumnType::Float};

    TableInput right;
    right.name    = "right";
    right.headers = hdrs;
    right.types   = types;
    right.cells   = g_cells;
    right.rows    = 3;
    right.cols    = 2;

    Join j;
    j.alias    = "r";
    j.source   = "right";
    j.on       = {{"Name", "Name"}};
    j.strategy = JoinStrategy::Left;

    StageOutput out = join_tables(g_cells, 3, 2, hdrs, types, right, j);
    assert(out.rows == 3 && "join_tables: self-join must produce 3 rows");
    std::printf("PASS: join_tables trivial self-join (rows=%d cols=%d)\n",
                out.rows, out.cols);
}

// ---------------------------------------------------------------------------
// Test 8: data_table::render symbol is linkable (Wave 3.5 — issue 0081-I).
// Does NOT call render (requires ImGui context); just takes its address.
// ---------------------------------------------------------------------------
static void test_data_table_render_links() {
    // Taking the address of render verifies the linker resolved the symbol
    // from data_table.cpp inside fn_table_viz. No ImGui context needed.
    // Use the events_out overload (Phase 2) as the canonical full-signature check.
    auto* render_fn = static_cast<void(*)(const char*,
                                          const std::vector<data_table::TableInput>&,
                                          data_table::State&,
                                          std::vector<data_table::TableEvent>*,
                                          bool)>(&data_table::render);
    (void)render_fn;
    std::printf("PASS: data_table::render symbol links (address=%p)\n",
                reinterpret_cast<void*>(render_fn));
}

// ---------------------------------------------------------------------------
// main
// ---------------------------------------------------------------------------
int main() {
    std::printf("=== test_fn_table_viz_smoke ===\n");
    test_compute_stage_passthrough();
    test_auto_detect_type();
    test_compute_column_stats();
    test_tql_roundtrip();
    test_tql_apply_extended();
    test_lua_engine_compile();
    test_join_tables_trivial();
    test_data_table_render_links();
    std::printf("=== ALL TESTS PASSED ===\n");
    return 0;
}
