#include "lumacs/tui_editor.hpp"
#include "lumacs/editor_core.hpp"
#include "lumacs/lua_api.hpp"
#include <ncurses.h>
#include <iostream>
#include <fstream>
#include <memory>
#include <chrono>
#include <string>
#include <sstream>
#include <algorithm>

// Global debug log
extern std::ofstream debug_log;
std::ofstream debug_log("lumacs_debug.log");

using namespace lumacs;

/// ncurses-based TUI frontend for Lumacs
class TuiEditor : public IEditorView {
public:
    TuiEditor() : core_(nullptr) {}
    
    ~TuiEditor() override {
        endwin(); // Cleanup ncurses
    }

    // IEditorView methods
    void init() override;
    void run() override;
    void handle_editor_event(EditorEvent event) override;
    void set_core(EditorCore* core) override;

private:
    EditorCore* core_ = nullptr; // Raw pointer to EditorCore, not owned
    bool should_quit_ = false;
    std::string message_line_;
    int height_ = 0, width_ = 0;
    
    // For cursor blinking logic
    bool cursor_visible_ = true;
    std::chrono::steady_clock::time_point last_cursor_move_time_ = std::chrono::steady_clock::now();
    static constexpr std::chrono::milliseconds BLINK_INTERVAL = std::chrono::milliseconds(500);
    static constexpr std::chrono::milliseconds BLINK_STATIONARY_THRESHOLD = std::chrono::milliseconds(1000); // 1 second
    
    // Private helper method declarations
    std::string resolve_key(int ch);
    bool handle_input(int ch);
    void render();
    void render_layout_node(std::shared_ptr<LayoutNode> node, int x, int y, int width, int height);
    void render_window(std::shared_ptr<Window> window, int x, int y, int width, int height);
    void render_window_modeline(std::shared_ptr<Window> window, int x, int y, int width, bool is_active);
    void render_message_line();
    
    int get_attributes_for_face(const std::string& face_name);
};

// --- TuiEditor Public Method Definitions ---

void TuiEditor::init() {
    // Initialize ncurses
    initscr();
    cbreak();           // Disable line buffering
    noecho();           // Don't echo pressed keys
    keypad(stdscr, TRUE); // Enable special keys
    raw();              // Enable all control characters
    timeout(50);        // Set 50ms timeout for getch() to avoid blocking forever
    
    // Color support
    if (has_colors()) {
        start_color();
        use_default_colors();
    }
    
    // Get screen dimensions
    getmaxyx(stdscr, height_, width_);
    
    // Initialize theme colors for ncurses
    if (has_colors() && core_->theme_manager().active_theme()) {
        core_->theme_manager().active_theme()->initialize_ncurses_colors();
    }
    
    // Set initial viewport size (leave room for minibuffer/message line)
    int minibuffer_lines = 1; // Always reserve 1 line for minibuffer/message
    int content_height = height_ - minibuffer_lines;
    bool show_line_numbers = core_->config().get<bool>("show_line_numbers", true);
    int line_number_width = show_line_numbers ? core_->config().get<int>("line_number_width", 6) : 0;
    int content_width = width_ - line_number_width;
    core_->set_viewport_size(content_width, content_height);
    
    debug_log << "ncurses editor initialized: " << width_ << "x" << height_ << std::endl;
}

