#include "lumacs/gtk_renderer.hpp"
#include "lumacs/editor_core.hpp"
#include "lumacs/minibuffer_manager.hpp" // For MinibufferManager and MinibufferMode
#include "lumacs/keybinding.hpp" // For KeyBindingManager
#include "lumacs/message_formatter.hpp"
#include <algorithm> // For std::max, std::min, std::round
#include <cmath> // For std::ceil, std::floor, std::round
#include <spdlog/spdlog.h> // Added for debug logging
#include <glibmm/ustring.h> // For UTF-8 handling

namespace lumacs {

GtkRenderer::GtkRenderer(EditorCore& core, Gtk::Widget& context_widget)
    : core_(core), context_widget_(context_widget) {
    // Get font size from config
    font_size_ = core_.config().get<int>("font_size", 12);
    initialize_font_metrics(); // Initialize font metrics once during construction
}

void GtkRenderer::initialize_font_metrics() {
    // Use a minimal string for layout creation
    auto layout = context_widget_.create_pango_layout(" ");
    font_desc_ = Pango::FontDescription("Monospace " + std::to_string(font_size_));
    layout->set_font_description(font_desc_);

    // Configure font options for crisp text rendering
    auto pango_context = layout->get_context();
    Cairo::FontOptions font_options;
    font_options.set_antialias(Cairo::ANTIALIAS_SUBPIXEL);
    font_options.set_hint_style(Cairo::FontOptions::HintStyle::MEDIUM);
    font_options.set_hint_metrics(Cairo::FontOptions::HintMetrics::ON);
    font_options.set_subpixel_order(Cairo::SUBPIXEL_ORDER_RGB);
    pango_context->set_cairo_font_options(font_options);

    Pango::FontMetrics metrics = pango_context->get_metrics(font_desc_);

    // Calculate line height with proper spacing - use ascent + descent + small padding
    double ascent = static_cast<double>(metrics.get_ascent()) / PANGO_SCALE;
    double descent = static_cast<double>(metrics.get_descent()) / PANGO_SCALE;

    // Round up line height to avoid fractional pixel issues
    line_height_ = std::ceil(ascent + descent + 2.0); // +2 for minimal line spacing
    ascent_ = ascent;
    descent_ = descent;

    // Get character width more precisely using index_to_pos for a known monospace char
    layout->set_text("M"); // Use a single, representative character
    Pango::Rectangle pos0 = layout->index_to_pos(0); // Position at index 0 (before 'M')
    Pango::Rectangle pos1 = layout->index_to_pos(1); // Position at index 1 (after 'M')
    char_width_ = static_cast<double>(pos1.get_x() - pos0.get_x()) / PANGO_SCALE;

    // Get HiDPI scale factor
    scale_factor_ = context_widget_.get_scale_factor();

    spdlog::debug("GtkRenderer font metrics: line_height={}, ascent={}, descent={}, char_width={}, scale={}",
                  line_height_, ascent_, descent_, char_width_, scale_factor_);

    font_initialized_ = true;
}

size_t GtkRenderer::char_to_byte_index(const std::string& str, size_t char_pos) {
    // Convert character position to byte index for UTF-8 string
    Glib::ustring ustr(str);
    if (char_pos >= ustr.length()) {
        return str.length();
    }
    // Get the byte offset for the character at char_pos
    auto it = ustr.begin();
    std::advance(it, char_pos);
    return it.base() - ustr.begin().base();
}

void GtkRenderer::apply_face_attributes(Pango::AttrList& attr_list, const FaceAttributes& face, int start_index, int end_index) {
    if (start_index >= end_index) return;

    // Foreground
    if (face.foreground) {
        auto attr = Pango::Attribute::create_attr_foreground(
            face.foreground->r * 257, face.foreground->g * 257, face.foreground->b * 257);
        attr.set_start_index(start_index);
        attr.set_end_index(end_index);
        attr_list.insert(attr);
    }
    // Background
    if (face.background) {
        auto attr = Pango::Attribute::create_attr_background(
            face.background->r * 257, face.background->g * 257, face.background->b * 257);
        attr.set_start_index(start_index);
        attr.set_end_index(end_index);
        attr_list.insert(attr);
    }
    // Font Family
    if (face.family) {
        auto attr = Pango::Attribute::create_attr_family(*face.family);
        attr.set_start_index(start_index);
        attr.set_end_index(end_index);
        attr_list.insert(attr);
    }
    // Weight
    if (face.weight) {
        Pango::Weight w = Pango::Weight::NORMAL;
        if (*face.weight == FontWeight::Bold) w = Pango::Weight::BOLD;
        else if (*face.weight == FontWeight::Light) w = Pango::Weight::LIGHT;
        auto attr = Pango::Attribute::create_attr_weight(w);
        attr.set_start_index(start_index);
        attr.set_end_index(end_index);
        attr_list.insert(attr);
    }
    // Slant/Style
    if (face.slant) {
        Pango::Style s = Pango::Style::NORMAL;
        if (*face.slant == FontSlant::Italic) s = Pango::Style::ITALIC;
        else if (*face.slant == FontSlant::Oblique) s = Pango::Style::OBLIQUE;
        auto attr = Pango::Attribute::create_attr_style(s);
        attr.set_start_index(start_index);
        attr.set_end_index(end_index);
        attr_list.insert(attr);
    }
    // Underline
    if (face.underline && *face.underline) {
        auto attr = Pango::Attribute::create_attr_underline(Pango::Underline::SINGLE);
        attr.set_start_index(start_index);
        attr.set_end_index(end_index);
        attr_list.insert(attr);
    }
}

std::optional<Position> GtkRenderer::resolve_screen_pos(std::shared_ptr<Window> window, double x, double y) {
    if (!window || line_height_ <= 0 || char_width_ <= 0) return std::nullopt;

    int row = static_cast<int>((y - PADDING_TOP) / line_height_);
    int col = static_cast<int>((x - PADDING_LEFT) / char_width_);
    
    if (row < 0) row = 0;
    if (col < 0) col = 0;
    
    auto viewport = window->viewport();
    size_t target_line = viewport.scroll_offset + row;
    size_t target_col = viewport.horizontal_offset + col;
    
    // Clamp to buffer bounds
    if (target_line >= window->buffer().line_count()) {
         target_line = window->buffer().line_count() - 1;
    }
    
    // Clamp column to line length
    size_t line_len = window->buffer().line(target_line).length();
    if (target_col > line_len) target_col = line_len;
    
    return Position{target_line, target_col};
}

void GtkRenderer::on_draw(const Cairo::RefPtr<Cairo::Context>& cr, int width, int height,
                            std::shared_ptr<Window> active_window_cache, bool cursor_visible_state) {
    // Safety check - don't draw if core has no active theme (during destruction)
    if (!core_.theme_manager().active_theme()) return;

    // Fill background of the entire drawing area
    auto theme = core_.theme_manager().active_theme();
    Color bg = theme->get_bg_color(ThemeElement::Background);
    cr->set_source_rgb(bg.r / 255.0, bg.g / 255.0, bg.b / 255.0);
    cr->paint();

    // Get the active window from core to render it
    auto active_window = core_.active_window();
    if (!active_window) return;

    // Render the active window (main editor area)
    draw_window(cr, width, height, active_window, &context_widget_, active_window_cache, cursor_visible_state);

    // Use a temporary layout for modeline/minibuffer as they are dynamic
    auto temp_layout = Pango::Layout::create(context_widget_.get_pango_context());
    temp_layout->set_font_description(font_desc_);

    // Render modeline
    render_modeline_for_window(cr, width, height, active_window, active_window_cache);
    
    // Render minibuffer
    render_minibuffer(cr, width, height);
}

void GtkRenderer::draw_window(const Cairo::RefPtr<Cairo::Context>& cr, int width, int height,
                                std::shared_ptr<Window> window, Gtk::Widget* widget,
                                std::shared_ptr<Window> active_window_cache, bool cursor_visible_state) {
    if (!core_.theme_manager().active_theme() || !window || !widget) return;

    // Use a temporary layout that is properly initialized
    auto layout = Pango::Layout::create(widget->get_pango_context());
    layout->set_font_description(font_desc_);

    const auto cursor = window->cursor();
    const auto& buffer = window->buffer();
    auto theme = core_.theme_manager().active_theme();

    // Fill background
    Color bg = theme->get_bg_color(ThemeElement::Background);
    cr->set_source_rgb(bg.r / 255.0, bg.g / 255.0, bg.b / 255.0);
    cr->paint();

    int content_width_px = width - static_cast<int>(PADDING_LEFT + PADDING_RIGHT);
    int content_height_px = height - static_cast<int>(PADDING_TOP + PADDING_BOTTOM);

    // Use floor for visible lines/cols to avoid rendering partial lines
    int visible_lines = static_cast<int>(std::floor(content_height_px / line_height_));
    int visible_cols = static_cast<int>(std::floor(content_width_px / char_width_));

    // Reserve space for modeline (1 line). Minibuffer is now separate.
    int editor_lines = std::max(0, visible_lines - 1);
    window->set_viewport_size(visible_cols, editor_lines);

    // Visual line mode (soft word wrap)
    bool visual_line_mode = core_.visual_line_mode();
    if (visual_line_mode) {
        layout->set_width(content_width_px * PANGO_SCALE);
        layout->set_wrap(Pango::WrapMode::WORD_CHAR);
    }

    // Region/Mark Calculation
    std::optional<Range> selection_range;
    if (buffer.has_active_mark() && buffer.mark()) {
         selection_range = buffer.get_region(window->cursor());
    }

    // Get default foreground color from theme
    Color fg = theme->get_fg_color(ThemeElement::Normal);
    cr->set_source_rgb(fg.r / 255.0, fg.g / 255.0, fg.b / 255.0);

        // Render visible lines
        auto [start_line, end_line] = window->visible_line_range();
        int horizontal_offset = visual_line_mode ? 0 : window->viewport().horizontal_offset;

        auto& window_cache = render_cache_[window.get()];

        // In visual line mode, we need to track screen rows consumed
        int current_screen_row = 0;
        size_t buffer_line_idx = start_line;

        while (current_screen_row < editor_lines && buffer_line_idx < buffer.line_count()) {
            // Legacy loop condition for non-wrapped mode
            if (!visual_line_mode && buffer_line_idx >= end_line) break;

            const auto& line_text = buffer.line(buffer_line_idx);
            const auto& styles = buffer.get_line_styles(buffer_line_idx);

            // Apply horizontal scrolling (disabled in visual line mode)
            std::string visible_text;
            if (horizontal_offset < static_cast<int>(line_text.length())) {
                visible_text = line_text.substr(horizontal_offset);
            } else if (visual_line_mode) {
                visible_text = line_text;  // Show full line in visual mode
            }

            // Always set text for the layout before any further operations, especially cursor positioning
            layout->set_text(visible_text);

            // Calculate how many screen rows this line takes (for visual line mode)
            int line_screen_rows = 1;
            if (visual_line_mode && !visible_text.empty()) {
                int layout_height = 0;
                layout->get_pixel_size(layout_height, layout_height);
                auto iter = layout->get_iter();
                int line_count = 0;
                do {
                    line_count++;
                } while (iter.next_line());
                line_screen_rows = std::max(1, line_count);
            }

            // Calculate pixel-aligned positions to avoid blurry text
            // Round to whole pixels to prevent subpixel rendering artifacts
            double text_x = std::round(PADDING_LEFT);
            double text_y = std::round(PADDING_TOP + current_screen_row * line_height_);

            // For wrapped lines, we need the full layout height
            double effective_line_height = visual_line_mode ? (line_screen_rows * line_height_) : line_height_;

            // Check for Region/Selection Intersection
            bool has_selection = false;
            int sel_start = 0, sel_end = 0;
    
            if (selection_range) {
                 if (buffer_line_idx >= selection_range->start.line && buffer_line_idx <= selection_range->end.line) {
                     has_selection = true;
                     size_t s_col = (buffer_line_idx == selection_range->start.line) ? selection_range->start.column : 0;
                     size_t e_col = (buffer_line_idx == selection_range->end.line) ? selection_range->end.column : line_text.length();
                     
                     sel_start = static_cast<int>(s_col) - horizontal_offset;
                     sel_end = static_cast<int>(e_col) - horizontal_offset;
                     
                     sel_start = std::max(0, sel_start);
                     sel_end = std::min(static_cast<int>(visible_text.length()), sel_end);
                     
                     if (sel_start >= sel_end) has_selection = false;
                 }
            }
    
            bool use_cache = false;
            if (!has_selection && window_cache.count(buffer_line_idx)) {
                 auto& entry = window_cache[buffer_line_idx];
                 // Validate text content, styles, AND scale factor to ensure cache is still valid
                 // (styles change on theme switch, scale changes on display switch)
                 if (entry.text_content == visible_text && entry.styles == styles && entry.scale == scale_factor_) {
                     cr->set_source(entry.surface, text_x, text_y);
                     cr->paint();
                     use_cache = true;
                 }
            }
    
            if (!use_cache) {
                // Draw fresh
                // layout->set_text(visible_text); // Moved this line outside the if block
                Pango::AttrList attr_list;
    
                // 1. Apply Syntax Highlighting
                for (const auto& style : styles) {
                    if (auto face = theme->get_face(style.attr.face_name)) {
                        int start = static_cast<int>(style.range.start.column) - horizontal_offset;
                        int end = static_cast<int>(style.range.end.column) - horizontal_offset;
                        
                        start = std::max(0, start);
                        end = std::min(static_cast<int>(visible_text.length()), end);
                        
                        if (start < end) {
                            apply_face_attributes(attr_list, *face, start, end);
                        }
                    }
                }
    
                // 2. Apply Region/Selection Highlight (if any)
                if (has_selection) {
                    if (auto region_face = theme->get_face("region")) {
                        apply_face_attributes(attr_list, *region_face, sel_start, sel_end);
                    } else {
                        auto attr = Pango::Attribute::create_attr_background(0x4444, 0x4444, 0x4444);
                        attr.set_start_index(sel_start);
                        attr.set_end_index(sel_end);
                        attr_list.insert(attr);
                    }
                }
                
                layout->set_attributes(attr_list);
    
                // Render
                if (!has_selection && !visual_line_mode) {
                    // Render to Cache surface first (disabled in visual line mode for simplicity)
                    // Create HiDPI-aware surface
                    int surf_width = std::max(1, static_cast<int>(std::ceil(content_width_px * scale_factor_)));
                    int surf_height = static_cast<int>(std::ceil(effective_line_height * scale_factor_));

                    auto surface = Cairo::ImageSurface::create(Cairo::Surface::Format::ARGB32, surf_width, surf_height);
                    // Set device scale for HiDPI - this makes Cairo render at higher resolution
                    cairo_surface_set_device_scale(surface->cobj(), scale_factor_, scale_factor_);

                    auto cr_surf = Cairo::Context::create(surface);

                    // Clear surface
                    cr_surf->set_operator(Cairo::Context::Operator::CLEAR);
                    cr_surf->paint();
                    cr_surf->set_operator(Cairo::Context::Operator::OVER);

                    cr_surf->set_source_rgb(fg.r / 255.0, fg.g / 255.0, fg.b / 255.0);
                    layout->show_in_cairo_context(cr_surf); // Render layout to surface

                    // Store with scale factor for cache validation
                    LineCacheEntry entry;
                    entry.text_content = visible_text;
                    entry.surface = surface;
                    entry.styles = styles;
                    entry.scale = scale_factor_;
                    window_cache[buffer_line_idx] = entry;

                    // Blit to screen
                    cr->set_source(surface, text_x, text_y);
                    cr->paint();

                } else {
                    // Render directly to screen (don't cache selected text or wrapped lines)
                    cr->move_to(text_x, text_y);
                    cr->set_source_rgb(fg.r / 255.0, fg.g / 255.0, fg.b / 255.0);
                    layout->show_in_cairo_context(cr);
                }
            } else if (visual_line_mode) {
                // In visual line mode, always render directly (simpler for wrapped text)
                Pango::AttrList attr_list;

                // Apply Syntax Highlighting
                for (const auto& style : styles) {
                    if (auto face = theme->get_face(style.attr.face_name)) {
                        int start = static_cast<int>(style.range.start.column);
                        int end = static_cast<int>(style.range.end.column);

                        start = std::max(0, start);
                        end = std::min(static_cast<int>(visible_text.length()), end);

                        if (start < end) {
                            apply_face_attributes(attr_list, *face, start, end);
                        }
                    }
                }

                // Apply selection if any
                if (has_selection) {
                    if (auto region_face = theme->get_face("region")) {
                        apply_face_attributes(attr_list, *region_face, sel_start, sel_end);
                    } else {
                        auto attr = Pango::Attribute::create_attr_background(0x4444, 0x4444, 0x4444);
                        attr.set_start_index(sel_start);
                        attr.set_end_index(sel_end);
                        attr_list.insert(attr);
                    }
                }

                layout->set_attributes(attr_list);

                cr->move_to(text_x, text_y);
                cr->set_source_rgb(fg.r / 255.0, fg.g / 255.0, fg.b / 255.0);
                layout->show_in_cairo_context(cr);
            }
    
            // Render Cursor (assuming `cursor_visible_` is managed by GtkEditor)
            bool cursor_visible_ = cursor_visible_state;
            bool is_active_window = (window == active_window_cache);
            if (is_active_window && cursor_visible_ && buffer_line_idx == cursor.line) {
                 int cursor_idx = static_cast<int>(cursor.column) - horizontal_offset;
                 
                 spdlog::debug("Cursor debug: Logical Curs ({},{}) | H-Offset {} | VisTextLen {} | CursorIdx {}",
                               cursor.line, cursor.column, horizontal_offset, visible_text.length(), cursor_idx);

                 Pango::Rectangle pos;
                 if (cursor_idx < 0) {
                     // Out of view
                     spdlog::debug("Cursor debug: Cursor out of view (left)");
                     continue; // Don't draw if out of view
                 } else if (cursor_idx > static_cast<int>(visible_text.length())) {
                      // Past end of line (after last character)
                      pos = layout->index_to_pos(visible_text.length()); 
                      int diff = cursor_idx - static_cast<int>(visible_text.length());
                      if (diff > 0) {
                           pos.set_x(pos.get_x() + diff * char_width_ * PANGO_SCALE);
                      }
                      spdlog::debug("Cursor debug: Past end of line. PangoX={}, FinalPangoX={}", layout->index_to_pos(visible_text.length()).get_x(), pos.get_x());

                 } else {
                     pos = layout->index_to_pos(cursor_idx);
                     spdlog::debug("Cursor debug: Within line. PangoX={}", pos.get_x());
                 }
    
                 double cursor_screen_x = PADDING_LEFT + (pos.get_x() / (double)PANGO_SCALE);
                 
                 spdlog::debug("Cursor debug: ScreenX={}, ScreenY={} | CharWidth={}, LineHeight={}",
                               cursor_screen_x, text_y, char_width_, line_height_);

                 if (cursor_screen_x >= PADDING_LEFT && cursor_screen_x < (width - PADDING_RIGHT)) {
                     // Determine cursor width
                     double cur_width = char_width_;
                     if (cursor_idx < static_cast<int>(visible_text.length())) {
                          Pango::Rectangle next_pos;
                          next_pos = layout->index_to_pos(cursor_idx + 1);
                          cur_width = (next_pos.get_x() - pos.get_x()) / (double)PANGO_SCALE;
                     }
                     
                     // Draw Cursor Block
                     Color cursor_bg = fg;
                     cr->set_source_rgb(cursor_bg.r / 255.0, cursor_bg.g / 255.0, cursor_bg.b / 255.0);
                     cr->rectangle(cursor_screen_x, text_y, cur_width, line_height_);
                     cr->fill();
                     
                     // Draw Character Inverted
                     if (cursor_idx < static_cast<int>(visible_text.length())) {
                         char cursor_char = visible_text[cursor_idx];
                         cr->set_source_rgb(bg.r / 255.0, bg.g / 255.0, bg.b / 255.0);
                         
                         auto cursor_layout = widget->create_pango_layout(std::string(1, cursor_char));
                         cursor_layout->set_font_description(font_desc_);
                         
                         cr->move_to(cursor_screen_x, text_y);
                         cursor_layout->show_in_cairo_context(cr);
                     }
                 }
            }

            // Advance to next line
            buffer_line_idx++;
            current_screen_row += line_screen_rows;
        }

} // Closing brace for GtkRenderer::draw_window method


void GtkRenderer::render_modeline_for_window(const Cairo::RefPtr<Cairo::Context>& cr, int width, int height,
                                           std::shared_ptr<Window> window,
                                           std::shared_ptr<Window> active_window_cache) {
    if (!core_.theme_manager().active_theme() || !window) return;

    // The logic for is_active needs to be passed from GtkEditor
    bool is_active = (window == active_window_cache);

    // Calculate modeline position (bottom of the drawing area)
    // Minibuffer is separate, so modeline is at the very bottom
    // Use pixel-aligned coordinates to avoid blurry text
    double modeline_y = std::round(height - line_height_ - PADDING_BOTTOM);
    double modeline_x = std::round(PADDING_LEFT);

    // Get theme colors
    auto theme = core_.theme_manager().active_theme();
    ThemeElement element = is_active ? ThemeElement::StatusLine : ThemeElement::StatusLineInactive;

    Color bg = theme->get_bg_color(element);
    Color fg = theme->get_fg_color(element);

    // Draw modeline background
    cr->set_source_rgb(bg.r / 255.0, bg.g / 255.0, bg.b / 255.0);
    cr->rectangle(0, modeline_y, width, line_height_);
    cr->fill();

    // Build modeline content using ModelineManager
    auto content = core_.modeline_manager().generate_content(window, is_active);

    double x_offset = modeline_x;
    for (const auto& chunk : content) {
        Color chunk_fg = fg;

        // Resolve chunk face if needed
        if (!chunk.face_name.empty()) {
             if (auto face = theme->get_face(chunk.face_name)) {
                 if (face->foreground) chunk_fg = *face->foreground;
             }
        }

        // Use a temporary layout for modeline chunk
        auto layout = Pango::Layout::create(context_widget_.get_pango_context());
        layout->set_font_description(font_desc_);
        layout->set_text(chunk.text);

        // Apply attributes
        Pango::AttrList attr_list;
        if (!chunk.face_name.empty()) {
             if (auto face = theme->get_face(chunk.face_name)) {
                 apply_face_attributes(attr_list, *face, 0, static_cast<int>(chunk.text.length()));
             }
        }
        layout->set_attributes(attr_list);

        cr->set_source_rgb(chunk_fg.r / 255.0, chunk_fg.g / 255.0, chunk_fg.b / 255.0);
        cr->move_to(x_offset, modeline_y);
        layout->show_in_cairo_context(cr);

        // Advance - keep pixel aligned
        int w, h;
        layout->get_pixel_size(w, h);
        x_offset = std::round(x_offset + w);
    }
}


void GtkRenderer::render_minibuffer(const Cairo::RefPtr<Cairo::Context>& cr, int width, int height) {
    if (!core_.theme_manager().active_theme()) return;

    // Get theme colors
    auto theme = core_.theme_manager().active_theme();
    Color bg = theme->get_bg_color(ThemeElement::Background);
    Color fg = theme->get_fg_color(ThemeElement::Normal);
    
    // ALWAYS draw minibuffer background to prevent it from disappearing
    // Clear the ENTIRE surface first to avoid artifacts
    cr->set_source_rgb(bg.r / 255.0 * 0.9, bg.g / 255.0 * 0.9, bg.b / 255.0 * 0.9);
    cr->rectangle(0, 0, width, height); // Clear full dedicated area
    cr->fill();
    
    // Draw separator line above minibuffer
    cr->set_source_rgb(fg.r / 255.0 * 0.5, fg.g / 255.0 * 0.5, fg.b / 255.0 * 0.5);
    cr->set_line_width(1.0);
    cr->move_to(0, 0);
    cr->line_to(width, 0);
    cr->stroke();

    // Check if we're building a key sequence (show partial keys like "C-x-")
    bool showing_key_sequence = false;
    std::string key_sequence_display;
    if (core_.keybinding_manager().is_building_sequence()) {
        key_sequence_display = core_.keybinding_manager().current_sequence_display();
        showing_key_sequence = true;
    }

    // Get extended echo area lines (for packages like which-key)
    const auto& echo_area_lines = core_.echo_area_lines();
    bool showing_echo_lines = !echo_area_lines.empty();

    // Only render text if minibuffer is active, a message is set, showing key sequence, or echo area lines
    if (!core_.minibuffer_manager().is_active() && core_.last_message().empty() &&
        !showing_key_sequence && !showing_echo_lines) {
        return;
    }

    // If showing extended echo area lines, render them first (above the main minibuffer line)
    if (showing_echo_lines) {
        double hint_y = std::round(PADDING_TOP);
        cr->set_source_rgb(fg.r / 255.0, fg.g / 255.0, fg.b / 255.0);

        for (const auto& line : echo_area_lines) {
            auto layout = Pango::Layout::create(context_widget_.get_pango_context());
            layout->set_font_description(font_desc_);
            layout->set_text(line);
            cr->move_to(std::round(PADDING_LEFT), hint_y);
            layout->show_in_cairo_context(cr);
            hint_y = std::round(hint_y + line_height_);
        }
    }

    // Calculate minibuffer position (bottom line with padding)
    // Use pixel-aligned coordinates to avoid blurry text
    double minibuffer_y = std::round(height - line_height_ - PADDING_BOTTOM);
    double minibuffer_x = std::round(PADDING_LEFT);

    // Prepare minibuffer text
    std::string minibuffer_text;
    std::string prompt_part;
    std::string input_part;

    if (core_.minibuffer_manager().is_active()) {
        prompt_part = core_.minibuffer_manager().get_prompt();
        input_part = core_.minibuffer_manager().get_input_buffer();
        minibuffer_text = prompt_part + input_part;
    } else if (showing_key_sequence) {
        // Show partial key sequence (like Emacs "C-x-")
        minibuffer_text = key_sequence_display;
    } else if (!core_.last_message().empty()) {
        minibuffer_text = core_.last_message();
    }

    // Render minibuffer text
    if (!minibuffer_text.empty()) {
        // Set color based on message severity (only for messages, not minibuffer input)
        bool is_message_mode = !core_.minibuffer_manager().is_active() && !showing_key_sequence;
        if (is_message_mode) {
            switch (core_.last_message_severity()) {
                case MessageSeverity::Warning:
                    cr->set_source_rgb(1.0, 0.8, 0.0);  // Yellow/Orange for warnings
                    break;
                case MessageSeverity::Error:
                    cr->set_source_rgb(1.0, 0.3, 0.3);  // Red for errors
                    break;
                default:
                    cr->set_source_rgb(fg.r / 255.0, fg.g / 255.0, fg.b / 255.0);
                    break;
            }

            // Handle multi-line messages: collapse to first line with indicator
            minibuffer_text = MessageFormatter::collapse_multiline(minibuffer_text);
        } else {
            cr->set_source_rgb(fg.r / 255.0, fg.g / 255.0, fg.b / 255.0);
        }

        auto layout = Pango::Layout::create(context_widget_.get_pango_context());
        layout->set_font_description(font_desc_);

        // Check if message needs truncation (only for non-active minibuffer)
        double available_width = width - PADDING_LEFT - PADDING_RIGHT;

        if (is_message_mode) {
            // Measure full text width
            layout->set_text(minibuffer_text);
            int text_width, text_height;
            layout->get_pixel_size(text_width, text_height);

            // Truncate if too wide
            if (text_width > available_width) {
                const std::string suffix = "... [C-h e]";
                // Binary search for max chars that fit
                size_t lo = 0, hi = minibuffer_text.length();
                while (lo < hi) {
                    size_t mid = lo + (hi - lo + 1) / 2;
                    layout->set_text(minibuffer_text.substr(0, mid) + suffix);
                    layout->get_pixel_size(text_width, text_height);
                    if (text_width <= available_width) {
                        lo = mid;
                    } else {
                        hi = mid - 1;
                    }
                }
                minibuffer_text = minibuffer_text.substr(0, lo) + suffix;
            }
        }

        layout->set_text(minibuffer_text);

        // Apply face attributes for prompt if active
        if (core_.minibuffer_manager().is_active()) {
            Pango::AttrList attr_list;
            if (auto face = theme->get_face("minibuffer-prompt")) {
                apply_face_attributes(attr_list, *face, 0, static_cast<int>(prompt_part.length()));
            }
            layout->set_attributes(attr_list);
        }

        cr->move_to(minibuffer_x, minibuffer_y);
        layout->show_in_cairo_context(cr);
    }
    
    // Render minibuffer cursor if active and visible
    if (core_.minibuffer_manager().is_active()) {
        // Calculate cursor position in minibuffer using proper UTF-8 byte indexing
        // Cursor position from minibuffer is in characters, we need byte index for Pango
        size_t cursor_char_pos = core_.minibuffer_manager().get_cursor_position();

        // Convert character positions to byte indices for UTF-8 strings
        // prompt_part byte length + byte offset within input_part for cursor position
        size_t prompt_byte_len = prompt_part.length(); // Prompt is typically ASCII, bytes == chars
        size_t cursor_byte_idx = prompt_byte_len + char_to_byte_index(input_part, cursor_char_pos);

        auto layout = Pango::Layout::create(context_widget_.get_pango_context());
        layout->set_font_description(font_desc_);
        layout->set_text(minibuffer_text);

        Pango::Rectangle pos = layout->index_to_pos(static_cast<int>(cursor_byte_idx));
        double cursor_screen_x = std::round(minibuffer_x + (pos.get_x() / static_cast<double>(PANGO_SCALE)));

        // Draw Cursor Block - measure actual char width using Pango
        double cur_width = char_width_;
        size_t text_byte_len = minibuffer_text.length();
        if (cursor_byte_idx < text_byte_len) {
            // Get next character's byte index for width calculation
            Glib::ustring utext(minibuffer_text);
            size_t total_char_pos = Glib::ustring(prompt_part).length() + cursor_char_pos;
            if (total_char_pos < utext.length()) {
                size_t next_byte_idx = char_to_byte_index(minibuffer_text, total_char_pos + 1);
                Pango::Rectangle next_pos = layout->index_to_pos(static_cast<int>(next_byte_idx));
                cur_width = (next_pos.get_x() - pos.get_x()) / static_cast<double>(PANGO_SCALE);
            }
        }

        cr->set_source_rgb(fg.r / 255.0, fg.g / 255.0, fg.b / 255.0);
        cr->rectangle(cursor_screen_x, minibuffer_y, cur_width, line_height_);
        cr->fill();

        // Draw Character Inverted (if not at end)
        if (cursor_byte_idx < text_byte_len) {
            // Extract the UTF-8 character at cursor position using Glib::ustring
            Glib::ustring utext(minibuffer_text);
            size_t total_char_pos = Glib::ustring(prompt_part).length() + cursor_char_pos;
            if (total_char_pos < utext.length()) {
                // Get single character as string
                Glib::ustring cursor_char_str = utext.substr(total_char_pos, 1);

                cr->set_source_rgb(bg.r / 255.0, bg.g / 255.0, bg.b / 255.0);
                auto cursor_layout = context_widget_.create_pango_layout(cursor_char_str);
                cursor_layout->set_font_description(font_desc_);
                cr->move_to(cursor_screen_x, minibuffer_y);
                cursor_layout->show_in_cairo_context(cr);
            }
        }

        // Render completion overlay if applicable
        auto current_completion = core_.minibuffer_manager().get_current_completion();
        if (current_completion && input_part != *current_completion) {
            // Use character-based length comparison for proper UTF-8 handling
            Glib::ustring u_input(input_part);
            Glib::ustring u_completion(*current_completion);
            if (u_input.length() <= u_completion.length()) {
                Glib::ustring completion_suffix = u_completion.substr(u_input.length());
                if (!completion_suffix.empty()) {
                    // Calculate position of the end of input using byte index
                    size_t input_end_byte_idx = prompt_byte_len + input_part.length();
                    Pango::Rectangle end_pos = layout->index_to_pos(static_cast<int>(input_end_byte_idx));
                    double completion_x = std::round(minibuffer_x + (end_pos.get_x() / static_cast<double>(PANGO_SCALE)));

                    auto completion_layout = Pango::Layout::create(context_widget_.get_pango_context());
                    completion_layout->set_font_description(font_desc_);
                    completion_layout->set_text(completion_suffix);

                    Pango::AttrList completion_attr_list;
                    std::string suffix_str = completion_suffix;
                    if (auto face = theme->get_face("minibuffer-completion")) {
                        apply_face_attributes(completion_attr_list, *face, 0, static_cast<int>(suffix_str.length()));
                    } else {
                        // Fallback: dimmed foreground
                        Color dim_fg = fg;
                        dim_fg.r = static_cast<unsigned char>(dim_fg.r * 0.7);
                        dim_fg.g = static_cast<unsigned char>(dim_fg.g * 0.7);
                        dim_fg.b = static_cast<unsigned char>(dim_fg.b * 0.7);
                        auto attr = Pango::Attribute::create_attr_foreground(dim_fg.r * 257, dim_fg.g * 257, dim_fg.b * 257);
                        attr.set_start_index(0);
                        attr.set_end_index(static_cast<int>(suffix_str.length()));
                        completion_attr_list.insert(attr);
                    }
                    completion_layout->set_attributes(completion_attr_list);

                    cr->set_source_rgb(fg.r / 255.0, fg.g / 255.0, fg.b / 255.0);
                    cr->move_to(completion_x, minibuffer_y);
                    completion_layout->show_in_cairo_context(cr);
                }
            }
        }
    }
}

bool GtkRenderer::get_minibuffer_coords(int& x, int& y, int& width, int& height) const {
    // These coordinates are relative to the main drawing area.
    // The minibuffer is always at the bottom of the main drawing area.
    width = context_widget_.get_width();
    height = static_cast<int>(line_height_ + PADDING_TOP + PADDING_BOTTOM); // Approximate minibuffer height
    x = 0; // Starts at the left edge
    y = context_widget_.get_height() - height; // Position from the bottom

    return true;
}

void GtkRenderer::invalidate_cache() {
    render_cache_.clear();
    spdlog::debug("GtkRenderer: Cache invalidated due to theme change.");
}

void GtkRenderer::set_font_size(int size) {
    if (size < 6) size = 6;    // Minimum readable size
    if (size > 72) size = 72;  // Maximum reasonable size

    font_size_ = size;
    core_.config().set("font_size", size);

    // Reinitialize font metrics
    initialize_font_metrics();
    invalidate_cache();

    spdlog::debug("GtkRenderer: Font size changed to {}", size);
}

} // namespace lumacs
