#include <gtest/gtest.h>

#include "render_3D/detail/Ordered_Artifact_Transaction.h"
#include "render_3D/detail/Ordered_Backend_Shutdown.h"

#include <string>
#include <vector>

namespace renderive::detail {
namespace {

struct Fake_Artifact {
    int stream = 7;
};

struct Fake_Transaction_Api {
    std::vector<std::string> events;
    Fake_Artifact artifact;
    bool emit_succeeds = true;
    bool attach_succeeds = true;
    bool execute_succeeds = true;
    std::size_t execute_count{};

    Fake_Artifact* emit() {
        events.emplace_back("emit");
        return emit_succeeds ? &artifact : nullptr;
    }

    int* stream(Fake_Artifact& value) {
        events.emplace_back("stream");
        return &value.stream;
    }

    bool attach() {
        events.emplace_back("attach");
        return attach_succeeds;
    }

    bool execute(const int&) {
        events.emplace_back("execute");
        ++execute_count;
        return execute_succeeds;
    }

    void destroy(Fake_Artifact&) { events.emplace_back("destroy"); }
};

TEST(DatovizTransaction, SuccessfulEmitExecutesExactlyOnceInCausalOrder) {
    Fake_Transaction_Api api;

    EXPECT_TRUE(static_cast<bool>(execute_ordered_artifact_transaction(api)));
    EXPECT_EQ(api.execute_count, 1U);
    EXPECT_EQ(api.events, std::vector<std::string>({"emit", "stream", "attach", "execute", "destroy"}));
}

TEST(DatovizTransaction, AttachFailureStillConsumesEmittedArtifactExactlyOnce) {
    Fake_Transaction_Api api;
    api.attach_succeeds = false;

    EXPECT_FALSE(static_cast<bool>(execute_ordered_artifact_transaction(api)));
    EXPECT_EQ(api.execute_count, 1U);
    EXPECT_EQ(api.events, std::vector<std::string>({"emit", "stream", "attach", "execute", "destroy"}));
}

TEST(DatovizTransaction, ExecuteFailureConsumesAndDestroysArtifactBeforeRecovery) {
    Fake_Transaction_Api api;
    api.execute_succeeds = false;

    EXPECT_FALSE(static_cast<bool>(execute_ordered_artifact_transaction(api)));
    EXPECT_EQ(api.execute_count, 1U);
    EXPECT_EQ(api.events, std::vector<std::string>({"emit", "stream", "attach", "execute", "destroy"}));
}

TEST(DatovizTransaction, FailedEmitDoesNotStartAttachOrExecute) {
    Fake_Transaction_Api api;
    api.emit_succeeds = false;

    EXPECT_FALSE(static_cast<bool>(execute_ordered_artifact_transaction(api)));
    EXPECT_EQ(api.execute_count, 0U);
    EXPECT_EQ(api.events, std::vector<std::string>({"emit"}));
}

TEST(DatovizOwnership, ShutdownOrderProtectsBorrowedDeviceAndTargets) {
    std::vector<std::string> events;

    ordered_backend_shutdown(
        [&] { events.emplace_back("runtime"); },
        [&] { events.emplace_back("targets"); },
        [&] { events.emplace_back("scene"); },
        [&] { events.emplace_back("gpu_ctx"); });

    EXPECT_EQ(events, std::vector<std::string>({"runtime", "targets", "scene", "gpu_ctx"}));
}

} // namespace
} // namespace renderive::detail
