/*********************************************************************
 * \file   BigFile.cpp
 *
 * \author Romain BOULLARD
 * \date   December 2025
 *********************************************************************/
#include <Engine/BigFile/BigFile.hpp>

#include <Engine/EngineLogger_generated.hpp>

#include <System/Log/Log.hpp>
#include <System/Time/Time.hpp>
#include <System/UUID/UUID.hpp>

#include <Utils/Singleton.hpp>
#include <Utils/TargetMacros.h>

#include <EngineTests/BigFileInfo_generated.hpp>

#include <ankerl/unordered_dense.h>
#include <flatbuffers/reflection.h>

#include <gtest/gtest.h>

namespace Bigfoot
{
class BigFileFixture: public ::testing::Test
{
  protected:
    void SetUp() override
    {
        BigFile::Request deleteHeader {m_bigFile, "DELETE FROM AssetHeader"};
        BigFile::Request deleteAsset {m_bigFile, "DELETE FROM Asset"};

        m_bigFile.BeginTransaction();
        deleteHeader.Execute();
        deleteAsset.Execute();
        m_bigFile.CommitTransaction();

        BIGFOOT_NOT_OPTIMIZED_ONLY(std::ignore = Singleton<Log>::Instance().RegisterLogger(ENGINE_LOGGER);)
    }

    BIGFOOT_NOT_OPTIMIZED_ONLY(Singleton<Log>::Lifetime m_loggerLifetime;)