void TuiEditor::run() {
    should_quit_ = false;
    
    // Initial render
    render();
    
    std::chrono::steady_clock::time_point last_blink_toggle_time = std::chrono::steady_clock::now();

    while (!should_quit_) {
        // Handle screen resize
        int new_height, new_width;
        getmaxyx(stdscr, new_height, new_width);
        if (new_height != height_ || new_width != width_) {
            height_ = new_height;
            width_ = new_width;
            int minibuffer_lines = 1; // Always reserve 1 line for minibuffer/message
            int content_height = height_ - minibuffer_lines;
            bool show_line_numbers = core_->config().get<bool>("show_line_numbers", true);
            int line_number_width = show_line_numbers ? core_->config().get<int>("line_number_width", 6) : 0;
            int content_width = width_ - line_number_width;
            core_->set_viewport_size(content_width, content_height);
            debug_log << "Screen resized to: " << width_ << "x" << height_ << std::endl;
            debug_log << "Content area: " << content_width << "x" << content_height << std::endl;
            // Force cursor to be visible after resize, as it implies movement.
            last_cursor_move_time_ = std::chrono::steady_clock::now();
            cursor_visible_ = true;
            render();  // Re-render after resize
        }
        
        // Get input (with timeout)
        int ch = getch();
        
        // Only process input and render if we got actual input (not timeout)
        if (ch != ERR) {
            last_cursor_move_time_ = std::chrono::steady_clock::now();
            cursor_visible_ = true; // Ensure cursor is visible on input
            handle_input(ch);
            render();
            last_blink_toggle_time = std::chrono::steady_clock::now(); // Reset blink timer after input
        } else { // No input (timeout occurred)
            auto now = std::chrono::steady_clock::now();
            if (now - last_cursor_move_time_ > BLINK_STATIONARY_THRESHOLD) {
                // If stationary for long enough, start/continue blinking
                if (now - last_blink_toggle_time > BLINK_INTERVAL) {
                    cursor_visible_ = !cursor_visible_;
                    core_->check_and_clear_message(); // Check and clear messages (like for GtkEditor's blink)
                    render(); // Re-render to show/hide cursor
                    last_blink_toggle_time = now;
                }
            } else {
                // Still "moving" recently, keep cursor visible.
                // If it was just hidden by a blink, make it visible again.
                if (!cursor_visible_) {
                    cursor_visible_ = true;
                    render(); // Make sure it's visible
                }
            }
        }
    }
}

void TuiEditor::handle_editor_event(EditorEvent event) {
    if (event == EditorEvent::Quit) {
        should_quit_ = true;
    } else if (event == EditorEvent::Message) {
        message_line_ = core_->last_message(); // Still update local message_line_ for rendering
    } else if (event == EditorEvent::CursorMoved) {
        last_cursor_move_time_ = std::chrono::steady_clock::now();
        cursor_visible_ = true;
        render(); // Ensure immediate redraw to show cursor at new position
    } else if (event == EditorEvent::TransientMessageCleared) {
        // Redraw to clear the message from the screen
        render();
    } else if (event == EditorEvent::CommandMode) {
        core_->minibuffer_manager().activate_minibuffer(
            MinibufferMode::Command, ":",
            [this](const std::string& input) {
                if (input == "q" || input == "quit") {
                    should_quit_ = true;
                } else {
                    auto result = core_->minibuffer_manager().parse_and_execute_command_string(input);
                    core_->set_message(result.message);
                }
            }, // Added comma here
            [this]() { core_->set_message("Cancelled"); }
        );
    } else if (event == EditorEvent::BufferSwitchMode) {
        core_->minibuffer_manager().activate_minibuffer(
            MinibufferMode::BufferName, "Switch to buffer: ",
            [this](const std::string& input) {
                if (core_->switch_buffer_in_window(input)) {
                    core_->set_message("Switched to: " + input);
                    core_->lua_api()->execute("auto_activate_major_mode()");
                } else {
                    core_->set_message("Buffer not found: " + input);
                }
            },
            [this]() { core_->set_message("Cancelled"); }
        );
    } else if (event == EditorEvent::KillBufferMode) {
        core_->minibuffer_manager().activate_minibuffer(
            MinibufferMode::BufferName, "Kill buffer: ",
            [this](const std::string& input) {
                // MinibufferManager should handle confirmation for modified buffers
                if (core_->close_buffer(input)) {
                    core_->set_message("Closed buffer: " + input);
                } else {
                    core_->set_message("Failed to close buffer: " + input);
                }
            },
            [this]() { core_->set_message("Cancelled"); }
        );
    } else if (event == EditorEvent::FindFileMode) {
        core_->minibuffer_manager().activate_minibuffer(
            MinibufferMode::FilePath, "Find file: ",
            [this](const std::string& input) {
                if (core_->load_file(input)) {
                    core_->set_message("Loaded: " + input);
                    core_->lua_api()->execute("auto_activate_major_mode()");
                } else {
                    core_->set_message("Failed to load: " + input);
                }
            },
            [this]() { core_->set_message("Cancelled"); }
        );
    } 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);
                }
            }, // Added comma here
            [this]() { core_->set_message("Cancelled"); }
        );
    } 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 query: " + input); },
            [this]() { core_->set_message("Cancelled I-search"); }
        );
    } else if (event == EditorEvent::ISearchBackwardMode) {
        core_->minibuffer_manager().activate_minibuffer(
            MinibufferMode::ISearch, "I-search backward: ",
            [this](const std::string& input) { /* TODO: Implement actual isearch logic */ core_->set_message("I-search backward query: " + input); },
            [this]() { core_->set_message("Cancelled I-search backward"); }
        );
    } else if (event == EditorEvent::TransientMessageCleared) {
        // Redraw to clear the message from the screen
        render();
    }
}

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

