#include "lumacs/gtk_editor.hpp"
#include "lumacs/editor_core.hpp"
#include "lumacs/lua_api.hpp"
#include "lumacs/keybinding.hpp"
#include "lumacs/command_system.hpp"
#include "lumacs/minibuffer_manager.hpp" // Include for MinibufferManager and MinibufferMode
#include "lumacs/gtk_renderer.hpp" // Include for GtkRenderer
#include "lumacs/gtk_completion_popup.hpp" // Include for GtkCompletionPopup
#include "lumacs/buffer_manager.hpp" // Include for BufferManager
#include "lumacs/window_manager.hpp" // Include for WindowManager
#include "lumacs/kill_ring_manager.hpp" // Include for KillRingManager
#include "lumacs/register_manager.hpp" // Include for RegisterManager
#include "lumacs/macro_manager.hpp" // Include for MacroManager
#include "lumacs/rectangle_manager.hpp" // Include for RectangleManager
#include "lumacs/logger.hpp"
#include <spdlog/spdlog.h>
#include <filesystem>
#include <vector>
#include <functional> // For std::function
#include <cctype>     // For std::isalnum

// Check if GTK is enabled in build
#ifdef LUMACS_WITH_GTK
#include <gtkmm.h>
#include <pangomm.h>

namespace lumacs {

// LumacsWindow method implementations
LumacsWindow::LumacsWindow(const Glib::RefPtr<Gtk::Application>& application)
    : Gtk::ApplicationWindow(application) {
    set_title("Lumacs - GTK4");
    set_default_size(1024, 768);
}

// GtkEditor method implementations
GtkEditor::GtkEditor() : core_(nullptr), drawing_area_(nullptr), content_widget_(nullptr), last_cursor_move_time_(std::chrono::steady_clock::now()) {}

GtkEditor::~GtkEditor() {
    // Disconnect cursor timer first to prevent callbacks during destruction
    if (cursor_timer_connection_.connected()) {
        cursor_timer_connection_.disconnect();
    }
    
    // Clear core pointer to prevent any callbacks during GTK cleanup
    core_ = nullptr;
    
    // If we still have an app reference, try to quit gracefully
    if (app_ && app_->is_registered()) {
        try {
            app_->quit();
        } catch (...) {
            // Ignore exceptions during cleanup
        }
    }
    
    // Clear widget pointers - GTK manages their lifetime
    // drawing_area_ is now a raw pointer managed by Gtk::make_managed, so no delete needed
    drawing_area_ = nullptr;
    window_ = nullptr;
    content_widget_ = nullptr; // Also managed by Gtk::make_managed
    // Let app_ RefPtr be destroyed naturally
}

void GtkEditor::init() {
    // Initialize GTK application
    app_ = Gtk::Application::create("org.lumacs.editor");
    app_->signal_activate().connect(sigc::mem_fun(*this, &GtkEditor::on_activate));
}

void GtkEditor::run() {
    // Run the application's event loop
    app_->run();
}

void GtkEditor::handle_editor_event(EditorEvent event) {
    // Safety check during destruction
    if (!core_ || !app_) return;

    // Handle layout changes
    if (event == EditorEvent::WindowLayoutChanged) {
        rebuild_layout();
    } else if (event == EditorEvent::CursorMoved) {
        // Cursor moved, reset blink timer and ensure cursor is visible
        last_cursor_move_time_ = std::chrono::steady_clock::now();
        cursor_visible_ = true;
        if (content_widget_) {
             // Redraw immediately to ensure cursor is visible at new position
            queue_redraw_all_windows(content_widget_);
        }
    }

    // Request redraw on most events - recursively find all drawing areas
    // This catches CursorMoved too, but we added explicit redraw for immediate visibility
    if (content_widget_ && event != EditorEvent::CursorMoved) { // Avoid double redraw for CursorMoved
        queue_redraw_all_windows(content_widget_);
    }

    if (event == EditorEvent::CommandMode) {
        core_->minibuffer_manager().activate_minibuffer(
            MinibufferMode::Command, "M-x ",
            [this](const std::string& input) {
                if (input == "quit" || input == "q") {
                    app_->quit();
                } else {
                    auto result = core_->command_system().execute(input, {}); // Pass empty args
                    core_->set_message(result.message);
                }
            }, nullptr
        );
    } else if (event == EditorEvent::FindFileMode) {
        core_->minibuffer_manager().activate_minibuffer(
            MinibufferMode::FilePath, "Find file: ",
            [this](const std::string& input) {
                if (core_->buffer_manager().load_file(input)) core_->set_message("Loaded");
                else core_->set_message("Failed to load");
            }, nullptr
        );
    } else if (event == EditorEvent::BufferSwitchMode) {
        core_->minibuffer_manager().activate_minibuffer(
            MinibufferMode::BufferName, "Switch to buffer: ",
            [this](const std::string& input) {
                if (core_->buffer_manager().switch_buffer_in_window(input)) core_->set_message("Switched");
                else core_->set_message("Buffer not found");
            }, nullptr
        );
    } else if (event == EditorEvent::KillBufferMode) {
        core_->minibuffer_manager().activate_minibuffer(
            MinibufferMode::BufferName, "Kill buffer: ",
            [this](const std::string& input) {
                if (core_->buffer_manager().close_buffer(input)) core_->set_message("Killed buffer");
                else core_->set_message("Buffer not found");
            }, nullptr
        );
    } else if (event == EditorEvent::ThemeSelectionMode) {
        core_->minibuffer_manager().activate_minibuffer(
            MinibufferMode::ThemeName, "Set theme: ",
            [this](const std::string& input) {
                auto theme_names = core_->theme_manager().theme_names();
                auto it = std::find(theme_names.begin(), theme_names.end(), input);
                if (it != theme_names.end()) {
                    core_->theme_manager().set_active_theme(input);
                    core_->set_message("Switched to theme: " + input);
                } else {
                    core_->set_message("Theme not found: " + input);
                }
            }, nullptr
        );
    } else if (event == EditorEvent::ISearchMode) {
        core_->minibuffer_manager().activate_minibuffer(
            MinibufferMode::ISearch, "I-search: ",
            [this](const std::string& input) { /* TODO: Implement actual isearch logic */ core_->set_message("I-search: " + input); }, nullptr
        );
    } else if (event == EditorEvent::TransientMessageCleared) {
        // Force redraw to clear the message
        if (content_widget_) queue_redraw_all_windows(content_widget_);
    } else if (event == EditorEvent::Quit) {
        // Disconnect timer before quitting to prevent segfault
        if (cursor_timer_connection_.connected()) {
            cursor_timer_connection_.disconnect();
        }
        // Use idle callback to quit safely after current event processing
        Glib::signal_idle().connect_once([this]() {
            if (app_) {
                app_->quit();
            }
        });
    }
    
    // Check if minibuffer is active AFTER processing event to decide popup visibility
    if (core_->minibuffer_manager().is_active()) {
        show_completion_popup(); // Show/hide completion popup when minibuffer state changes
        if (content_widget_) queue_redraw_all_windows(content_widget_);
    } else {
        hide_completion_popup(); // Minibuffer deactivated, hide popup
    }
}

void GtkEditor::set_core(EditorCore* core) {
    core_ = core;
}

// Helper to recursively find and redraw all drawing areas
void GtkEditor::queue_redraw_all_windows(Gtk::Widget* widget) {
    if (!widget) return;
    
    if (auto drawing_area = dynamic_cast<Gtk::DrawingArea*>(widget)) {
        drawing_area->queue_draw();
    } else if (auto paned = dynamic_cast<Gtk::Paned*>(widget)) {
        if (auto start_child = paned->get_start_child()) {
            queue_redraw_all_windows(start_child);
        }
        if (auto end_child = paned->get_end_child()) {
            queue_redraw_all_windows(end_child);
        }
    }
}

// Helper to convert GDK keyval and modifiers to Lumacs key name
std::string GtkEditor::get_lumacs_key_name(guint keyval, Gdk::ModifierType state) {
    std::string key_name;
    if ((state & Gdk::ModifierType::CONTROL_MASK) == Gdk::ModifierType::CONTROL_MASK) {
        key_name += "C-";
    }
    // Alt key is often mapped to Meta in Emacs-like editors
    if ((state & Gdk::ModifierType::ALT_MASK) == Gdk::ModifierType::ALT_MASK || (state & Gdk::ModifierType::META_MASK) == Gdk::ModifierType::META_MASK) { 
        key_name += "M-";
    }
    // Shift is generally handled by the keyval itself for letters (e.g., 'A' vs 'a')
    // For special keys, we might want "S-ArrowUp" but for now, rely on keyval.

    // GDK_KEY_Tab does not have a unicode value, handle separately
    if (keyval == GDK_KEY_Tab) {
        key_name += "Tab";
    }
    else if (keyval == GDK_KEY_ISO_Left_Tab) { // Shift-Tab for cycling backwards
        key_name += "S-Tab";
    }
    else if (keyval >= GDK_KEY_space && keyval <= GDK_KEY_asciitilde) { // Printable ASCII
        key_name += static_cast<char>(gdk_keyval_to_unicode(keyval));
    } else {
        // Handle special keys
        switch (keyval) {
            case GDK_KEY_Return: key_name += "Return"; break;
            case GDK_KEY_Escape: key_name += "Escape"; break;
            case GDK_KEY_BackSpace: key_name += "Backspace"; break; // Fixed case sensitivity
            case GDK_KEY_Delete: key_name += "Delete"; break;
            case GDK_KEY_Up: key_name += "ArrowUp"; break;
            case GDK_KEY_Down: key_name += "ArrowDown"; break;
            case GDK_KEY_Left: key_name += "ArrowLeft"; break;
            case GDK_KEY_Right: key_name += "ArrowRight"; break;
            case GDK_KEY_Home: key_name += "Home"; break;
            case GDK_KEY_End: key_name += "End"; break;
            case GDK_KEY_Page_Up: key_name += "PageUp"; break;
            case GDK_KEY_Page_Down: key_name += "PageDown"; break;
            case GDK_KEY_F1: key_name += "F1"; break;
            case GDK_KEY_F2: key_name += "F2"; break;
            case GDK_KEY_F3: key_name += "F3"; break;
            case GDK_KEY_F4: key_name += "F4"; break;
            case GDK_KEY_F5: key_name += "F5"; break;
            case GDK_KEY_F6: key_name += "F6"; break;
            case GDK_KEY_F7: key_name += "F7"; break;
            case GDK_KEY_F8: key_name += "F8"; break;
            case GDK_KEY_F9: key_name += "F9"; break;
            case GDK_KEY_F10: key_name += "F10"; break;
            case GDK_KEY_F11: key_name += "F11"; break;
            case GDK_KEY_F12: key_name += "F12"; break;
            // Add more special keys as needed
            default:
                // Fallback for unhandled keys
                // Gdk::keyval_name(keyval) might give "dead_acute", which is not good.
                // Best to ignore unhandled keys for now or map explicitly.
                return ""; // Consume if we couldn't map to a Lumacs key (to avoid random chars appearing)
        }
    }
    return key_name;
}


bool GtkEditor::on_global_key_pressed(guint keyval, guint keycode, Gdk::ModifierType state) {
    (void)keycode;
    spdlog::debug("GtkEditor::on_global_key_pressed: keyval={}, state={}", keyval, (int)state);
    if (!core_) return false;

    // Translate GDK event to Lumacs key name
    std::string lumacs_key_name = get_lumacs_key_name(keyval, state);
    if (lumacs_key_name.empty()) {
        return false; // Not a key we care about or couldn't map
    }

    if (core_->minibuffer_manager().is_active()) {
        // Minibuffer is active, keys should primarily go to it, or completion popup
        if (completion_popup_ && completion_popup_->get_visible()) {
            // If completion popup is visible, it takes precedence for navigation/selection keys
            if (lumacs_key_name == "ArrowUp") {
                completion_popup_->select_previous();
                return true;
            } else if (lumacs_key_name == "ArrowDown") {
                completion_popup_->select_next();
                return true;
            } else if (lumacs_key_name == "Return") {
                if (auto selected = completion_popup_->get_selected_candidate()) {
                    on_completion_selected(selected.value());
                } else {
                    // If no completion selected, but Return pressed, pass to minibuffer
                    core_->minibuffer_manager().handle_key_event(lumacs_key_name);
                }
                return true;
            } else if (lumacs_key_name == "Escape") {
                on_completion_cancelled();
                return true;
            } else if (lumacs_key_name == "Tab" || lumacs_key_name == "S-Tab") {
                 // Forward Tab to MinibufferManager for its internal cycle
                 core_->minibuffer_manager().handle_key_event(lumacs_key_name);
                 // Redraw popup to show updated selection
                 show_completion_popup(); 
                 return true;
            }
        }
        
        // If key is not handled by completion popup, or popup is not visible,
        // pass to MinibufferManager
        bool handled_by_minibuffer = core_->minibuffer_manager().handle_key_event(lumacs_key_name);
        
        // After any minibuffer key event, update and potentially show/hide completion
        if (handled_by_minibuffer) {
            if (core_->minibuffer_manager().get_completion_candidates().empty()) {
                hide_completion_popup();
            } else {
                show_completion_popup();
            }
            queue_redraw_all_windows(content_widget_); // Redraw minibuffer content
        } else {
            // If minibuffer didn't handle it, it could be a keybinding for the editor that
            // should still work while the minibuffer is active (e.g., C-g for quit).
            // For now, we assume if minibuffer is active, it consumes all relevant keys.
            // If the key is not handled, it probably means it's irrelevant to minibuffer input.
            // But we should still consume it to avoid propagating to editor keybindings accidentally.
            // Returning true consumes the event.
            return true; 
        }
        return handled_by_minibuffer; // Return if minibuffer handled it
    } else {
        // Minibuffer not active, pass to main keybinding manager
        KeyProcessingResult result = core_->keybinding_manager().process_key(lumacs_key_name);
        if (result.type == KeyResult::Executed) {
            queue_redraw_all_windows(content_widget_);
            return true;
        } else if (result.type == KeyResult::Partial) {
            // Multi-key sequence in progress, wait for next key
            queue_redraw_all_windows(content_widget_);
            return true;
        } else if (result.type == KeyResult::Unbound) {
            // Check if it's a self-insertable character
            // We assume single characters without modifiers (except Shift) are self-insertable
            // if they are not bound to something else.
            // lumacs_key_name format: "a", "S-a", "C-a"
            bool has_ctrl = lumacs_key_name.find("C-") != std::string::npos;
            bool has_meta = lumacs_key_name.find("M-") != std::string::npos;
            
            // Allow Shift-key (e.g. "S-a" -> "A" is handled by GDK usually giving "A" directly if we use unicode, 
            // but our get_lumacs_key_name handles it.
            // Actually, for printable chars, get_lumacs_key_name returns the unicode char (e.g. "A").
            // So we just check length == 1 and no modifiers.
            
            if (!has_ctrl && !has_meta && lumacs_key_name.length() == 1) {
                 // Execute self-insert-command
                 core_->command_system().execute("self-insert-command", {lumacs_key_name});
                 
                 // --- Macro Recording Logic for Self-Insert ---
                 if (core_->is_recording_macro()) {
                     core_->record_key_sequence(lumacs_key_name);
                 }
                 // --------------------------------------------

                 queue_redraw_all_windows(content_widget_);
                 return true;
            }
        }
        // If not processed by keybinding, let GTK handle it (e.g., for system shortcuts)
        return false; 
    }
}

void GtkEditor::on_activate() {
    // Create main window and associate with the application
    // Note: The window is owned by the application through GObject reference counting
    // We just keep a raw pointer for access, but don't manage its lifetime
    window_ = new LumacsWindow(app_);

    // Build initial layout (single window)
    rebuild_layout();

    // Handle window close event
    window_->signal_close_request().connect([this]() -> bool {
        // Cleanup before closing
        if (cursor_timer_connection_.connected()) {
            cursor_timer_connection_.disconnect();
        }
        core_ = nullptr;
        drawing_area_ = nullptr;
        content_widget_ = nullptr;
        
        // Allow window to close
        return false; // false means "allow close"
    }, false);

    // Show window
    window_->present();
    if (drawing_area_) {
        drawing_area_->grab_focus();
    }
    
    // Initialize GtkRenderer after drawing_area_ is set
    if (core_ && drawing_area_) {
        gtk_renderer_ = std::make_unique<GtkRenderer>(*core_, *drawing_area_);
    }

    // Set up cursor blinking timer (BLINK_INTERVAL like Emacs)
    cursor_timer_connection_ = Glib::signal_timeout().connect(
        sigc::mem_fun(*this, &GtkEditor::on_cursor_blink), BLINK_INTERVAL.count()
    );

    // Initialize completion popup
    completion_popup_ = std::make_unique<GtkCompletionPopup>();
    // Popovers are automatically transient and handle their own hide_on_close logic.
    completion_popup_->signal_candidate_selected().connect(sigc::mem_fun(*this, &GtkEditor::on_completion_selected));
    completion_popup_->signal_cancelled().connect(sigc::mem_fun(*this, &GtkEditor::on_completion_cancelled));

    // Attach global key controller to the main window
    auto global_key_controller = Gtk::EventControllerKey::create();
    global_key_controller->set_propagation_phase(Gtk::PropagationPhase::CAPTURE);
    global_key_controller->signal_key_pressed().connect(
        sigc::mem_fun(*this, &GtkEditor::on_global_key_pressed), false 
    );
    window_->add_controller(global_key_controller);
}

// Rendering
void GtkEditor::on_draw(const Cairo::RefPtr<Cairo::Context>& cr, int width, int height) {
    // Safety check - don't draw if core is destroyed or gtk_renderer_ not initialized
    if (!core_ || !gtk_renderer_) return;

    gtk_renderer_->on_draw(cr, width, height, cached_active_window_, cursor_visible_);
}

// Cursor blinking callback
bool GtkEditor::on_cursor_blink() {
    // Safety check - don't blink if core is destroyed or no drawing area
    if (!core_ || !drawing_area_ || !app_) {
        return false; // Stop the timer
    }
    
    // Double check that the app is still running
    if (!app_->is_registered()) {
        return false; // Stop the timer
    }
    
    try {
        auto now = std::chrono::steady_clock::now();
        if (now - last_cursor_move_time_ > BLINK_STATIONARY_THRESHOLD) {
            // Only blink if cursor has been stationary for a while
            cursor_visible_ = !cursor_visible_;
            core_->check_and_clear_message(); // Check and clear messages
            drawing_area_->queue_draw();
        } else {
            // Cursor is still moving or just stopped, keep it visible
            cursor_visible_ = true;
            // No need to queue_draw here, it will be done on CursorMoved or next blink.
        }
    } catch (...) {
        return false; // Stop timer on any exception
    }
    
    return true; // Continue timer
}

// Rebuild the GTK layout to match the core's window tree
void GtkEditor::rebuild_layout() {
    if (!core_ || !window_) return;

    auto root_layout = core_->root_layout();
    if (!root_layout) return;

    // Remove existing content
    if (content_widget_) {
        window_->unset_child();
    }

    // Clear the drawing area reference since we're rebuilding
    drawing_area_ = nullptr;

    // Initialize cached active window to prevent focus jumping
    cached_active_window_ = core_->active_window();

    // Create new layout based on the tree
    content_widget_ = create_widget_for_layout_node(root_layout);
    if (content_widget_) {
        window_->set_child(*content_widget_);
    }
}

// Create GTK widget tree from LayoutNode tree
Gtk::Widget* GtkEditor::create_widget_for_layout_node(std::shared_ptr<LayoutNode> node) {
    if (!node) return nullptr;

    if (node->type == LayoutNode::Type::Leaf) {
        // Create a new DrawingArea for this window
        auto drawing_area = Gtk::make_managed<Gtk::DrawingArea>();
        
        // Set up drawing for this specific window
        // Use a weak reference to the window to avoid crashes if the layout is rebuilt
        std::weak_ptr<Window> weak_window = node->window;
        drawing_area->set_draw_func([this, weak_window, drawing_area](const Cairo::RefPtr<Cairo::Context>& cr, int width, int height) {
            if (auto window_ptr = weak_window.lock()) { // Correctly capture shared_ptr
                if (gtk_renderer_) {
                    gtk_renderer_->draw_window(cr, width, height, window_ptr, drawing_area, cached_active_window_, cursor_visible_);
                }
            }
        });
        
        drawing_area->set_focusable(true);
        
        // Add click handling to set active window explicitly and move cursor
        // We use GestureClick instead of EventControllerFocus to avoid spurious focus changes
        auto click_controller = Gtk::GestureClick::create();
        std::weak_ptr<Window> weak_window_click = node->window;
        click_controller->signal_pressed().connect([this, weak_window_click, drawing_area](int /*n_press*/, double x, double y) {
            if (auto window = weak_window_click.lock()) {
                // 1. Activate Window
                if (core_ && core_->active_window() != window) {
                    core_->window_manager().set_active_window(window);
                    cached_active_window_ = window; // Cache for rendering to prevent focus jumping
                }
                // IMPORTANT: Grab keyboard focus for this widget
                drawing_area->grab_focus();
                
                // 2. Move Cursor
                if (gtk_renderer_) {
                    if (auto pos = gtk_renderer_->resolve_screen_pos(window, x, y)) {
                        window->set_cursor(*pos);
                        // Clear mark on simple click
                        window->buffer().deactivate_mark();
                        drawing_area->queue_draw();
                    }
                }
            }
        });
        drawing_area->add_controller(click_controller);

        // Add Drag Gesture for Selection
        auto drag_controller = Gtk::GestureDrag::create();
        std::weak_ptr<Window> weak_window_drag = node->window;
        
        drag_controller->signal_drag_begin().connect([this, weak_window_drag, drawing_area](double x, double y) {
            if (auto window = weak_window_drag.lock()) {
                if (gtk_renderer_) {
                    if (auto pos = gtk_renderer_->resolve_screen_pos(window, x, y)) {
                        // Set mark at start of drag
                        window->buffer().set_mark(*pos);
                        window->set_cursor(*pos);
                        drawing_area->queue_draw();
                    }
                }
            }
        });
        
        drag_controller->signal_drag_update().connect([this, weak_window_drag, drawing_area, drag_controller](double dx, double dy) {
            if (auto window = weak_window_drag.lock()) {
                 double start_x, start_y;
                 if (drag_controller->get_start_point(start_x, start_y) && gtk_renderer_) {
                     double current_x = start_x + dx;
                     double current_y = start_y + dy;
                     
                     if (auto pos = gtk_renderer_->resolve_screen_pos(window, current_x, current_y)) {
                         window->set_cursor(*pos);
                         drawing_area->queue_draw();
                     }
                 }
            }
        });
        
        drawing_area->add_controller(drag_controller);
        
        // Add scroll handling
        auto scroll_controller = Gtk::EventControllerScroll::create();
        scroll_controller->set_flags(Gtk::EventControllerScroll::Flags::VERTICAL);
        std::weak_ptr<Window> weak_window_scroll = node->window;
        scroll_controller->signal_scroll().connect([weak_window_scroll, drawing_area](double /*dx*/, double dy) -> bool {
             if (auto window = weak_window_scroll.lock()) {
                 // dy is usually 1.0 or -1.0 for wheel steps
                 // Scroll 3 lines per step
                 int lines = static_cast<int>(dy * 3.0);
                 if (lines != 0) {
                     window->scroll_lines(lines);
                     drawing_area->queue_draw();
                 }
                 return true;
             }
             return false;
        }, true);
        drawing_area->add_controller(scroll_controller);
        
        // Context menus and tooltips removed per user request (Phase A.1)
        
        // Store reference for single-window compatibility
        if (!drawing_area_) {
            drawing_area_ = drawing_area;
        }
        
        return drawing_area;
    } else {
        // Create a paned container for splits
        Gtk::Paned* paned = nullptr;
        if (node->type == LayoutNode::Type::HorizontalSplit) {
            paned = Gtk::make_managed<Gtk::Paned>(Gtk::Orientation::VERTICAL);
        } else { // VerticalSplit  
            paned = Gtk::make_managed<Gtk::Paned>(Gtk::Orientation::HORIZONTAL);
        }
        
        // Recursively create children
        auto child1 = create_widget_for_layout_node(node->child1);
        auto child2 = create_widget_for_layout_node(node->child2);
        
        if (child1) paned->set_start_child(*child1);
        if (child2) paned->set_end_child(*child2);
        
        // Set initial position based on ratio
        // Use signal_map to set position when widget is ready
        paned->signal_map().connect([paned, node](){
             int width = paned->get_width();
             int height = paned->get_height();
             int size = (paned->get_orientation() == Gtk::Orientation::HORIZONTAL) ? width : height;
             
             // Fallback if size not yet available
             if (size <= 1) size = 1000; // Assume a reasonable default window size
             
             paned->set_position(static_cast<int>(size * node->ratio));
        });
        
        return paned;
    }
}

// === Completion Popup Helpers ===

void GtkEditor::show_completion_popup() {
    if (!completion_popup_ || !window_) return;

    auto candidates = core_->minibuffer_manager().get_completion_candidates();
    if (candidates.empty()) {
        hide_completion_popup();
        return;
    }

    // Minibuffer is rendered at the bottom of the main window/drawing area.
    // We need to position the popover relative to the main window, pointing to the minibuffer area.
    Gdk::Rectangle rect;
    rect.set_x(0); // Minibuffer starts at x=0
    rect.set_y(window_->get_height() - 1); // Last line of the window
    rect.set_width(window_->get_width());
    rect.set_height(1); // Small height to represent the minibuffer line

    completion_popup_->show_popup(candidates, 0, *window_, rect.get_x(), rect.get_y());
}

void GtkEditor::hide_completion_popup() {
    if (completion_popup_) {
        completion_popup_->hide_popup();
    }
}

void GtkEditor::on_completion_selected(CompletionCandidate candidate) {
    core_->minibuffer_manager().set_input_buffer(candidate.text);
    hide_completion_popup();
}

void GtkEditor::on_completion_cancelled() {
    hide_completion_popup();
}

std::unique_ptr<IEditorView> create_gtk_editor() {
    return std::make_unique<GtkEditor>();
}

} // namespace lumacs

#else // LUMACS_WITH_GTK not defined

namespace lumacs {
std::unique_ptr<IEditorView> create_gtk_editor() {
    spdlog::error("Error: Lumacs was built without GTK support.");
    return nullptr;
}
} // namespace lumacs

#endif