    BigFile m_bigFile {File {BIGFILE_ENGINETESTS_LOCATION}};
};

/****************************************************************************************/

TEST_F(BigFileFixture, BigFileManipulation)
{
    UUID uuid;
    UUID uuid2;
    UUID uuid3;

    constexpr eastl::array<std::byte, 4> blob {std::byte {1}, std::byte {2}, std::byte {3}, std::byte {4}};
    constexpr eastl::array<std::byte, 4> blob2 {std::byte {1}, std::byte {2}, std::byte {3}, std::byte {5}};
    constexpr eastl::array<std::byte, 4> blob3 {std::byte {1}, std::byte {2}, std::byte {3}, std::byte {6}};
    constexpr eastl::array<std::byte, 4> blob4 {std::byte {10}, std::byte {11}, std::byte {12}, std::byte {13}};

    constexpr std::uint64_t typeIDRef = std::numeric_limits<std::uint64_t>::max() - 1;

    {
        BigFile::Request assetHeaderRequest {
          m_bigFile,
          "INSERT INTO AssetHeader (UUID, Name, TypeID, TypeName) VALUES(?, ?, ?, ?)"};
        assetHeaderRequest.Bind(1, static_cast<std::span<const std::byte, UUID::UUID_BYTE_SIZE>>(uuid));
        assetHeaderRequest.Bind(2, "Test");
        assetHeaderRequest.Bind(3, typeIDRef);
        assetHeaderRequest.Bind(4, "TypeTest");

        BigFile::Request assetRequest {m_bigFile, "INSERT INTO Asset (UUID, Asset) VALUES(?, ?)"};
        assetRequest.Bind(1, static_cast<std::span<const std::byte, UUID::UUID_BYTE_SIZE>>(uuid));
        assetRequest.Bind(2, blob);

        BigFile::Request assetHeaderRequest2 {
          m_bigFile,
          "INSERT INTO AssetHeader (UUID, Name, TypeID, TypeName) VALUES(?, ?, ?, ?)"};
        assetHeaderRequest2.Bind(1, static_cast<std::span<const std::byte, UUID::UUID_BYTE_SIZE>>(uuid2));
        assetHeaderRequest2.Bind(2, "Test2");
        assetHeaderRequest2.Bind(3, typeIDRef);
        assetHeaderRequest2.Bind(4, "TypeTest");

        BigFile::Request assetRequest2 {m_bigFile, "INSERT INTO Asset (UUID, Asset) VALUES(?, ?)"};
        assetRequest2.Bind(1, static_cast<std::span<const std::byte, UUID::UUID_BYTE_SIZE>>(uuid2));
        assetRequest2.Bind(2, blob3);

        BigFile::Request assetHeaderRequest3 {
          m_bigFile,
          "INSERT INTO AssetHeader (UUID, Name, TypeID, TypeName) VALUES(?, ?, ?, ?)"};
        assetHeaderRequest3.Bind(1, static_cast<std::span<const std::byte, UUID::UUID_BYTE_SIZE>>(uuid3));
        assetHeaderRequest3.Bind(2, "Test3");
        assetHeaderRequest3.Bind(3, typeIDRef);
        assetHeaderRequest3.Bind(4, "TypeTest");

        BigFile::Request assetRequest3 {m_bigFile, "INSERT INTO Asset (UUID, Asset) VALUES(?, ?)"};
        assetRequest3.Bind(1, static_cast<std::span<const std::byte, UUID::UUID_BYTE_SIZE>>(uuid3));
        assetRequest3.Bind(2, blob4);

        m_bigFile.BeginTransaction();
        std::uint32_t assetHeaderChangedCount = assetHeaderRequest.Execute();
        EXPECT_EQ(assetHeaderChangedCount, 1);
        std::uint32_t assetChangedCount = assetRequest.Execute();
        EXPECT_EQ(assetChangedCount, 1);

        std::uint32_t assetHeaderChangedCount2 = assetHeaderRequest2.Execute();
        EXPECT_EQ(assetHeaderChangedCount2, 1);
        std::uint32_t assetChangedCount2 = assetRequest2.Execute();
        EXPECT_EQ(assetChangedCount2, 1);

        std::uint32_t assetHeaderChangedCount3 = assetHeaderRequest3.Execute();
        EXPECT_EQ(assetHeaderChangedCount3, 1);
        std::uint32_t assetChangedCount3 = assetRequest3.Execute();
        EXPECT_EQ(assetChangedCount3, 1);

        m_bigFile.CommitTransaction();
    }

    {
        BigFile::Request updateAsset {m_bigFile, "UPDATE Asset SET Asset = ? WHERE UUID = ?"};
        updateAsset.Bind(1, blob2);
        updateAsset.Bind(2, static_cast<std::span<const std::byte, UUID::UUID_BYTE_SIZE>>(uuid));

        m_bigFile.BeginTransaction();
        std::uint32_t updateAssetChangedCount = updateAsset.Execute();
        EXPECT_EQ(updateAssetChangedCount, 1);
        m_bigFile.CommitTransaction();
    }

    {
        BigFile::Request request {
          m_bigFile,
          "SELECT Name, TypeID, TypeName, CreateTime, ModificationTime FROM AssetHeader WHERE UUID = ?"};
        request.Bind(1, static_cast<std::span<const std::byte, UUID::UUID_BYTE_SIZE>>(uuid));

        const bool get = request.Step();
        EXPECT_TRUE(get);

        const eastl::string name {static_cast<eastl::string_view>(request.Get(0))};
        EXPECT_STREQ(name.c_str(), "Test");
        const std::uint64_t typeID = static_cast<std::uint64_t>(request.Get(1));
        EXPECT_EQ(typeID, typeIDRef);
        const eastl::string typeName {static_cast<eastl::string_view>(request.Get(2))};
        EXPECT_STREQ(typeName.c_str(), "TypeTest");

        const Time createTime = static_cast<std::uint64_t>(request.Get(3));
        const Time modificationTime = static_cast<std::uint64_t>(request.Get(4));

        EXPECT_GT(createTime.Epoch(), 0);
        EXPECT_GT(modificationTime.Epoch(), 0);

        EXPECT_GE(createTime, modificationTime);

        EXPECT_GT(createTime.Year(), 2025);
        EXPECT_GT(modificationTime.Year(), 2025);
    }
}
}   // namespace Bigfoot
