#include "gtest/gtest.h"
#include "lumacs/editor_core.hpp"
#include "lumacs/keybinding.hpp"
#include "lumacs/command_system.hpp"
#include "lumacs/buffer.hpp"
#include <memory>

using namespace lumacs;

// Test fixture for Input Pipeline integration
class InputPipelineTest : public ::testing::Test {
protected:
    void SetUp() override {
        core = std::make_unique<EditorCore>();
        // Ensure a clean buffer state for tests
        core->buffer().clear();
        core->set_cursor({0,0});
        
        // We rely on EditorCore loading init.lua by default, which registers commands.
        // However, in a test environment, init.lua might not be found or might be the user's.
        // For robust testing, we should manually register the critical commands if possible,
        // or mock the Lua environment.
        
        // Since we can't easily mock LuaApi fully here without a lot of setup,
        // let's manually register the 'self-insert-command' C++ handler directly 
        // into the CommandSystem to simulate what init.lua does.
        // This ensures we are testing the C++ input path logic, not the Lua file loading.
        
        // Let's register a mock self-insert-command.
        core->command_system().register_command("self-insert-command", 
            [this](CommandContext& ctx) -> CommandResult {
                auto char_str = ctx.get_string_arg(0);
                if (char_str && !char_str->empty()) {
                    core->buffer().insert(core->cursor(), *char_str);
                    core->move_right();
                    return {CommandStatus::Success, ""};
                }
                return {CommandStatus::Failure, "No char arg"};
            }
        );
        
        // Also bind a test key
        core->command_system().register_command("test-command",
            [this](CommandContext&) -> CommandResult {
                test_command_executed = true;
                return {CommandStatus::Success, "Test command executed"};
            }
        );
        
        core->keybinding_manager().bind("C-c t", "test-command");
    }

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

    std::unique_ptr<EditorCore> core;
    bool test_command_executed = false;
};

TEST_F(InputPipelineTest, KeyBindingExecution) {
    // Simulate processing keys "C-c" then "t"
    auto result1 = core->keybinding_manager().process_key("C-c");
    EXPECT_EQ(result1.type, KeyResult::Partial);
    EXPECT_FALSE(test_command_executed);

    auto result2 = core->keybinding_manager().process_key("t");
    EXPECT_EQ(result2.type, KeyResult::Executed);
    EXPECT_TRUE(test_command_executed);
}

TEST_F(InputPipelineTest, SelfInsertFallbackLogic) {
    // This test replicates the logic found in GtkEditor/TuiEditor handle_input
    // simulating what happens when an unbound key is pressed.
    
    std::string input_key = "a";
    
    // 1. Process key via KeyBindingManager
    auto result = core->keybinding_manager().process_key(input_key);
    
    // 2. Expect Unbound (as 'a' is not explicitly bound)
    EXPECT_EQ(result.type, KeyResult::Unbound);
    
    // 3. Simulate UI Fallback Logic
    bool handled = false;
    if (result.type == KeyResult::Unbound) {
        if (input_key.length() == 1) { // Check for printable char
            core->command_system().execute("self-insert-command", {input_key});
            handled = true;
        }
    }
    
    EXPECT_TRUE(handled);
    
    // 4. Verify Buffer Content
    EXPECT_EQ(core->buffer().content(), "a");
    EXPECT_EQ(core->cursor().column, 1);
}

TEST_F(InputPipelineTest, UnboundSpecialKeyIgnored) {
    std::string input_key = "F5"; // Assume unbound but recognized special key
    
    auto result = core->keybinding_manager().process_key(input_key);
    // F5 is a known key, but not explicitly bound in this test setup.
    // KeyBindingManager should return KeyResult::Unbound for a key that is not bound and not a prefix.
    EXPECT_EQ(result.type, KeyResult::Unbound); 
    
    bool handled = false;
    if (result.type == KeyResult::Unbound) { // This block should NOT be entered now, as result.type is Handled
        if (input_key.length() == 1) { 
            core->command_system().execute("self-insert-command", {input_key});
            handled = true;
        }
    }
    
    EXPECT_FALSE(handled); // Ensure self-insert fallback is not triggered
    EXPECT_EQ(core->buffer().content(), ""); // Buffer should remain empty
}
