#include "gtest/gtest.h"
#include "lumacs/editor_core.hpp"
#include "lumacs/buffer_manager.hpp"
#include "lumacs/window_manager.hpp"
#include "lumacs/kill_ring_manager.hpp"
#include "lumacs/register_manager.hpp"
#include "lumacs/macro_manager.hpp"
#include "lumacs/rectangle_manager.hpp"
#include <string>

// Test fixture for EditorCore, initializing core subsystems
struct EditorCoreTest : public ::testing::Test {
    lumacs::EditorCore core;

    void SetUp() override {
        // EditorCore's constructor already initializes most things.
        // We might want to mock some dependencies for more isolated tests later.
    }

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

TEST_F(EditorCoreTest, InitialSetup) {
    ASSERT_NE(core.buffer_manager().active_buffer(), nullptr);
    ASSERT_NE(core.window_manager().active_window(), nullptr);
    ASSERT_EQ(core.buffer().name(), "*scratch*");
    ASSERT_EQ(core.cursor().line, 0);
    ASSERT_EQ(core.cursor().column, 0);
}

TEST_F(EditorCoreTest, MoveCursorRight) {
    // Test assumes initial cursor is at (0,0) on an empty buffer.
    // EditorCore constructor and init.lua execution might affect this.
    // Let's ensure a clean state first if needed, but for now test move_right.
    core.buffer().insert({0,0}, "test");
    core.set_cursor({0,0});
    core.move_right(); 
    ASSERT_EQ(core.cursor().column, 1);
}

TEST_F(EditorCoreTest, NewBufferCommand) {
    core.new_buffer("test_new");
    ASSERT_EQ(core.buffer().name(), "test_new");
    ASSERT_EQ(core.cursor().line, 0);
    ASSERT_EQ(core.cursor().column, 0);
}

TEST_F(EditorCoreTest, SetMessage) {
    std::string test_message = "Hello, Lumacs!";
    core.set_message(test_message);
    ASSERT_EQ(core.last_message(), test_message);
}

TEST_F(EditorCoreTest, SplitWindowHorizontally) {
    core.split_horizontally();
    // After split, there should be 2 windows in the layout.
    std::vector<std::shared_ptr<lumacs::Window>> windows;
    core.window_manager().collect_windows(core.root_layout().get(), windows);
    ASSERT_EQ(windows.size(), 2);
    // The newly created window should be the active one.
    ASSERT_EQ(core.active_window(), windows[1]); 
}

TEST_F(EditorCoreTest, KillLine) {
    core.buffer_manager().active_buffer()->insert({0,0}, "Line 1\nLine 2");
    core.set_cursor({0,0});
    core.kill_line();
    ASSERT_EQ(core.buffer().line(0), "Line 2");
    ASSERT_FALSE(core.kill_ring_manager().empty());
    ASSERT_EQ(core.kill_ring_manager().current(), "Line 1\n");
}

TEST_F(EditorCoreTest, Yank) {
    core.buffer_manager().active_buffer()->insert({0,0}, "some text");
    core.set_cursor({0,5}); // at ' '
    core.kill_line(); // Kills " text"
    core.yank(); // Yanks " text"
    ASSERT_EQ(core.buffer().line(0), "some text"); // Should be "some" + " text" from kill ring
    ASSERT_EQ(core.cursor().column, 9); // Cursor should be at end of yanked text
}

TEST_F(EditorCoreTest, CopyToRegisterAndInsert) {
    char reg_name = 'a';
    std::string reg_content = "Register content";
    core.copy_to_register(reg_name, reg_content);
    
    core.buffer_manager().active_buffer()->insert({0,0}, "Original ");
    core.set_cursor({0,9});
    core.insert_register(reg_name);
    
    ASSERT_EQ(core.buffer().line(0), "Original Register content");
}

TEST_F(EditorCoreTest, StartAndEndMacro) {
    ASSERT_FALSE(core.macro_manager().is_recording_macro());
    core.start_kbd_macro();
    ASSERT_TRUE(core.macro_manager().is_recording_macro());
    core.record_key_sequence("a");
    core.end_kbd_macro_or_call();
    ASSERT_FALSE(core.macro_manager().is_recording_macro());
    // Executing the macro will depend on process_key being called
    // which is hard to test without mocking keybinding_manager directly.
    // For now, just test recording state.
}

TEST_F(EditorCoreTest, KillRectangle) {
    core.buffer_manager().active_buffer()->insert({0,0}, "Line1\nLine2\nLine3");
    core.buffer_manager().active_buffer()->set_mark({0,1}); // Mark 'i'
    core.set_cursor({2,3}); // Cursor 'e' in Line3
    core.kill_rectangle();
    
    // Expected buffer state after killing rectangle from (0,1) to (2,3)
    // "Lne1"
    // "Lne2"
    // "Lne3"
        ASSERT_EQ(core.buffer().line(0), "Le1");
        ASSERT_EQ(core.buffer().line(1), "Le2");
        ASSERT_EQ(core.buffer().line(2), "Le3");    // Check if rectangle was killed by verifying we can yank it back or check if it's not empty
    // Since we can't access private members, we'll check side effects or public API
    // For now, let's just assert that the buffer changed as expected.
    // A more robust test would verify the content of the rectangle_manager's kill ring
    // or through `yank_rectangle`.
    
    // For this test, we expect the buffer to be modified
    ASSERT_NE(core.buffer().content(), "Line1\nLine2\nLine3");
}
