#include "gtest/gtest.h"
#include "lumacs/window_manager.hpp"
#include "lumacs/editor_core.hpp"
#include "lumacs/window.hpp"
#include "lumacs/layout_node.hpp"
#include <memory>
#include <vector>
#include <numeric> // For std::iota

using namespace lumacs;

// Fixture for WindowManager tests
class WindowManagerTest : public ::testing::Test {
protected:
    std::unique_ptr<EditorCore> core;
    WindowManager* wm; // Raw pointer for convenience, owned by core

    void SetUp() override {
        // Instantiate EditorCore which in turn initializes WindowManager and BufferManager
        core = std::make_unique<EditorCore>();
        wm = &core->window_manager(); // Get reference to the manager owned by core

        // Ensure a clean slate for some tests by explicitly creating a new scratch buffer
        // if the default one from init.lua is not suitable.
        // For these tests, we mostly care about window *layout*, not buffer content.
        core->buffer().clear();
        core->set_cursor({0,0});
    }

    void TearDown() override {
        core.reset();
    }

    // Helper to count windows in the layout tree
    size_t count_windows_in_layout() {
        std::vector<std::shared_ptr<Window>> windows;
        core->collect_windows(wm->root_layout().get(), windows);
        return windows.size();
    }
};

TEST_F(WindowManagerTest, InitialState) {
    // Initially, there should be one active window
    ASSERT_NE(wm->active_window(), nullptr);
    ASSERT_EQ(count_windows_in_layout(), 1);
    ASSERT_EQ(wm->root_layout()->type, LayoutNode::Type::Leaf);
    ASSERT_EQ(wm->root_layout()->window, wm->active_window());
}

TEST_F(WindowManagerTest, SplitHorizontally) {
    auto original_active_window = wm->active_window();
    wm->split_horizontally();

    // Should now have two windows
    ASSERT_EQ(count_windows_in_layout(), 2);
    ASSERT_EQ(wm->root_layout()->type, LayoutNode::Type::HorizontalSplit);
    ASSERT_NE(wm->root_layout()->child1, nullptr);
    ASSERT_NE(wm->root_layout()->child2, nullptr);

    // New window should be active
    ASSERT_NE(wm->active_window(), original_active_window);
}

TEST_F(WindowManagerTest, SplitVertically) {
    auto original_active_window = wm->active_window();
    wm->split_vertically();

    // Should now have two windows
    ASSERT_EQ(count_windows_in_layout(), 2);
    ASSERT_EQ(wm->root_layout()->type, LayoutNode::Type::VerticalSplit);
    ASSERT_NE(wm->root_layout()->child1, nullptr);
    ASSERT_NE(wm->root_layout()->child2, nullptr);

    // New window should be active
    ASSERT_NE(wm->active_window(), original_active_window);
}

TEST_F(WindowManagerTest, CloseActiveWindowMergesLayout) {
    wm->split_horizontally(); // Creates 2 windows
    ASSERT_EQ(count_windows_in_layout(), 2);
    auto window_before_close = wm->active_window();

    wm->close_active_window(); // Closes the newly created window
    ASSERT_EQ(count_windows_in_layout(), 1); // Should merge back to one window
    ASSERT_NE(wm->active_window(), window_before_close); // Active window should be the remaining one
    ASSERT_EQ(wm->root_layout()->type, LayoutNode::Type::Leaf);
}

TEST_F(WindowManagerTest, CloseLastWindowDoesNothing) {
    ASSERT_EQ(count_windows_in_layout(), 1);
    auto original_active = wm->active_window();

    wm->close_active_window(); // Attempt to close the only window
    ASSERT_EQ(count_windows_in_layout(), 1); // Should still be one window
    ASSERT_EQ(wm->active_window(), original_active); // Should still be the same window
}

TEST_F(WindowManagerTest, NextWindowCyclesFocus) {
    wm->split_horizontally(); // Window 1 (original), Window 2 (newly active)
    auto w1 = wm->root_layout()->child1->window; // Original window
    auto w2 = wm->root_layout()->child2->window; // New window, active

    ASSERT_EQ(wm->active_window(), w2);
    
    wm->next_window(); // Should cycle to w1
    ASSERT_EQ(wm->active_window(), w1);

    wm->next_window(); // Should cycle back to w2
    ASSERT_EQ(wm->active_window(), w2);
}

TEST_F(WindowManagerTest, ComplexSplitAndClose) {
    // Initial: W1
    wm->split_vertically(); // Root: |W1|W2| (W2 active)
    wm->split_horizontally(); // Root: |W1| [W3;W2] (W2 active remains W2, so W3 active)
    // The previous split makes the new window active. So if W2 was active, after split_horizontally on W2, W3 is active.
    // Layout: |W1|
    //        ----
    //        |W3|
    //        ---- (W3 active)
    //        |W2|

    ASSERT_EQ(count_windows_in_layout(), 3);
    auto w1 = wm->root_layout()->child1->window;
    auto w2_node = wm->root_layout()->child2; // This is a split node
    auto w3 = w2_node->child1->window;
    auto w2 = w2_node->child2->window;

    wm->next_window(); // Should cycle to W1 (or W2, depends on traversal order)
    wm->next_window(); // Cycle again

    wm->close_active_window(); // Close the active window (W2 or W3)
    ASSERT_EQ(count_windows_in_layout(), 2); // Should have 2 windows left
}
