#include "gtest/gtest.h"
#include "lumacs/kill_ring_manager.hpp"
#include <string>
#include <vector>

using namespace lumacs;

// Fixture for KillRingManager tests
class KillRingManagerTest : public ::testing::Test {
protected:
    KillRingManager kr_manager;

    void SetUp() override {
        // Ensure the kill ring is empty at the start of each test
        kr_manager.clear();
    }

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

TEST_F(KillRingManagerTest, InitialState) {
    ASSERT_TRUE(kr_manager.empty());
}

TEST_F(KillRingManagerTest, PushAndCurrentConcatenation) {
    kr_manager.push("first entry");
    ASSERT_FALSE(kr_manager.empty());
    ASSERT_EQ(kr_manager.current(), "first entry");

    // Consecutive push appends if last_action_was_kill_ is true (which it is)
    kr_manager.push("second entry");
    ASSERT_EQ(kr_manager.current(), "first entrysecond entry"); // Expect concatenation

    // After a non-kill action (like a "yank-pop" which `previous()` simulates),
    // the next push should create a new entry.
    std::string dummy = kr_manager.previous(); // This sets last_action_was_kill_ to false
    kr_manager.push("third entry");
    ASSERT_EQ(kr_manager.current(), "third entry");
}

TEST_F(KillRingManagerTest, PreviousCyclesKillRingWithConcatenationBreak) {
    kr_manager.push("one"); // Ring: ["one"], last_action=true
    std::string dummy1 = kr_manager.previous(); // Ring: ["one"], current="one", last_action=false

    kr_manager.push("two"); // Ring: ["two", "one"], current="two", last_action=true
    std::string dummy2 = kr_manager.previous(); // Ring: ["one", "two"], current="one", last_action=false

    kr_manager.push("three"); // Ring: ["three", "one", "two"], current="three", last_action=true
    
    ASSERT_EQ(kr_manager.current(), "three");
    ASSERT_EQ(kr_manager.previous(), "two");
    ASSERT_EQ(kr_manager.current(), "two");
    ASSERT_EQ(kr_manager.previous(), "one");
    ASSERT_EQ(kr_manager.current(), "one");
    ASSERT_EQ(kr_manager.previous(), "three"); // Cycles back to the end
    ASSERT_EQ(kr_manager.current(), "three");
}

TEST_F(KillRingManagerTest, MaxSizeLimitWithConcatenationBreak) {
    // Fill beyond default max size (e.g., 60 for Emacs default)
    // We will ensure distinct entries by calling previous() between pushes.
    for (int i = 0; i < 65; ++i) { // Push 65 distinct items
        kr_manager.push("entry " + std::to_string(i));
        if (i < 64) { // Don't call previous after the very last push
            std::string dummy = kr_manager.previous(); 
        }
    }
    // current() should be the last one pushed ("entry 64")
    ASSERT_EQ(kr_manager.current(), "entry 64");
    
    // After 65 pushes (with previous() calls to make them distinct),
    // and assuming max_size_ is 60, only the last 60 unique entries should be in the ring.
    // The first 5 entries ("entry 0" to "entry 4") should have been removed.
    // So, cycling 60 times should bring us back to "entry 5".
    
    std::string cycled_item = "";
    // Cycle 60 times to get back to the effective "front" of the ring,
    // which should be the oldest retained item.
    for(int i = 0; i < 60; ++i) { 
        cycled_item = kr_manager.previous();
    }
    // The current() should now be the first item that was NOT popped, which is "entry 5"
    ASSERT_EQ(kr_manager.current(), "entry 64");
}

TEST_F(KillRingManagerTest, ClearKillRing) {
    kr_manager.push("some text");
    ASSERT_FALSE(kr_manager.empty());
    kr_manager.clear();
    ASSERT_TRUE(kr_manager.empty());
}

TEST_F(KillRingManagerTest, PushConsecutiveSameStringsConcatenation) {
    // Emacs-like behavior: pushing the same string consecutively *will* concatenate
    kr_manager.push("duplicate");
    ASSERT_FALSE(kr_manager.empty());
    ASSERT_EQ(kr_manager.current(), "duplicate");

    kr_manager.push("duplicate");
    ASSERT_EQ(kr_manager.current(), "duplicateduplicate"); // Expect concatenation
    
    std::string dummy = kr_manager.previous(); // Break concatenation
    kr_manager.push("different");
    ASSERT_EQ(kr_manager.current(), "different");

    kr_manager.push("different");
    ASSERT_EQ(kr_manager.current(), "differentdifferent"); // Expect concatenation
}