// --- TuiEditor Private Helper Method Definitions ---

int TuiEditor::get_attributes_for_face(const std::string& face_name) {
    auto theme = core_->theme_manager().active_theme();
    if (theme) {
        return theme->get_face_attributes_ncurses(face_name);
    }
    return 0; // A_NORMAL
}

/// Convert ncurses key code to our key name format
std::string TuiEditor::resolve_key(int ch) {
    debug_log << "=== NCURSES INPUT DEBUG ===" << std::endl;
    debug_log << "Raw key code: " << ch << " (0x" << std::hex << ch << std::dec << ")" << std::endl;
    
    if (ch == 27) return "Escape";
    if (ch == '\n' || ch == '\r' || ch == KEY_ENTER) return "Return";
    if (ch == '\t') return "Tab";
    if (ch == KEY_BACKSPACE || ch == 127 || ch == '\b') return "Backspace";
    if (ch == KEY_UP) return "ArrowUp";
    if (ch == KEY_DOWN) return "ArrowDown";
    if (ch == KEY_LEFT) return "ArrowLeft";
    if (ch == KEY_RIGHT) return "ArrowRight";
    if (ch == KEY_HOME) return "Home";
    if (ch == KEY_END) return "End";
    if (ch == KEY_PPAGE) return "PageUp";
    if (ch == KEY_NPAGE) return "PageDown";
    if (ch == ' ') return "Space";
    
    // Control keys
    if (ch > 0 && ch < 32) {
        return "C-" + std::string(1, (char)('a' + ch - 1));
    }

    // Regular characters
    if (ch >= 32 && ch < 127) {
        return std::string(1, (char)ch);
    }

    return "";
}

bool TuiEditor::handle_input(int ch) {
    // Resolve key name
    std::string key_name = resolve_key(ch);
    
    if (key_name.empty()) {
        debug_log << "Empty key name, ignoring input" << std::endl;
        return false;
    }

    // Handle Minibuffer Input Logic first
    if (core_->minibuffer_manager().is_active()) {
        return core_->minibuffer_manager().handle_key_event(key_name);
    }

    // Normal mode processing (pass to keybinding system)
    KeyProcessingResult result = core_->keybinding_manager().process_key(Key::parse(key_name));

    if (result.command_result.has_value()) {
        core_->set_message(result.command_result->message);
    }
    
    if (result.type == KeyResult::Unbound) {
        // Fallback: Self-insert for printable characters
        // Check if key is a single character and not a control sequence
        // The resolve_key function returns "C-x", "M-x", "Esc", "Return", etc.
        // Printable characters are returned as single chars "a", "1", etc.
        if (key_name.length() == 1) {
            // We can assume it's printable if length is 1 and it's not a special key (which resolve_key handles)
            // Actually, resolve_key might return special single chars? No, most are named.
            // But let's be safe.
            core_->command_system().execute("self-insert-command", {key_name});
            
            // --- Macro Recording Logic for Self-Insert ---
            if (core_->is_recording_macro()) {
                core_->record_key_sequence(key_name);
            }
            // --------------------------------------------

            return true;
        }
    }

    return result.type != KeyResult::Unbound;
}

// process_key is removed as keybinding_manager handles it



