#include "core/sql_workbench.h"

#include "core/sql_parse.h"
#include "core/text_editor.h"
#include "core/tokens.h"
#include "core/button.h"
#include "viz/table_view.h"

#include "imgui.h"

#include <sqlite3.h>

#include <algorithm>
#include <cctype>
#include <chrono>
#include <cstdio>
#include <cstring>
#include <string>
#include <vector>

namespace fn {

// ─────────────────────────────────────────────────────────────────────────────
// Helpers
// ─────────────────────────────────────────────────────────────────────────────

namespace {

// Clasifica el primer statement con sql_parse pure. Si el input no contiene
// statements (todo whitespace/comentarios), devuelve Unknown.
fn_ui::SqlStmtKind first_stmt_kind(const std::string& sql) {
    auto stmts = fn_ui::sql_parse(sql);
    if (stmts.empty()) return fn_ui::SqlStmtKind::Unknown;
    return stmts.front().kind;
}

bool is_readonly_stmt(const std::string& sql) {
    using K = fn_ui::SqlStmtKind;
    K k = first_stmt_kind(sql);
    // sql_parse clasifica WITH como Select; Pragma y Explain son readonly.
    return k == K::Select || k == K::Pragma || k == K::Explain;
}

TextEditorState* editor_of(SqlWorkbenchState& state) {
    return reinterpret_cast<TextEditorState*>(state._editor);
}

void ensure_editor(SqlWorkbenchState& state) {
    if (!state._editor) {
        state._editor = text_editor_create(CodeLang::SQL);
        text_editor_set_text(editor_of(state), state.query.c_str());
        state._editor_synced = true;
    }
}

} // namespace

// ─────────────────────────────────────────────────────────────────────────────
// Backend
// ─────────────────────────────────────────────────────────────────────────────

bool sql_workbench_run_query(sqlite3* db, const char* sql, SqlWorkbenchState& state) {
    state.columns.clear();
    state.rows.clear();
    state.last_error.clear();
    state.truncated = false;
    state.last_ms   = 0.0;

    if (!db) {
        state.last_error = "no database handle";
        return false;
    }
    if (!sql || !*sql) {
        state.last_error = "empty query";
        return false;
    }

    if (state.readonly && !is_readonly_stmt(sql)) {
        state.last_error = "readonly mode: only SELECT/PRAGMA/EXPLAIN/WITH allowed";
        return false;
    }

    auto t0 = std::chrono::steady_clock::now();

    sqlite3_stmt* stmt = nullptr;
    int rc = sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr);
    if (rc != SQLITE_OK) {
        state.last_error = sqlite3_errmsg(db);
        if (stmt) sqlite3_finalize(stmt);
        return false;
    }

    int ncols = sqlite3_column_count(stmt);
    state.columns.reserve(static_cast<size_t>(ncols));
    for (int c = 0; c < ncols; ++c) {
        const char* name = sqlite3_column_name(stmt, c);
        state.columns.emplace_back(name ? name : "");
    }

    int row_cap = state.truncated_at > 0 ? state.truncated_at : 10000;

    while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
        if (static_cast<int>(state.rows.size()) >= row_cap) {
            state.truncated = true;
            break;
        }
        std::vector<std::string> row;
        row.reserve(static_cast<size_t>(ncols));
        for (int c = 0; c < ncols; ++c) {
            int t = sqlite3_column_type(stmt, c);
            if (t == SQLITE_NULL) {
                row.emplace_back("NULL");
            } else {
                const unsigned char* txt = sqlite3_column_text(stmt, c);
                row.emplace_back(txt ? reinterpret_cast<const char*>(txt) : "");
            }
        }
        state.rows.emplace_back(std::move(row));
    }

    bool ok = (rc == SQLITE_ROW || rc == SQLITE_DONE);
    if (!ok) {
        state.last_error = sqlite3_errmsg(db);
    }

    sqlite3_finalize(stmt);

    auto t1 = std::chrono::steady_clock::now();
    state.last_ms = std::chrono::duration<double, std::milli>(t1 - t0).count();
    return ok;
}

void sql_workbench_load_schema(sqlite3* db, SqlWorkbenchState& state) {
    state._schema_tables.clear();
    state._schema_views.clear();
    state._schema_loaded = true;

    if (!db) return;

    const char* sql =
        "SELECT name, type FROM sqlite_master "
        "WHERE type IN ('table','view') "
        "  AND name NOT LIKE 'sqlite_%' "
        "ORDER BY type, name;";

    sqlite3_stmt* stmt = nullptr;
    if (sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr) != SQLITE_OK) return;

    while (sqlite3_step(stmt) == SQLITE_ROW) {
        const unsigned char* name = sqlite3_column_text(stmt, 0);
        const unsigned char* type = sqlite3_column_text(stmt, 1);
        if (!name || !type) continue;
        std::string n = reinterpret_cast<const char*>(name);
        std::string t = reinterpret_cast<const char*>(type);
        if (t == "table") state._schema_tables.push_back(n);
        else if (t == "view") state._schema_views.push_back(n);
    }
    sqlite3_finalize(stmt);
}

