// data_table_viz_panels — paneles de visualizacion lateral de la tabla TQL.
// Sub-funcion extraida de modules/data_table/data_table.cpp (issue 0107c).
//
// Funciones implementadas:
//   - draw_table_toggle      (ex lineas 745-767)
//   - draw_extra_panel       (ex lineas 771-856)
//   - draw_viz_config_popup  (ex lineas 858-1021)
//   - draw_viz_selector      (ex lineas 1034-1110)
//   - maybe_recompute_stats  (ex lineas 1118-1145)

#include "viz/data_table_viz_panels.h"
#include "data_table/data_table_internal.h"
#include "viz/data_table_grid.h"       // draw_cell_custom
#include "core/data_table_types.h"
#include "core/compute_column_stats.h"
#include "core/tql_emit.h"
#include "viz/viz_render.h"
#include "imgui.h"

#include <algorithm>
#include <cstdio>
#include <string>
#include <vector>

namespace data_table {

// ---------------------------------------------------------------------------
// draw_table_toggle
// ---------------------------------------------------------------------------
void draw_table_toggle(ViewMode& display, ViewMode& last_non_table,
                       const char* id_suffix,
                       State* st_opt)
{
    bool is_table = (display == ViewMode::Table);
    char b[64];
    std::snprintf(b, sizeof(b), "%s##tbl_%s",
                  is_table ? "Show chart" : "Show table", id_suffix);
    ImGui::PushStyleColor(ImGuiCol_Button,        IM_COL32(80, 140, 200, 240));
    ImGui::PushStyleColor(ImGuiCol_ButtonHovered, IM_COL32(100, 160, 220, 240));
    if (ImGui::SmallButton(b)) {
        if (is_table) {
            ViewMode tgt = (last_non_table == ViewMode::Table)
                            ? ViewMode::Bar : last_non_table;
            display = tgt;
            if (st_opt && view_mode_needs_aggregation(tgt)) {
                auto_promote_aggregated(*st_opt);
            }
        } else {
            last_non_table = display;
            display        = ViewMode::Table;
        }
    }
    ImGui::PopStyleColor(2);
}

// ---------------------------------------------------------------------------
// draw_extra_panel
// ---------------------------------------------------------------------------
bool draw_extra_panel(State& st, VizPanel& p, int idx,
                      const StageOutput& so,
                      const std::vector<ColumnSpec>* col_specs)
{
    bool close_req = false;
    char child_id[64]; std::snprintf(child_id, sizeof(child_id), "##extra_viz_%d", idx);
    ImGui::BeginChild(child_id, ImVec2(0, 320), true);

    // Toolbar
    int n_modes = 0;
    const ViewMode* modes = all_view_modes(&n_modes);
    ImGui::TextDisabled("View:");
    ImGui::SameLine();
    ImGui::SetNextItemWidth(180);
    char combo_id[64]; std::snprintf(combo_id, sizeof(combo_id), "##ev_mode_%d", idx);
    if (ImGui::BeginCombo(combo_id, view_mode_label(p.display))) {
        for (int i = 0; i < n_modes; ++i) {
            bool sel = (modes[i] == p.display);
            if (ImGui::Selectable(view_mode_label(modes[i]), sel)) {
                p.display = modes[i];
                p.config.fit_request = true;
            }
            if (sel) ImGui::SetItemDefaultFocus();
        }
        ImGui::EndCombo();
    }
    ImGui::SameLine();
    char fit_id[32]; std::snprintf(fit_id, sizeof(fit_id), "Fit##ev_fit_%d", idx);
    if (ImGui::SmallButton(fit_id)) p.config.fit_request = true;
    ImGui::SameLine();
    char lock_id[32]; std::snprintf(lock_id, sizeof(lock_id), "%s##ev_lock_%d",
                                     p.config.locked ? "Locked" : "Lock", idx);
    if (p.config.locked) {
        ImGui::PushStyleColor(ImGuiCol_Button,        IM_COL32(180, 60, 60, 230));
        ImGui::PushStyleColor(ImGuiCol_ButtonHovered, IM_COL32(200, 80, 80, 240));
        ImGui::PushStyleColor(ImGuiCol_ButtonActive,  IM_COL32(150, 40, 40, 240));
    }
    if (ImGui::SmallButton(lock_id)) p.config.locked = !p.config.locked;
    if (p.config.locked) ImGui::PopStyleColor(3);
    ImGui::SameLine();
    char close_id[32]; std::snprintf(close_id, sizeof(close_id), "X##ev_close_%d", idx);
    ImGui::PushStyleColor(ImGuiCol_Button,        IM_COL32(120, 50, 50, 220));
    ImGui::PushStyleColor(ImGuiCol_ButtonHovered, IM_COL32(160, 70, 70, 240));
    if (ImGui::SmallButton(close_id)) close_req = true;
    ImGui::PopStyleColor(2);

    // Toggle Table <-> View per-panel
    char ts[32]; std::snprintf(ts, sizeof(ts), "ep%d", idx);
    draw_table_toggle(p.display, p.last_non_table, ts);

    // Render: si Table -> mini table; else chart.
    if (p.display == ViewMode::Table) {
        ImGuiTableFlags flags = ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg |
                                 ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY |
                                 ImGuiTableFlags_ScrollX;
        char tid[64]; std::snprintf(tid, sizeof(tid), "##ep_table_%d", idx);
        if (so.cols > 0 && ImGui::BeginTable(tid, so.cols, flags, ImVec2(0, 0))) {
            for (int c = 0; c < so.cols; ++c)
                ImGui::TableSetupColumn(so.headers[c].c_str());
            ImGui::TableHeadersRow();
            for (int r = 0; r < so.rows; ++r) {
                ImGui::TableNextRow();
                for (int c = 0; c < so.cols; ++c) {
                    ImGui::TableSetColumnIndex(c);
                    const char* s = so.cells[(size_t)r * so.cols + c];
                    // Issue 0081-N: declarative renderer for extra panel mini-table.
                    // events_out not propagated to mini-table (secondary render).
                    bool custom_ep = false;
                    if (col_specs && c < (int)col_specs->size()) {
                        const ColumnSpec& cs = (*col_specs)[(size_t)c];
                        if (cs.renderer != CellRenderer::Text) {
                            draw_cell_custom(cs, s, r, c, nullptr);
                            custom_ep = true;
                        }
                    }
                    if (!custom_ep) ImGui::TextUnformatted(s ? s : "");
                }
            }
            ImGui::EndTable();
        }
    } else {
        viz::render(so, p.display, p.config, ImVec2(-1, -1));
    }

    ImGui::EndChild();
    (void)st;
    return close_req;
}

// ---------------------------------------------------------------------------
// draw_viz_config_popup
// ---------------------------------------------------------------------------
void draw_viz_config_popup(State& st) {
    if (!ImGui::BeginPopup("##viz_cfg_popup")) return;
    ImGui::Text("Configure: %s", view_mode_label(st.display));
    ImGui::Separator();

    auto cols = collect_active_col_info(st);
    std::vector<const char*> all_names;
    std::vector<const char*> num_names;
    std::vector<const char*> cat_names;
    for (auto& c : cols) {
        all_names.push_back(c.name.c_str());
        if (c.type == ColumnType::Int || c.type == ColumnType::Float)
            num_names.push_back(c.name.c_str());
        else
            cat_names.push_back(c.name.c_str());
    }

    auto& vc = st.viz_config;
    ViewMode m = st.display;

    auto combo_for_col = [&](const char* label, std::string& target,
                              const std::vector<const char*>& options) {
        const char* preview = target.empty() ? "(auto)" : target.c_str();
        ImGui::SetNextItemWidth(220);
        if (ImGui::BeginCombo(label, preview)) {
            if (ImGui::Selectable("(auto)", target.empty())) target.clear();
            for (auto& o : options) {
                bool sel = (target == o);
                if (ImGui::Selectable(o, sel)) target = o;
            }
            ImGui::EndCombo();
        }
    };

    // X col: scatter, line, area, stairs, hist2d, bubble
    bool needs_x = (m == ViewMode::Scatter || m == ViewMode::Line ||
                    m == ViewMode::Area    || m == ViewMode::Stairs ||
                    m == ViewMode::Histogram2D || m == ViewMode::Bubble);
    if (needs_x) combo_for_col("X column", vc.x_col, num_names);

    // Y cols: most modes
    bool needs_y = (m != ViewMode::Pie && m != ViewMode::Donut && m != ViewMode::Funnel &&
                    m != ViewMode::Candlestick);
    if (needs_y) {
        ImGui::Text("Y columns:");
        ImGui::SameLine();
        ImGui::TextDisabled("(%d selected; empty = auto)", (int)vc.y_cols.size());
        ImGui::Indent();
        for (auto& nn : num_names) {
            std::string ns = nn;
            bool checked = std::find(vc.y_cols.begin(), vc.y_cols.end(), ns) != vc.y_cols.end();
            if (ImGui::Checkbox(nn, &checked)) {
                if (checked) vc.y_cols.push_back(ns);
                else {
                    auto it = std::find(vc.y_cols.begin(), vc.y_cols.end(), ns);
                    if (it != vc.y_cols.end()) vc.y_cols.erase(it);
                }
            }
        }
        ImGui::Unindent();
        if (ImGui::SmallButton("Clear Y##clr_y")) vc.y_cols.clear();
    }

    // Cat col: bar/pie/funnel/box/waterfall
    bool needs_cat = (m == ViewMode::Bar || m == ViewMode::Column ||
                      m == ViewMode::GroupedBar || m == ViewMode::StackedBar ||
                      m == ViewMode::Pie || m == ViewMode::Donut ||
                      m == ViewMode::Funnel || m == ViewMode::BoxPlot ||
                      m == ViewMode::Waterfall);
    if (needs_cat) {
        // Si el active stage YA esta agrupado (breakouts != empty), la categoria
        // del chart la dicta el breakout. Mostrar todas las cols del INPUT del
        // stage (= cols pre-agrupacion). Selecionar otra = reemplaza breakouts[0]
        // (re-agrupa).
        int as = st.active_stage;
        bool grouped = (as >= 0 && as < (int)st.stages.size() &&
                        !st.stages[as].breakouts.empty());
        const auto& U = ui();
        if (grouped) {
            std::vector<const char*> input_cat_names;
            for (size_t i = 0; i < U.input_headers_active.size() &&
                                i < U.input_types_active.size(); ++i) {
                ColumnType t = U.input_types_active[i];
                if (t == ColumnType::String || t == ColumnType::Date ||
                    t == ColumnType::Bool   || t == ColumnType::Json) {
                    input_cat_names.push_back(U.input_headers_active[i].c_str());
                }
            }
            std::string cur_break = st.stages[as].breakouts[0];
            const char* preview = cur_break.empty() ? "(none)" : cur_break.c_str();
            ImGui::SetNextItemWidth(220);
            if (ImGui::BeginCombo("Category (breakout)", preview)) {
                for (auto& o : input_cat_names) {
                    bool sel = (cur_break == o);
                    if (ImGui::Selectable(o, sel)) {
                        st.stages[as].breakouts[0] = o;
                    }
                }
                ImGui::EndCombo();
            }
        } else {
            combo_for_col("Category", vc.cat_col, cat_names);
        }
    }

    // Size col: bubble
    if (m == ViewMode::Bubble) combo_for_col("Size column", vc.size_col, num_names);

    // Color
    ImGui::Separator();
    float col_f[4] = {
        ((vc.primary_color)         & 0xFF) / 255.0f,
        ((vc.primary_color >>  8)   & 0xFF) / 255.0f,
        ((vc.primary_color >> 16)   & 0xFF) / 255.0f,
        ((vc.primary_color >> 24)   & 0xFF) / 255.0f,
    };
    if (vc.primary_color == 0) { col_f[0]=col_f[1]=col_f[2]=1.0f; col_f[3]=1.0f; }
    if (ImGui::ColorEdit4("Primary color", col_f, ImGuiColorEditFlags_AlphaBar)) {
        unsigned int r2 = (unsigned int)(col_f[0] * 255);
        unsigned int g2 = (unsigned int)(col_f[1] * 255);
        unsigned int b2 = (unsigned int)(col_f[2] * 255);
        unsigned int a2 = (unsigned int)(col_f[3] * 255);
        vc.primary_color = (a2 << 24) | (b2 << 16) | (g2 << 8) | r2;
    }
    ImGui::SameLine();
    if (ImGui::SmallButton("Auto##color")) vc.primary_color = 0;

    // Hist bins
    if (m == ViewMode::Histogram || m == ViewMode::Histogram2D) {
        ImGui::SetNextItemWidth(120);
        int bins = vc.hist_bins;
        if (ImGui::InputInt("Bins (0=auto)", &bins)) {
            if (bins < 0) bins = 0;
            vc.hist_bins = bins;
        }
    }

    // Pie radius
    if (m == ViewMode::Pie || m == ViewMode::Donut) {
        ImGui::SetNextItemWidth(120);
        float rad = vc.pie_radius;
        if (ImGui::SliderFloat("Radius (0=auto)", &rad, 0.0f, 0.5f, "%.2f")) {
            vc.pie_radius = rad;
        }
    }

    // Toggles
    ImGui::Separator();
    ImGui::Checkbox("Show legend", &vc.show_legend);
    if (m == ViewMode::Line || m == ViewMode::Area || m == ViewMode::Stairs) {
        ImGui::SameLine();
        ImGui::Checkbox("Show markers", &vc.show_markers);
    }

    ImGui::Separator();
    if (ImGui::SmallButton("Reset config")) {
        vc = ViewConfig{};
    }
    ImGui::SameLine();
    if (ImGui::SmallButton("Close")) ImGui::CloseCurrentPopup();

    ImGui::EndPopup();
}

// ---------------------------------------------------------------------------
// draw_viz_selector
// ---------------------------------------------------------------------------
void draw_viz_selector(State& st) {
    int n_modes = 0;
    const ViewMode* modes = all_view_modes(&n_modes);

    // Right-align: reserve "View: [combo] [Fit] [Lock] [Config] [Ask AI] [+ Viz]"
    const float combo_w  = 200.0f;
    const float total_w  = combo_w + 50.0f + 280.0f;
    float right_edge = ImGui::GetWindowContentRegionMax().x;
    float target_x   = right_edge - total_w;
    float min_x      = ImGui::GetCursorPosX() + 20.0f; // do not overlap breadcrumb
    if (target_x < min_x) target_x = min_x;
    ImGui::SameLine();
    ImGui::SetCursorPosX(target_x);

    ImGui::TextDisabled("View:");
    ImGui::SameLine();
    ImGui::SetNextItemWidth(combo_w);
    if (ImGui::BeginCombo("##viz_mode", view_mode_label(st.display))) {
        for (int i = 0; i < n_modes; ++i) {
            bool sel = (modes[i] == st.display);
            if (ImGui::Selectable(view_mode_label(modes[i]), sel)) {
                ViewMode nm = modes[i];
                if (nm != st.display) {
                    st.display = nm;
                    if (view_mode_needs_aggregation(nm)) {
                        auto_promote_aggregated(st);
                    }
                }
            }
            if (sel) ImGui::SetItemDefaultFocus();
        }
        ImGui::EndCombo();
    }
    ImGui::SameLine();
    if (ImGui::SmallButton("Fit##viz_fit")) {
        st.viz_config.fit_request = true;
    }
    ImGui::SameLine();
    bool locked = st.viz_config.locked;
    if (locked) {
        ImGui::PushStyleColor(ImGuiCol_Button,        IM_COL32(180, 60, 60, 230));
        ImGui::PushStyleColor(ImGuiCol_ButtonHovered, IM_COL32(200, 80, 80, 240));
        ImGui::PushStyleColor(ImGuiCol_ButtonActive,  IM_COL32(150, 40, 40, 240));
    }
    if (ImGui::SmallButton(locked ? "Locked##viz_lock" : "Lock##viz_lock")) {
        st.viz_config.locked = !st.viz_config.locked;
    }
    if (locked) ImGui::PopStyleColor(3);
    ImGui::SameLine();
    if (ImGui::SmallButton("Config##viz_cfg")) {
        ImGui::OpenPopup("##viz_cfg_popup");
    }
    ImGui::SameLine();
    if (ImGui::SmallButton("Ask AI##ask_open")) {
        auto& U2 = ui();
        U2.ask_ai.open = true;
        U2.ask_ai.busy = false;
        U2.ask_ai.error.clear();
        U2.ask_ai.status.clear();
        U2.ask_ai.response_code.clear();
        U2.ask_ai.response_raw.clear();
        U2.ask_ai.current_tql = tql::emit(st,
            std::vector<std::string>(),
            std::vector<ColumnType>());
    }
    ImGui::SameLine();
    if (ImGui::SmallButton("+ Viz##viz_add")) {
        VizPanel p;
        p.display = ViewMode::Bar;
        if (view_mode_needs_aggregation(p.display)) {
            auto_promote_aggregated(st);
        }
        st.extra_panels.push_back(p);
    }
    draw_viz_config_popup(st);
    ImGui::NewLine();
}

// ---------------------------------------------------------------------------
// maybe_recompute_stats
// ---------------------------------------------------------------------------
void maybe_recompute_stats(State& st,
                           const char* const* cells,
                           int row_count, int orig_cols, int eff_cols,
                           const std::vector<Filter>& active_filters,
                           const std::vector<int>& visible_rows,
                           const std::vector<int>& src_for_eff)
{
    if (!st.stats_mode) return;
    size_t fh = filters_hash(active_filters);
    bool ds_changed = (cells != st.stats_last_cells || row_count != st.stats_last_rows ||
                       eff_cols != st.stats_last_eff_cols ||
                       (int)st.stats_cache.size() != eff_cols);
    bool fl_changed = (fh != st.stats_last_filter_h ||
                       (int)visible_rows.size() != st.stats_last_visible);
    if (!ds_changed && !fl_changed) return;
    st.stats_cache.resize(eff_cols);
    const int* idx = visible_rows.empty() ? nullptr : visible_rows.data();
    int        n   = (int)visible_rows.size();
    for (int c = 0; c < eff_cols; ++c) {
        int src = src_for_eff[c];
        st.stats_cache[c] = compute_column_stats(cells, row_count, orig_cols, src,
                                                  100000, idx, n);
    }
    st.stats_last_cells    = cells;
    st.stats_last_rows     = row_count;
    st.stats_last_eff_cols = eff_cols;
    st.stats_last_filter_h = fh;
    st.stats_last_visible  = (int)visible_rows.size();
}

} // namespace data_table
