#include "lumacs/buffer_manager.hpp"
#include "lumacs/editor_core.hpp"
#include "gtest/gtest.h"
#include <algorithm>

// Fixture for BufferManager tests
class BufferManagerTest : public ::testing::Test {
protected:
    // EditorCore is needed to initialize BufferManager
    // For unit tests, we want to isolate BufferManager.
    // However, BufferManager's constructor requires an EditorCore&.
    // We will use a real EditorCore instance but only test BufferManager's direct responsibilities.
    lumacs::EditorCore core; 
    std::unique_ptr<lumacs::BufferManager> buffer_manager;

    void SetUp() override {
        // BufferManager's constructor relies on EditorCore's core subsystems.
        // For these unit tests, we'll instantiate BufferManager with a minimal EditorCore.
        // A fully functional EditorCore creates the "*scratch*" buffer via new_buffer()
        // which involves WindowManager. For unit testing BufferManager's internals,
        // we'll explicitly create buffers using create_buffer_no_window().
        // Since EditorCore now implements IEditorNotifier and IWindowManager, we pass it for both interfaces.
        buffer_manager = std::make_unique<lumacs::BufferManager>(core, core);
    }

    void TearDown() override {
        // Clean up if necessary
    }
};

TEST_F(BufferManagerTest, InitialState) {
    // No buffers created by BufferManager itself in its constructor in this setup.
    // The *scratch* buffer is created by EditorCore::new_buffer which is not invoked here.
    ASSERT_EQ(buffer_manager->get_all_buffer_info().size(), 0);
}

TEST_F(BufferManagerTest, CreateBufferNoWindow) {
    auto new_buffer_ptr = buffer_manager->create_buffer_no_window("new-buffer");
    ASSERT_NE(new_buffer_ptr, nullptr);
    ASSERT_EQ(new_buffer_ptr->name(), "new-buffer");
    ASSERT_EQ(buffer_manager->get_all_buffer_info().size(), 1);
    ASSERT_NE(buffer_manager->get_buffer_by_name("new-buffer"), nullptr);
}

TEST_F(BufferManagerTest, GetBufferNames) {
    buffer_manager->create_buffer_no_window("alpha");
    buffer_manager->create_buffer_no_window("beta");
    auto names = buffer_manager->get_buffer_names();
    
    ASSERT_EQ(names.size(), 2); // alpha, beta
    ASSERT_TRUE(std::find(names.begin(), names.end(), "alpha") != names.end());
    ASSERT_TRUE(std::find(names.begin(), names.end(), "beta") != names.end());
}

TEST_F(BufferManagerTest, CloseBuffer) {
    buffer_manager->create_buffer_no_window("temp-buffer");
    buffer_manager->create_buffer_no_window("another-buffer"); // Ensure there's always another buffer
    ASSERT_EQ(buffer_manager->get_all_buffer_info().size(), 2); 

    buffer_manager->close_buffer("temp-buffer");
    ASSERT_EQ(buffer_manager->get_all_buffer_info().size(), 1); 
    ASSERT_EQ(buffer_manager->get_buffer_by_name("temp-buffer"), nullptr);
}

TEST_F(BufferManagerTest, CloseNonExistentBuffer) {
    auto initial_size = buffer_manager->get_all_buffer_info().size();
    bool closed = buffer_manager->close_buffer("non-existent-buffer");
    ASSERT_FALSE(closed);
    ASSERT_EQ(buffer_manager->get_all_buffer_info().size(), initial_size);
}

TEST_F(BufferManagerTest, CreateBufferNoWindowWithDuplicateName) {
    auto buf1 = buffer_manager->create_buffer_no_window("duplicate");
    ASSERT_EQ(buffer_manager->get_all_buffer_info().size(), 1); // First creation adds it

    auto buf2 = buffer_manager->create_buffer_no_window("duplicate"); // Should return existing
    ASSERT_EQ(buffer_manager->get_all_buffer_info().size(), 1); // Size should not increase
    ASSERT_EQ(buf1, buf2); // Should return the same shared_ptr
    ASSERT_EQ(buf2->name(), "duplicate");
}
