#include <gtest/gtest.h>
#include "LockableDObject.h"
#include <thread>
#include <vector>

/**
 * @file LockableDObjectTest.cpp
 * @brief Unit tests for the LockableDObject class.
 */

/**
 * @brief Test suite for the LockableDObject class.
 */
class LockableDObjectTest : public ::testing::Test {
protected:
    LockableDObject lockableObject;

    void SetUp() override {
        // Initialize the LockableDObject for tests
        DObject& obj = lockableObject.get();
        obj.setData(42); // Set initial data
    }
};

/**
 * @test Verify the default construction of LockableDObject.
 */
TEST_F(LockableDObjectTest, DefaultConstruction) {
    EXPECT_NO_THROW(LockableDObject obj);
}

/**
 * @test Verify access to the underlying DObject.
 */
TEST_F(LockableDObjectTest, AccessDObject) {
    DObject& obj = lockableObject.get();
    EXPECT_EQ(std::get<int>(obj.getData()), 42); // Ensure the data is accessible
}

/**
 * @test Verify locking and unlocking.
 */
TEST_F(LockableDObjectTest, LockAndUnlock) {
    EXPECT_NO_THROW(lockableObject.lock());
    EXPECT_NO_THROW(lockableObject.unlock());
}

/**
 * @test Verify thread safety with multiple threads.
 */
TEST_F(LockableDObjectTest, ThreadSafety) {
    const int numThreads = 10;
    std::vector<std::thread> threads;
    std::atomic<int> successCount{0};

    // Each thread tries to update the data
    for (int i = 0; i < numThreads; ++i) {
        threads.emplace_back([this, i, &successCount]() {
            lockableObject.lock();
            DObject& obj = lockableObject.get();
            obj.setData(i);
            if (std::get<int>(obj.getData()) == i) {
                successCount++;
            }
            lockableObject.unlock();
        });
    }

    for (auto& t : threads) {
        t.join();
    }

    EXPECT_EQ(successCount, numThreads);
}

/**
 * @test Verify that the DObject remains consistent when accessed via LockableDObject.
 */
TEST_F(LockableDObjectTest, ConsistentState) {
    lockableObject.lock();
    DObject& obj = lockableObject.get();
    obj.setData(100);
    EXPECT_EQ(std::get<int>(obj.getData()), 100);
    lockableObject.unlock();

    lockableObject.lock();
    EXPECT_EQ(std::get<int>(lockableObject.get().getData()), 100);
    lockableObject.unlock();
}
