#include "lumacs/window_manager.hpp"
#include "lumacs/buffer_manager.hpp"
#include "lumacs/i_editor_notifier.hpp"
#include "lumacs/logger.hpp"
#include <spdlog/spdlog.h>
#include <algorithm>

namespace lumacs {

// Helper to recursively replace a window in the tree
bool WindowManager::replace_window_node(std::shared_ptr<LayoutNode> node, 
                                        std::shared_ptr<Window> target, 
                                        std::shared_ptr<LayoutNode> replacement) {
    if (node->type == LayoutNode::Type::Leaf) {
        return false;
    }
    
    // Check immediate children
    if (node->child1->type == LayoutNode::Type::Leaf) {
        if (node->child1->window == target) {
            node->child1 = replacement;
            return true;
        }
    }
    
    if (node->child2->type == LayoutNode::Type::Leaf) {
        if (node->child2->window == target) {
            node->child2 = replacement;
            return true;
        }
    }
    
    // Recurse
    bool found = false;
    if (node->child1->type != LayoutNode::Type::Leaf) {
        found = replace_window_node(node->child1, target, replacement);
    }
    
    if (!found && node->child2->type != LayoutNode::Type::Leaf) {
        found = replace_window_node(node->child2, target, replacement);
    }
    
    return found;
}

// Recursive parent finder
LayoutNode* WindowManager::find_parent_of_node(LayoutNode* current, LayoutNode* child_target) {
    if (current->type == LayoutNode::Type::Leaf) return nullptr;
    
    if (current->child1.get() == child_target || current->child2.get() == child_target) {
        return current;
    }
    
    auto left = find_parent_of_node(current->child1.get(), child_target);
    if (left) return left;
    
    return find_parent_of_node(current->child2.get(), child_target);
}

// Recursive leaf finder
LayoutNode* WindowManager::find_node_with_window(LayoutNode* current, std::shared_ptr<Window> target) {
    if (current->type == LayoutNode::Type::Leaf) {
        return (current->window == target) ? current : nullptr;
    }
    auto left = find_node_with_window(current->child1.get(), target);
    if (left) return left;
    return find_node_with_window(current->child2.get(), target);
}


WindowManager::WindowManager(IEditorNotifier& notifier, BufferManager& buffer_manager) 
    : notifier_(notifier), buffer_manager_(buffer_manager) {
    // Initial setup: create a default window with a scratch buffer
    // Use create_buffer_no_window to avoid circular dependency with active_window()
    auto initial_buffer = buffer_manager_.create_buffer_no_window("*scratch*");
    active_window_ = std::make_shared<Window>(initial_buffer);
    root_node_ = std::make_shared<LayoutNode>(active_window_);
}

std::shared_ptr<Window> WindowManager::active_window() const {
    return active_window_;
}

bool WindowManager::set_active_window(std::shared_ptr<Window> window) {
    if (!window) return false;
    
    // Verify that the window exists in the current window tree
    std::vector<std::shared_ptr<Window>> windows;
    collect_windows(root_node_.get(), windows);
    
    auto it = std::find(windows.begin(), windows.end(), window);
    if (it != windows.end()) {
        if (active_window_ != window) {
            active_window_ = window;
            notifier_.emit_event(EditorEvent::WindowFocused);
        }
        return true;
    }
    
    return false; // Window not found in tree
}

void WindowManager::split_horizontally() {
    spdlog::debug("split_horizontally() called");

    // New window sharing same buffer as active window
    auto new_window = std::make_shared<Window>(active_window_->buffer_ptr());
    new_window->set_cursor(active_window_->cursor()); // Start at same position

    // Create split node
    auto new_leaf = std::make_shared<LayoutNode>(new_window);
    auto current_leaf = std::make_shared<LayoutNode>(active_window_);

    auto split = std::make_shared<LayoutNode>(
        LayoutNode::Type::HorizontalSplit,
        current_leaf, // Top
        new_leaf      // Bottom
    );

    if (root_node_->type == LayoutNode::Type::Leaf && root_node_->window == active_window_) {
        spdlog::debug("Replacing root node");
        root_node_ = split;
    } else {
        spdlog::debug("Replacing window node in tree");
        replace_window_node(root_node_, active_window_, split);
    }

    active_window_ = new_window; // Focus new window
    notifier_.emit_event(EditorEvent::WindowLayoutChanged);
    spdlog::debug("split_horizontally() completed");
}

void WindowManager::split_vertically() {
    spdlog::debug("split_vertically() called");

    // New window sharing same buffer as active window
    auto new_window = std::make_shared<Window>(active_window_->buffer_ptr());
    new_window->set_cursor(active_window_->cursor());

    // Create split node
    auto new_leaf = std::make_shared<LayoutNode>(new_window);
    auto current_leaf = std::make_shared<LayoutNode>(active_window_);

    // Vertical Split = Left/Right division
    auto split = std::make_shared<LayoutNode>(
        LayoutNode::Type::VerticalSplit,
        current_leaf, // Left
        new_leaf      // Right
    );

    if (root_node_->type == LayoutNode::Type::Leaf && root_node_->window == active_window_) {
        spdlog::debug("Replacing root node");
        root_node_ = split;
    } else {
        spdlog::debug("Replacing window node in tree");
        replace_window_node(root_node_, active_window_, split);
    }
    
    active_window_ = new_window;
    notifier_.emit_event(EditorEvent::WindowLayoutChanged);
    spdlog::debug("split_vertically() completed");
}

void WindowManager::close_active_window() {
    // Cannot close last window
    if (root_node_->type == LayoutNode::Type::Leaf) {
        return;
    }
    
    // Find the node containing active_window
    LayoutNode* target_node = find_node_with_window(root_node_.get(), active_window_);
    if (!target_node) return; // Should not happen 
    
    LayoutNode* parent = find_parent_of_node(root_node_.get(), target_node);
    if (!parent) return; // Should not happen if not root
    
    // Identify sibling
    std::shared_ptr<LayoutNode> sibling;
    if (parent->child1.get() == target_node) {
        sibling = parent->child2;
    } else {
        sibling = parent->child1;
    }
    
    // Replace parent with sibling
    // If parent is root, root becomes sibling
    if (parent == root_node_.get()) {
        root_node_ = sibling;
    } else {
        // Find grandparent
        LayoutNode* grandparent = find_parent_of_node(root_node_.get(), parent);
        if (grandparent->child1.get() == parent) {
            grandparent->child1 = sibling;
        } else {
            grandparent->child2 = sibling;
        }
    }
    
    // Focus a new window (first leaf in sibling)
    std::vector<std::shared_ptr<Window>> windows;
    collect_windows(sibling.get(), windows);
    if (!windows.empty()) {
        active_window_ = windows[0];
    }
    
    notifier_.emit_event(EditorEvent::WindowLayoutChanged);
}

void WindowManager::next_window() {
    // Cycle to the next window in the window tree
    // Note: Focus jumping bug was fixed in GTK frontend by caching active_window during redraws
    std::vector<std::shared_ptr<Window>> windows;
    collect_windows(root_node_.get(), windows);

    if (windows.size() <= 1) return;

    auto it = std::find(windows.begin(), windows.end(), active_window_);
    if (it != windows.end()) {
        auto next = it + 1;
        if (next == windows.end()) {
            active_window_ = windows[0];
        } else {
            active_window_ = *next;
        }
        notifier_.emit_event(EditorEvent::WindowFocused);
    }
}

void WindowManager::collect_windows(LayoutNode* node, std::vector<std::shared_ptr<Window>>& windows) {
    if (node->type == LayoutNode::Type::Leaf) {
        windows.push_back(node->window);
    } else {
        collect_windows(node->child1.get(), windows);
        collect_windows(node->child2.get(), windows);
    }
}

} // namespace lumacs
