#include <balancer/kernel/fs/shared_files.h>

#include <balancer/kernel/fs/shared_file_exists_checker.h>

#include <balancer/kernel/testing/simple_barrier.h>

#include <library/cpp/testing/unittest/registar.h>

#include <util/system/fs.h>

#include <thread>

using namespace NSrvKernel;
using namespace NSrvKernel::NTesting;

Y_UNIT_TEST_SUITE(TSharedFileExistsCheckerTest) {
    Y_UNIT_TEST(DefaultConstructedTest) {
        TSharedFileExistsChecker fileExistsChecker;

        UNIT_ASSERT_EQUAL(fileExistsChecker.Exists(), false);
    }

    Y_UNIT_TEST(WorkersThreadSafetyTest) {
        static constexpr int32_t WorkerCount = 5;
        constexpr char Path[] = "test.txt";

        TSharedFiles sharedFiles;
        auto fileExistsChecker = sharedFiles.FileChecker(Path, TDuration::Seconds(1));

        sharedFiles.Start();

        auto waitForStateChange = [](const TSharedFileExistsChecker& checker, bool expectedState) {
            WaitForConditionOrDeadline([&]() { return checker.Exists() == expectedState; });

            UNIT_ASSERT_EQUAL(checker.Exists(), expectedState);
        };

        const TVector<bool> states = {
            false,
            true,
            false,
            true,
            false
        };

        TSimpleBarrier barrier(WorkerCount);

        auto worker = [&]() {
            auto workerFileExistsChecker = sharedFiles.FileChecker(Path, TDuration::Seconds(1));

            for (auto state : states) {
                waitForStateChange(workerFileExistsChecker, state);
                barrier.Pass();
            }
        };

        TVector<std::thread> workers;
        for (size_t i = 0; i < WorkerCount; i++) {
            workers.emplace_back(worker);
        }

        for (auto state : states) {
            if (!state) {
                NFs::Remove(Path);
            } else {
                NFs::MakeDirectory(Path);
            }
            waitForStateChange(fileExistsChecker, state);
            barrier.WaitForAllWorkersToPass();
        }

        for (auto& worker : workers) {
            worker.join();
        }
    }
}