void TuiEditor::render() {
    // Clear and update screen info
    getmaxyx(stdscr, height_, width_);
    
    // Set background color from theme
    auto theme = core_->theme_manager().active_theme();
    if (theme) {
        int bg_color_pair = theme->get_color_pair(ThemeElement::Background);
        bkgd(bg_color_pair);
    }
    clear();
    
    // Calculate content area (leave room for message line and potentially a completion line)
    int minibuffer_lines = 1; // Always reserve 1 line for minibuffer/message
    // if (core_->minibuffer_manager().is_active() && !core_->minibuffer_manager().get_completion_candidates().empty()) {
    //     minibuffer_lines++; // Reserve an extra line for completions
    // }
    int content_height = height_ - minibuffer_lines; 
    int content_width = width_;
    
    // Render the layout tree recursively (now includes per-window modelines)
    render_layout_node(core_->root_layout(), 0, 0, content_width, content_height);
    
    // Global message/command line (last line)
    render_message_line();
    
    // Refresh screen
    refresh();
}

void TuiEditor::render_layout_node(std::shared_ptr<LayoutNode> node, int x, int y, int width, int height) {
    if (!node) return;
    
    if (node->type == LayoutNode::Type::Leaf) {
        // Render a single window
        render_window(node->window, x, y, width, height);
    } else if (node->type == LayoutNode::Type::HorizontalSplit) {
        // Split horizontally: top and bottom windows
        int top_height = height / 2;
        int bottom_height = height - top_height;
        
        render_layout_node(node->child1, x, y, width, top_height);
        render_layout_node(node->child2, x, y + top_height, width, bottom_height);
    } else if (node->type == LayoutNode::Type::VerticalSplit) {
        // Split vertically: left and right windows  
        int separator_width = (width > 2) ? 1 : 0;
        int available_width = width - separator_width;

        int left_width = available_width / 2;
        int right_width = available_width - left_width;
        
        render_layout_node(node->child1, x, y, left_width, height);

        // Draw separator if enabled
        if (separator_width > 0) {
            int attrs = get_attributes_for_face("window-divider");
            attron(attrs);
            
            int sep_x = x + left_width;
            for (int i = 0; i < height; ++i) {
                mvaddch(y + i, sep_x, ACS_VLINE);
            }
            
            attroff(attrs);
        }

        render_layout_node(node->child2, x + left_width + separator_width, y, right_width, height);
    }
}