void sql_workbench_destroy(SqlWorkbenchState& state) {
    if (state._editor) {
        text_editor_destroy(editor_of(state));
        state._editor = nullptr;
    }
}

// ─────────────────────────────────────────────────────────────────────────────
// UI
// ─────────────────────────────────────────────────────────────────────────────

namespace {

void run_current_query(sqlite3* db, SqlWorkbenchState& state) {
    // Sincroniza desde el editor antes de ejecutar.
    if (state._editor) {
        const char* txt = text_editor_get_text(editor_of(state));
        if (txt) state.query = txt;
    }
    bool ok = sql_workbench_run_query(db, state.query.c_str(), state);
    if (ok) {
        // Solo apear al historial si no esta duplicada con la ultima.
        if (state.history.empty() || state.history.back() != state.query) {
            state.history.push_back(state.query);
            if (state.history.size() > 200) {
                state.history.erase(state.history.begin(),
                                    state.history.begin() + (state.history.size() - 200));
            }
        }
    }
}

void set_query(SqlWorkbenchState& state, const std::string& q) {
    state.query = q;
    if (state._editor) text_editor_set_text(editor_of(state), q.c_str());
}

void draw_schema_sidebar(SqlWorkbenchState& state) {
    using namespace fn_tokens;

    ImGui::PushStyleColor(ImGuiCol_Text, colors::text_dim);
    ImGui::TextUnformatted("SCHEMA");
    ImGui::PopStyleColor();
    ImGui::Separator();

    if (!state._schema_loaded) {
        ImGui::PushStyleColor(ImGuiCol_Text, colors::text_muted);
        ImGui::TextUnformatted("(no schema)");
        ImGui::PopStyleColor();
        return;
    }

    auto draw_section = [&](const char* label, const std::vector<std::string>& items) {
        if (items.empty()) return;
        ImGui::PushStyleColor(ImGuiCol_Text, colors::text_muted);
        ImGui::TextUnformatted(label);
        ImGui::PopStyleColor();
        for (const auto& name : items) {
            char buf[256];
            std::snprintf(buf, sizeof(buf), "%s##sw_t_%s", name.c_str(), name.c_str());
            if (ImGui::Selectable(buf, false)) {
                char q[512];
                std::snprintf(q, sizeof(q), "SELECT * FROM %s LIMIT 100;", name.c_str());
                set_query(state, q);
            }
        }
        ImGui::Dummy(ImVec2(0, spacing::xs));
    };

    draw_section("tables", state._schema_tables);
    draw_section("views",  state._schema_views);
}

void draw_history_popup(SqlWorkbenchState& state) {
    if (!state._show_history) return;

    ImGui::OpenPopup("##sw_history");
    ImGui::SetNextWindowSize(ImVec2(640, 420), ImGuiCond_Appearing);
    if (ImGui::BeginPopupModal("##sw_history", &state._show_history,
                               ImGuiWindowFlags_NoSavedSettings)) {
        ImGui::TextUnformatted("Query history (last 50)");
        ImGui::Separator();

        int total = static_cast<int>(state.history.size());
        int start = std::max(0, total - 50);
        for (int i = total - 1; i >= start; --i) {
            char buf[64];
            std::snprintf(buf, sizeof(buf), "##sw_h_%d", i);
            const std::string& q = state.history[i];
            // Mostrar primera linea como label.
            std::string preview = q;
            auto nl = preview.find('\n');
            if (nl != std::string::npos) preview = preview.substr(0, nl) + " ...";
            if (preview.size() > 120) preview = preview.substr(0, 117) + "...";
            ImGui::PushID(i);
            if (ImGui::Selectable(preview.c_str(), false)) {
                set_query(state, q);
                state._show_history = false;
                ImGui::CloseCurrentPopup();
            }
            ImGui::PopID();
        }
        ImGui::EndPopup();
    }
}

void draw_results_table(const SqlWorkbenchState& state) {
    if (state.columns.empty()) {
        using namespace fn_tokens;
        ImGui::PushStyleColor(ImGuiCol_Text, colors::text_muted);
        ImGui::TextUnformatted("(no results — run a query)");
        ImGui::PopStyleColor();
        return;
    }

    int ncols = static_cast<int>(state.columns.size());
    int nrows = static_cast<int>(state.rows.size());

    // Headers como const char*.
    std::vector<const char*> headers;
    headers.reserve(state.columns.size());
    for (const auto& c : state.columns) headers.push_back(c.c_str());

    // Cells flat row-major.
    std::vector<const char*> cells;
    cells.reserve(static_cast<size_t>(nrows) * static_cast<size_t>(ncols));
    for (const auto& row : state.rows) {
        for (int c = 0; c < ncols; ++c) {
            if (c < static_cast<int>(row.size())) cells.push_back(row[c].c_str());
            else cells.push_back("");
        }
    }

    table_view("##sw_results", headers.data(), ncols,
               cells.empty() ? nullptr : cells.data(), nrows);
}

} // namespace

