#include "repository_graph.h"

#include <iostream>
#include <random>
#include <stdexcept>
#include <vector>

namespace
{
void Require(bool condition, const char* message)
{
    if (!condition) throw std::runtime_error(message);
}

void RunDeterministicTests()
{
    ReposGraph graph;
    VectorTime a;
    VectorTime b;
    Require(graph.CheckIn({0, 0}, a) == ReposError::Ok, "first check-in failed");
    a.Add(0, 1);
    Require(graph.CheckIn({1, 0}, b) == ReposError::Ok, "concurrent check-in failed");
    b.Add(1, 1);

    VectorTime merged = a;
    merged.Add(1, 1);
    Require(graph.CheckIn({0, 1}, merged) == ReposError::Ok, "merged check-in failed");
    Require(graph.GetJoinNodeCount() == 1, "merge did not create one explicit join");
    Require(graph.GetCheckinNodeCount() == 3, "wrong number of check-in nodes");

    VectorTime invalid;
    invalid.Add(0, 2);
    Require(graph.ValidateVectorTime(invalid) == ReposError::VectorTimeViolatesCausality,
            "causality violation was accepted");
    Require(graph.CheckIn({0, 0}, VectorTime{}) == ReposError::OperationAlreadyPresent,
            "duplicate operation was accepted");
    VectorTime missing;
    missing.Add(2, 1);
    Require(graph.CheckIn({2, 1}, missing) == ReposError::OperationMissing,
            "missing operation was accepted");
}

void RunHistoricalSimulation()
{
    std::mt19937 random(178);
    for (int run = 0; run < 10000; ++run)
    {
        ReposGraph graph;
        std::vector<VectorTime> sites(3);
        for (int event = 0; event < 30; ++event)
        {
            const int s = static_cast<int>(random() % sites.size());
            VectorTime& v = sites[static_cast<std::size_t>(s)];
            Require(graph.CheckIn({s, v(s)}, v) == ReposError::Ok, "valid simulated check-in failed");
            v.Add(s, v(s) + 1);
            if (random() % 3 == 0) v = graph.GetVectorTime();
        }
    }
}
}

int main()
{
    try
    {
        RunDeterministicTests();
        RunHistoricalSimulation();
        std::cout << "All deterministic tests and 10,000 explicit-join simulations passed.\n";
        return 0;
    }
    catch (const std::exception& error)
    {
        std::cerr << "Test failed: " << error.what() << '\n';
        return 1;
    }
}