void TuiEditor::render_window(std::shared_ptr<Window> window, int x, int y, int width, int height) {
    if (!window) return;
    
    // Check configuration for line numbers and modeline
    bool show_line_numbers = core_->config().get<bool>("show_line_numbers", true);
    bool show_modeline = core_->config().get<bool>("show_modeline", true);
    int line_number_width = show_line_numbers ? core_->config().get<int>("line_number_width", 6) : 0;
    int modeline_height = show_modeline ? 1 : 0;
    
    // Update window viewport size (reserve space for modeline)
    int content_width = width - line_number_width;
    int content_height = height - modeline_height;
    window->set_viewport_size(content_width, content_height);
    
    // Get window data
    const auto& buffer = window->buffer();
    const auto cursor = window->cursor();
    auto [start_line, end_line] = window->visible_line_range();
    bool is_active = (window == core_->active_window());
    
    debug_log << "Render window at " << x << "," << y << " size " << width << "x" << height 
              << " viewport=" << start_line << "-" << end_line 
              << " cursor=(" << cursor.line << "," << cursor.column << ")"
              << " active=" << is_active << std::endl;
    
    // Render buffer lines
    for (int screen_y = 0; screen_y < content_height && start_line + screen_y < end_line; ++screen_y) {
        size_t buffer_line_idx = start_line + screen_y;
        const auto& line_text = buffer.line(buffer_line_idx);
        
        // Clear this line
        move(y + screen_y, x);
        for (int i = 0; i < width; ++i) addch(' ');
        
        // Line number (if enabled)
        if (show_line_numbers) {
            mvprintw(y + screen_y, x, "%3zu   ", buffer_line_idx + 1);
        }

        // Line content with syntax highlighting
        if (!line_text.empty()) {
            int max_content_width = content_width - 1;
            const auto& styles = buffer.get_line_styles(buffer_line_idx);

            if (styles.empty()) {
                // No styles, render as plain text
                std::string display_text = line_text;
                if ((int)display_text.length() > max_content_width) {
                    display_text = display_text.substr(0, max_content_width - 3) + "...";
                }
                mvprintw(y + screen_y, x + line_number_width, "%s", display_text.c_str());
            } else {
                // Render with syntax highlighting
                size_t pos = 0;
                int screen_x = x + line_number_width;

                for (const auto& styled : styles) {
                    size_t start = styled.range.start.column;
                    size_t end = std::min(styled.range.end.column, line_text.size());

                    // Render unstyled text before this styled range
                    if (pos < start && pos < line_text.size()) {
                        size_t len = std::min(start - pos, line_text.size() - pos);
                        std::string unstyled = line_text.substr(pos, len);
                        mvprintw(y + screen_y, screen_x, "%s", unstyled.c_str());
                        screen_x += unstyled.length();
                        pos = start;
                    }

                    // Render styled text
                    if (pos < end && pos < line_text.size()) {
                        size_t len = std::min(end - pos, line_text.size() - pos);
                        std::string styled_text = line_text.substr(pos, len);

                        // Apply color
                        int attrs = get_attributes_for_face(styled.attr.face_name);
                        attron(attrs);
                        mvprintw(y + screen_y, screen_x, "%s", styled_text.c_str());
                        attroff(attrs);

                        screen_x += styled_text.length();
                        pos = end;
                    }
                }

                // Render remaining unstyled text
                if (pos < line_text.size()) {
                    std::string remaining = line_text.substr(pos);
                    if ((int)(screen_x - x - line_number_width + remaining.length()) > max_content_width) {
                        remaining = remaining.substr(0, max_content_width - (screen_x - x - line_number_width) - 3) + "...";
                    }
                    mvprintw(y + screen_y, screen_x, "%s", remaining.c_str());
                }
            }
        }

        // Highlight ISearch match
        if (core_->minibuffer_manager().is_isearch_active() && core_->minibuffer_manager().get_isearch_match_range().has_value()) {
            auto match = core_->minibuffer_manager().get_isearch_match_range().value();
            
            // Check if current line is part of the match
            if (buffer_line_idx >= match.start.line && buffer_line_idx <= match.end.line) {
                int horizontal_offset = window->viewport().horizontal_offset;
                
                size_t hl_start = (buffer_line_idx == match.start.line) ? match.start.column : 0;
                size_t hl_end = (buffer_line_idx == match.end.line) ? match.end.column : line_text.length();
                
                // Handle case where match extends past end of line (e.g. capturing newline)
                // Ensure we don't go past line text length for string extraction
                size_t text_len = line_text.length();
                size_t safe_hl_end = std::min(hl_end, text_len);

                // Adjust for horizontal scroll
                int draw_start = static_cast<int>(hl_start) - horizontal_offset;
                int draw_end = static_cast<int>(safe_hl_end) - horizontal_offset;
                
                // Clip to viewable area
                draw_start = std::max(0, draw_start);
                // Clip draw_end to window width
                if (draw_end > content_width) draw_end = content_width;

                if (draw_start < draw_end) {
                    // Extract text to highlight
                    std::string text_to_hl = line_text.substr(draw_start + horizontal_offset, draw_end - draw_start);
                    
                    int screen_x = x + line_number_width + draw_start;
                    
                    int attrs = get_attributes_for_face(core_->minibuffer_manager().is_isearch_failed() ? "SearchFail" : "SearchMatch");
                    if (attrs == 0) attrs = A_REVERSE;
                    
                    attron(attrs);
                    mvprintw(y + screen_y, screen_x, "%s", text_to_hl.c_str());
                    attroff(attrs);
                }
            }
        }
        
        // Show cursor if this is the cursor line and this is the active window
        if (buffer_line_idx == cursor.line && is_active && cursor_visible_) {
            int cursor_screen_x = x + line_number_width + cursor.column;
            if (cursor_screen_x < x + width) {
                char cursor_char = ' ';
                if (cursor.column < line_text.size()) {
                    cursor_char = line_text[cursor.column];
                }
                
                int attrs = get_attributes_for_face("cursor");
                if (attrs == 0) attrs = A_REVERSE;
                
                attron(attrs);
                mvaddch(y + screen_y, cursor_screen_x, cursor_char);
                attroff(attrs);
            }
        }
    }
    
    // Fill remaining lines (for empty lines below buffer) - no tildes
    size_t displayed_lines = std::min((size_t)content_height, end_line - start_line);
    for (int screen_y = displayed_lines; screen_y < content_height; ++screen_y) {
        move(y + screen_y, x);
        for (int i = 0; i < width; ++i) addch(' ');
    }
    
    // Render modeline for this window
    if (show_modeline) {
        render_window_modeline(window, x, y + content_height, width, is_active);
    }
}

