#include "gtest/gtest.h"
#include "lumacs/macro_manager.hpp"
#include "lumacs/editor_core.hpp"
#include <string>
#include <vector>
#include <memory>

using namespace lumacs;

// Fixture for MacroManager tests
class MacroManagerTest : public ::testing::Test {
protected:
    EditorCore core; // MacroManager's constructor requires an EditorCore&
    std::unique_ptr<MacroManager> macro_manager;

    void SetUp() override {
        macro_manager = std::make_unique<MacroManager>(core);
    }

    void TearDown() override {
        // Nothing specific needed
    }
};

TEST_F(MacroManagerTest, InitialState) {
    ASSERT_FALSE(macro_manager->is_recording_macro());
    ASSERT_TRUE(macro_manager->get_last_macro().empty());
}

TEST_F(MacroManagerTest, StartAndEndRecording) {
    macro_manager->start_kbd_macro();
    ASSERT_TRUE(macro_manager->is_recording_macro());

    macro_manager->record_key_sequence("a");
    macro_manager->record_key_sequence("b");
    macro_manager->record_key_sequence("C-c");

    macro_manager->end_kbd_macro_or_call();
    ASSERT_FALSE(macro_manager->is_recording_macro());
    ASSERT_FALSE(macro_manager->get_last_macro().empty());
    ASSERT_EQ(macro_manager->get_last_macro().size(), 3);
    ASSERT_EQ(macro_manager->get_last_macro()[0], "a");
    ASSERT_EQ(macro_manager->get_last_macro()[1], "b");
    ASSERT_EQ(macro_manager->get_last_macro()[2], "C-c");
}

TEST_F(MacroManagerTest, CallLastMacroWithoutRecording) {
    // Should not crash, and should not execute anything
    macro_manager->end_kbd_macro_or_call(); // Call when not recording and no last macro
    ASSERT_FALSE(macro_manager->is_recording_macro());
    ASSERT_TRUE(macro_manager->get_last_macro().empty());
}

TEST_F(MacroManagerTest, CallLastMacroExecutesKeys) {
    // This test is harder to verify directly in a unit test.
    // Executing the macro involves calling core.process_key, which
    // affects the editor's state (buffer, cursor).
    // For now, we can only verify the MacroManager's internal state.
    // A proper test would require mocking EditorCore::process_key or
    // making an integration test.

    macro_manager->start_kbd_macro();
    macro_manager->record_key_sequence("h");
    macro_manager->record_key_sequence("e");
    macro_manager->record_key_sequence("l");
    macro_manager->record_key_sequence("l");
    macro_manager->record_key_sequence("o");
    macro_manager->end_kbd_macro_or_call(); // This stores the macro

    // Calling the last macro will execute the recorded sequence.
    // We expect the macro to be executed.
    // However, without mocking core.process_key, we can only test MacroManager's state.
    // The previous test already verified the macro is recorded.
    // Let's ensure calling it doesn't crash.
    macro_manager->end_kbd_macro_or_call(); // This calls the last recorded macro
    ASSERT_FALSE(macro_manager->is_recording_macro()); // Still not recording
    ASSERT_FALSE(macro_manager->get_last_macro().empty()); // Macro should still be there
}

TEST_F(MacroManagerTest, ConsecutiveMacroRecording) {
    macro_manager->start_kbd_macro();
    macro_manager->record_key_sequence("a");
    macro_manager->end_kbd_macro_or_call();
    ASSERT_EQ(macro_manager->get_last_macro().size(), 1);
    ASSERT_EQ(macro_manager->get_last_macro()[0], "a");

    macro_manager->start_kbd_macro();
    macro_manager->record_key_sequence("b");
    macro_manager->record_key_sequence("c");
    macro_manager->end_kbd_macro_or_call();
    ASSERT_EQ(macro_manager->get_last_macro().size(), 2);
    ASSERT_EQ(macro_manager->get_last_macro()[0], "b");
    ASSERT_EQ(macro_manager->get_last_macro()[1], "c");
}

TEST_F(MacroManagerTest, MacroRecordingDuringMacroExecution) {
    // This tests Emacs' behavior: if you start recording during a macro call,
    // the executed keys are recorded into the new macro.
    // This requires mocking EditorCore's process_key.

    // For now, simple check:
    macro_manager->start_kbd_macro();
    macro_manager->record_key_sequence("a");
    macro_manager->end_kbd_macro_or_call(); // Macro: [a]

    // Simulate calling the macro, and during its call, start recording another
    // This can't be fully tested without a way to intercept process_key calls
    // and inject start_kbd_macro().
    // We'll rely on integration tests for this.
}
