#include "lumacs/gtk_renderer.hpp"
#include "lumacs/editor_core.hpp"
#include "lumacs/minibuffer_manager.hpp" // For MinibufferManager and MinibufferMode
#include <algorithm> // For std::max, std::min
#include <spdlog/spdlog.h> // Added for debug logging

namespace lumacs {

GtkRenderer::GtkRenderer(EditorCore& core, Gtk::DrawingArea& main_drawing_area)
    : core_(core), main_drawing_area_(main_drawing_area) {
    initialize_font_metrics(); // Initialize font metrics once during construction
}

void GtkRenderer::initialize_font_metrics() {
    if (font_initialized_) return;

    // Use a minimal string for layout creation
    auto layout = main_drawing_area_.create_pango_layout(" ");
    font_desc_ = Pango::FontDescription("Monospace 12");
    layout->set_font_description(font_desc_);
    
    Pango::FontMetrics metrics = layout->get_context()->get_metrics(font_desc_);
    line_height_ = (double)metrics.get_height() / PANGO_SCALE;
    ascent_ = (double)metrics.get_ascent() / PANGO_SCALE;
    
    // 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_ = (double)(pos1.get_x() - pos0.get_x()) / PANGO_SCALE;

    spdlog::debug("GtkRenderer font metrics: line_height={}, char_width={}", line_height_, char_width_);
    
    font_initialized_ = true;
}

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, &main_drawing_area_, active_window_cache, cursor_visible_state);

    // Use a temporary layout for modeline/minibuffer as they are dynamic
    auto temp_layout = Pango::Layout::create(main_drawing_area_.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::DrawingArea* 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);

    int visible_lines = static_cast<int>(content_height_px / line_height_);
    int visible_cols = static_cast<int>(content_width_px / char_width_);

    // Reserve space for modeline (1 line). Minibuffer is global and rendered separately.
    bool is_main_window = (window == active_window_cache);
    int editor_lines = is_main_window ? std::max(0, visible_lines - 2) : std::max(0, visible_lines - 1);
    window->set_viewport_size(visible_cols, editor_lines);

    // 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 = window->viewport().horizontal_offset;
        
        auto& window_cache = render_cache_[window.get()];
    
        for (int screen_y = 0; screen_y < editor_lines && start_line + screen_y < end_line; ++screen_y) {
            size_t buffer_line_idx = start_line + screen_y;
            if (buffer_line_idx >= buffer.line_count()) break; // Safety check
            const auto& line_text = buffer.line(buffer_line_idx);
            const auto& styles = buffer.get_line_styles(buffer_line_idx);
    
            // Apply horizontal scrolling
            std::string visible_text;
            if (horizontal_offset < static_cast<int>(line_text.length())) {
                visible_text = line_text.substr(horizontal_offset);
            }
            
            // Always set text for the layout before any further operations, especially cursor positioning
            layout->set_text(visible_text);
            
            double text_x = PADDING_LEFT;
            double text_y = PADDING_TOP + screen_y * 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];
                 if (entry.text_content == visible_text) { // Simple validation
                     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) {
                    // Render to Cache surface first
                                int surf_width = std::max(1, static_cast<int>(content_width_px));
                                
                                auto surface = Cairo::ImageSurface::create(Cairo::Surface::Format::ARGB32, surf_width, static_cast<int>(line_height_));                    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
                    LineCacheEntry entry;
                    entry.text_content = visible_text;
                    entry.surface = surface;
                    entry.styles = styles;
                    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)
                    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);
                     }
                 }
            }
        }    
    // Refined Loop with Selection Logic:
    // (This replaces the loop in the file)
    
    // Since I cannot easily rewrite the whole loop with complex logic insertion in one go without potentially breaking,
    // I will rewrite the loop logic completely in the replacement string.


} // 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 (second line from bottom)
    double modeline_y = height - (2 * line_height_) - PADDING_BOTTOM; // Position above minibuffer
    double modeline_x = 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(main_drawing_area_.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
        int w, h;
        layout->get_pixel_size(w, h);
        x_offset += w;
    }
}


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

    // Only render if minibuffer is active or a message is set
    if (!core_.minibuffer_manager().is_active() && core_.last_message().empty()) {
        return;
    }
    
    // Calculate minibuffer position (bottom line with padding)
    double minibuffer_y = height - line_height_ - PADDING_BOTTOM;
    double minibuffer_x = PADDING_LEFT;
    
    // 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);
    
    // Draw minibuffer background (slightly different shade)
    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, minibuffer_y - 2, width, line_height_ + 4);
    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, minibuffer_y - 2);
    cr->line_to(width, minibuffer_y - 2);
    cr->stroke();
    
    // 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 (!core_.last_message().empty()) {
        minibuffer_text = core_.last_message();
    }
    
    // Render minibuffer text
    if (!minibuffer_text.empty()) {
        cr->set_source_rgb(fg.r / 255.0, fg.g / 255.0, fg.b / 255.0);
        auto layout = Pango::Layout::create(main_drawing_area_.get_pango_context());
        layout->set_font_description(font_desc_);
        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 (assuming cursor_visible is managed by GtkEditor)
    bool cursor_visible_ = true; // Placeholder
    if (core_.minibuffer_manager().is_active() && cursor_visible_) {
        // Calculate cursor position in minibuffer
        auto layout = Pango::Layout::create(main_drawing_area_.get_pango_context());
        layout->set_font_description(font_desc_);
        layout->set_text(minibuffer_text); // Measure full text
        Pango::Rectangle ink_rect, logical_rect;
        layout->get_pixel_extents(ink_rect, logical_rect);
        double cursor_x = minibuffer_x + logical_rect.get_width();
        
        // Draw minibuffer cursor
        cr->set_source_rgb(fg.r / 255.0, fg.g / 255.0, fg.b / 255.0);
        cr->rectangle(cursor_x, minibuffer_y, 2.0, line_height_);
        cr->fill();

        // Render completion overlay if applicable
        auto current_completion = core_.minibuffer_manager().get_current_completion();
        if (current_completion && input_part != *current_completion) {
            std::string completion_suffix = current_completion->substr(input_part.length());
            if (!completion_suffix.empty()) {
                auto completion_layout = Pango::Layout::create(cr);
                completion_layout->set_font_description(font_desc_);
                completion_layout->set_text(minibuffer_text + completion_suffix);
                
                Pango::AttrList completion_attr_list;
                if (auto face = theme->get_face("minibuffer-completion")) { // Assuming a face for completion
                    apply_face_attributes(completion_attr_list, *face, static_cast<int>(minibuffer_text.length()), static_cast<int>(completion_suffix.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(static_cast<int>(minibuffer_text.length()));
                    attr.set_end_index(static_cast<int>(minibuffer_text.length() + completion_suffix.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(minibuffer_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 = main_drawing_area_.get_width();
    height = static_cast<int>(line_height_ + PADDING_TOP + PADDING_BOTTOM); // Approximate minibuffer height
    x = 0; // Starts at the left edge
    y = main_drawing_area_.get_height() - height; // Position from the bottom

    return true;
}

} // namespace lumacs