void TuiEditor::render_window_modeline(std::shared_ptr<Window> window, int x, int y, int width, bool is_active) {
    const auto& buffer = window->buffer();
    const auto cursor = window->cursor();
    
    // Choose modeline colors
    std::string face_name = is_active ? "mode-line" : "mode-line-inactive";
    int attrs = get_attributes_for_face(face_name);
    if (attrs == 0) attrs = is_active ? A_REVERSE : A_DIM;
    
    attron(attrs);
    
    // Clear the modeline
    move(y, x);
    for (int i = 0; i < width; ++i) addch(' ');
    
    // Create modeline content
    std::string modeline;
    
    // Buffer name and modification status
    modeline += buffer.name();
    if (buffer.is_modified()) modeline += " [+] ";
    
    // Cursor position
    modeline += " | " + std::to_string(cursor.line + 1) + ":" + std::to_string(cursor.column + 1);
    
    // Major mode (if available)
    // TODO: Add major mode support when available
    
    // Right-aligned content (percentage through file)
    std::string right_side;
    if (buffer.line_count() > 0) {
        int percentage = (cursor.line * 100) / (buffer.line_count() - 1);
        right_side = " " + std::to_string(percentage) + "%";
    }
    
    // Truncate modeline if too long
    int available_width = width - right_side.length();
    if ((int)modeline.length() > available_width) {
        modeline = modeline.substr(0, available_width - 3) + "...";
    }
    
    // Render left side
    mvprintw(y, x, "%s", modeline.c_str());
    
    // Render right side
    if (!right_side.empty()) {
        mvprintw(y, x + width - right_side.length(), "%s", right_side.c_str());
    }
    
    // Turn off modeline attributes
    attroff(attrs);
}

void TuiEditor::render_message_line() {
    int msg_y = height_ - 1;
    int attrs = get_attributes_for_face("minibuffer-prompt");
    
    attron(attrs);
    move(msg_y, 0);
    clrtoeol();

    if (core_->minibuffer_manager().is_active()) {
        std::string prompt_part = core_->minibuffer_manager().get_prompt();
        std::string input_part = core_->minibuffer_manager().get_input_buffer();
        std::string display_text = prompt_part + input_part;
        
        mvprintw(msg_y, 0, "%s", display_text.c_str());

        // Display completion candidates below the input line
        auto candidates = core_->minibuffer_manager().get_completion_candidates();
        if (!candidates.empty()) {
            std::string completion_display;
            for (size_t i = 0; i < candidates.size() && static_cast<int>(completion_display.length()) < width_ - 5; ++i) {
                if (!completion_display.empty()) completion_display += " ";
                completion_display += candidates[i].display_text; // Use display_text
            }
            if (static_cast<int>(completion_display.length()) >= width_ - 5) {
                completion_display = completion_display.substr(0, width_ - 8) + "...";
            }
            // Move up one line to display completions above the current minibuffer line
            mvprintw(msg_y - 1, 0, "%s", completion_display.c_str());
        }

    } else if (!message_line_.empty()) {
        // Display transient message
        mvprintw(msg_y, 0, "%s", message_line_.c_str());
    }
    
    attroff(attrs);

    // After rendering, check if it's time to clear the message.
    core_->check_and_clear_message();
}

namespace lumacs {
std::unique_ptr<IEditorView> create_tui_editor() {
    return std::make_unique<TuiEditor>();
}
}