void sql_workbench(const char* id, sqlite3* db, SqlWorkbenchState& state, ImVec2 size) {
    using namespace fn_tokens;

    ensure_editor(state);

    if (db && !state._schema_loaded) {
        sql_workbench_load_schema(db, state);
    }

    ImGui::PushID(id);
    ImGui::BeginChild("##sw_root", size, ImGuiChildFlags_Borders);

    if (!db) {
        ImGui::PushStyleColor(ImGuiCol_Text, colors::warning);
        ImGui::TextUnformatted("sql_workbench: no database handle");
        ImGui::PopStyleColor();
        ImGui::EndChild();
        ImGui::PopID();
        return;
    }

    // Layout: sidebar (200px) | content (editor + tabla)
    if (ImGui::BeginTable("##sw_layout", 2,
            ImGuiTableFlags_Resizable | ImGuiTableFlags_SizingFixedFit |
            ImGuiTableFlags_BordersInnerV)) {
        ImGui::TableSetupColumn("schema",  ImGuiTableColumnFlags_WidthFixed,   200.0f);
        ImGui::TableSetupColumn("content", ImGuiTableColumnFlags_WidthStretch);
        ImGui::TableNextRow();

        // ── Sidebar ──
        ImGui::TableSetColumnIndex(0);
        ImGui::BeginChild("##sw_schema", ImVec2(0, 0));
        draw_schema_sidebar(state);
        ImGui::EndChild();

        // ── Content ──
        ImGui::TableSetColumnIndex(1);
        ImGui::BeginChild("##sw_content", ImVec2(0, 0));

        // Toolbar superior.
        bool run_clicked = fn_ui::button("Run", fn_ui::ButtonVariant::Primary);
        ImGui::SameLine();
        if (fn_ui::button("History")) state._show_history = true;
        ImGui::SameLine();
        if (state.readonly) {
            ImGui::PushStyleColor(ImGuiCol_Text, colors::warning);
            ImGui::TextUnformatted("READONLY");
            ImGui::PopStyleColor();
            ImGui::SameLine();
        }
        ImGui::PushStyleColor(ImGuiCol_Text, colors::text_dim);
        ImGui::TextUnformatted("(Ctrl+Enter)");
        ImGui::PopStyleColor();

        // Editor SQL.
        ImVec2 avail = ImGui::GetContentRegionAvail();
        float editor_h = avail.y * 0.40f;
        if (editor_h < 120.0f) editor_h = 120.0f;
        text_editor_render(editor_of(state), "##sw_editor", ImVec2(-1, editor_h));

        // Atajo Ctrl+Enter (cuando el child tiene foco/hover).
        bool shortcut = ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows) &&
                        ImGui::IsKeyDown(ImGuiKey_LeftCtrl) &&
                        ImGui::IsKeyPressed(ImGuiKey_Enter, false);

        if (run_clicked || shortcut) {
            run_current_query(db, state);
        }

        // Status bar.
        if (!state.last_error.empty()) {
            ImGui::PushStyleColor(ImGuiCol_Text, colors::error);
            ImGui::TextWrapped("error: %s", state.last_error.c_str());
            ImGui::PopStyleColor();
        } else if (!state.columns.empty()) {
            ImGui::PushStyleColor(ImGuiCol_Text, colors::text_dim);
            ImGui::Text("%d rows%s in %.2f ms",
                        static_cast<int>(state.rows.size()),
                        state.truncated ? " (truncated)" : "",
                        state.last_ms);
            ImGui::PopStyleColor();
        }

        // Tabla resultado.
        draw_results_table(state);

        ImGui::EndChild();
        ImGui::EndTable();
    }

    draw_history_popup(state);

    ImGui::EndChild();
    ImGui::PopID();
}

} // namespace fn
