#include <Juliet.h>
#if JULIET_DEBUG

#include <algorithm>
#include <Core/Common/CoreUtils.h>
#include <Core/Container/Vector.h>

namespace Juliet::UnitTest
{
    namespace
    {
        template <typename T, size_t ReserveSize>
        inline void VerifyVectorState(const VectorArena<T, ReserveSize>& vec, size_t expectedCount)
        {
            Assert(vec.Size() == expectedCount);

            if (expectedCount == 0)
            {
                Assert(vec.begin() == nullptr);
                Assert(vec.end() == nullptr);
            }
            else
            {
                Assert(vec.begin() != nullptr);
                Assert(vec.end() == vec.begin() + expectedCount);
            }
        }
    } // namespace

    void VectorUnitTest()
    {
        ArenaParams params{};
        Arena*      testArena = ArenaAllocate(params JULIET_DEBUG_ONLY(, "VectorUnitTestArena"));

        // Test 1: Integer VectorArena (Basic Operations)
        {
            VectorArena<int> vec = {};
            vec.Create(testArena);
            VerifyVectorState(vec, 0);

            vec.PushBack(10);
            VerifyVectorState(vec, 1);
            Assert(vec[0] == 10);
            Assert(*vec.begin() == 10);
            Assert(*(vec.end() - 1) == 10);

            vec.PushBack(20);
            VerifyVectorState(vec, 2);
            Assert(vec[0] == 10);
            Assert(vec[1] == 20);
            Assert(*(vec.end() - 1) == 20);

            vec.PushBack(30);
            VerifyVectorState(vec, 3);
            Assert(vec[2] == 30);
            Assert(*(vec.end() - 1) == 30);

            // Test Iteration
            int sum = 0;
            for (int val : vec)
            {
                sum += val;
            }
            Assert(sum == 60);
            vec.Destroy();
        }

        // Test 2: RemoveAtFast
        {
            VectorArena<int> vec = {};
            vec.Create(testArena);

            vec.PushBack(10);
            vec.PushBack(20);
            vec.PushBack(30);
            VerifyVectorState(vec, 3);

            // Remove index 0 (value 10). Should swap with Last (30).
            vec.RemoveAtFast(0);
            VerifyVectorState(vec, 2);
            Assert(vec[0] == 30);
            Assert(vec[1] == 20);
            Assert(*(vec.end() - 1) == 20);

            // Remove index 1 (value 20).
            vec.RemoveAtFast(1);
            VerifyVectorState(vec, 1);
            Assert(vec[0] == 30);
            Assert(*(vec.end() - 1) == 30);

            // Remove remaining element
            vec.RemoveAtFast(0);
            VerifyVectorState(vec, 0);

            vec.Destroy();
        }

        // Test 3: Clear and Reuse (Pointer State Check)
        {
            VectorArena<int> vec = {};
            vec.Create(testArena);

            vec.PushBack(100);
            vec.PushBack(200);
            VerifyVectorState(vec, 2);

            vec.Clear();
            VerifyVectorState(vec, 0); // Should verify begin/end are nullptr

            vec.PushBack(300);
            VerifyVectorState(vec, 1);
            Assert(vec[0] == 300);
            Assert(*(vec.end() - 1) == 300);

            vec.Destroy();
        }

        // Test 4: Struct VectorArena
        {
            struct TestItem
            {
                int   A;
                float B;

                bool operator==(const TestItem& other) const
                {
                    float diff = B - other.B;
                    return A == other.A && (diff > -0.0001f && diff < 0.0001f);
                }
            };

            VectorArena<TestItem> vec = {};
            vec.Create(testArena);

            VerifyVectorState(vec, 0);

            vec.PushBack({ 1, 1.0f });
            vec.PushBack({ 2, 2.0f });
            vec.PushBack({ 3, 3.0f });
            VerifyVectorState(vec, 3);

            // Remove middle element (index 1)
            vec.RemoveAtFast(1);
            VerifyVectorState(vec, 2);
            Assert(vec[0] == (TestItem{ 1, 1.0f }));
            Assert(vec[1] == (TestItem{ 3, 3.0f }));

            vec.Destroy();
        }

        // Test 5: Move Semantics
        {
            struct MoveOnly
            {
                int  Value;
                bool MovedFrom = false;

                MoveOnly(int v)
                  : Value(v)
                {
                }
                MoveOnly(const MoveOnly&) = delete; // No copy
                [[maybe_unused]] MoveOnly(MoveOnly&& other) noexcept
                  : Value(other.Value)
                  , MovedFrom(false)
                {
                    other.MovedFrom = true;
                    other.Value     = -1;
                }
                MoveOnly&                  operator=(const MoveOnly&) = delete;
                [[maybe_unused]] MoveOnly& operator=(MoveOnly&& other) noexcept
                {
                    Value           = other.Value;
                    MovedFrom       = false;
                    other.MovedFrom = true;
                    other.Value     = -1;
                    return *this;
                }
            };

            VectorArena<MoveOnly, 4> vec = {};
            NonNullPtr<Arena>        validArena(testArena);
            vec.Create(validArena);

            MoveOnly item1(10);
            vec.PushBack(std::move(item1));

            Assert(item1.MovedFrom); // Verify original was moved from
            Assert(vec.Size() == 1);
            Assert(vec[0].Value == 10);
            Assert(!vec[0].MovedFrom);

            vec.Destroy();
        }

        // Test 6: Resize Behavior
        {
            NonNullPtr<Arena> validArena(testArena);

            VectorArena<int, 4> vec = {};
            vec.Create(validArena);

            vec.PushBack(1);
            vec.PushBack(2);
            Assert(vec.Size() == 2);

            // Resize smaller
            vec.Resize(1);
            Assert(vec.Size() == 1);
            Assert(vec[0] == 1);

            // Resize larger up to max capacity
            vec.Resize(4);
            Assert(vec.Size() == 4);
            Assert(vec[0] == 1);

            vec.Destroy();
        }

        // Test 7: External Arena Usage & Volume Test within capacity
        {
            NonNullPtr<Arena> validArena(testArena);

            VectorArena<int, 100> vec = {};
            vec.Create(validArena); // Pass external arena

            for (int i = 0; i < 100; ++i)
            {
                vec.PushBack(i);
            }
            VerifyVectorState(vec, 100);

            for (int i = 0; i < 100; ++i)
            {
                Assert(vec[static_cast<size_t>(i)] == i);
            }

            vec.Destroy();
        }

        ArenaRelease(testArena);
    }
} // namespace Juliet::UnitTest
#endif
