#include "fl/stl/array.h"
#include "fl/stl/span.h"  // For fl::span
#include "fl/stl/new.h"
#include "test.h"
#include "fl/stl/vector.h"
#include "fl/stl/move.h"
#include "fl/stl/type_traits.h"
#include "fl/stl/int.h"
#include "fl/stl/static_assert.h"

FL_TEST_FILE(FL_FILEPATH) {

using namespace fl;

FL_TEST_CASE("fl::array - Basic construction and initialization") {
    FL_SUBCASE("Default constructor") {
        array<int, 5> arr;
        FL_CHECK_EQ(arr.size(), 5);
        FL_CHECK_FALSE(arr.empty());
    }

    // Note: Fill constructor disabled due to undefined fill_n function
    // FL_SUBCASE("Fill constructor") {
    //     array<int, 5> arr(42);
    //     FL_CHECK_EQ(arr.size(), 5);
    //     for (size i = 0; i < 5; ++i) {
    //         FL_CHECK_EQ(arr[i], 42);
    //     }
    // }

    FL_SUBCASE("Initializer list constructor") {
        array<int, 5> arr = {1, 2, 3, 4, 5};
        FL_CHECK_EQ(arr[0], 1);
        FL_CHECK_EQ(arr[1], 2);
        FL_CHECK_EQ(arr[2], 3);
        FL_CHECK_EQ(arr[3], 4);
        FL_CHECK_EQ(arr[4], 5);
    }

    FL_SUBCASE("Initializer list with fewer elements") {
        array<int, 5> arr = {1, 2, 3};
        FL_CHECK_EQ(arr[0], 1);
        FL_CHECK_EQ(arr[1], 2);
        FL_CHECK_EQ(arr[2], 3);
        // Note: arr[3] and arr[4] are default-initialized
    }

    FL_SUBCASE("Zero-size array") {
        array<int, 0> arr;
        FL_CHECK_EQ(arr.size(), 0);
        FL_CHECK(arr.empty());
    }
}

FL_TEST_CASE("fl::array - Copy and move semantics") {
    FL_SUBCASE("Copy constructor") {
        array<int, 3> arr1 = {1, 2, 3};
        array<int, 3> arr2(arr1);
        FL_CHECK_EQ(arr2[0], 1);
        FL_CHECK_EQ(arr2[1], 2);
        FL_CHECK_EQ(arr2[2], 3);
    }

    FL_SUBCASE("Copy assignment") {
        array<int, 3> arr1 = {1, 2, 3};
        array<int, 3> arr2;
        arr2 = arr1;
        FL_CHECK_EQ(arr2[0], 1);
        FL_CHECK_EQ(arr2[1], 2);
        FL_CHECK_EQ(arr2[2], 3);
    }

    FL_SUBCASE("Move constructor") {
        array<int, 3> arr1 = {1, 2, 3};
        array<int, 3> arr2(fl::move(arr1));
        FL_CHECK_EQ(arr2[0], 1);
        FL_CHECK_EQ(arr2[1], 2);
        FL_CHECK_EQ(arr2[2], 3);
    }

    FL_SUBCASE("Move assignment") {
        array<int, 3> arr1 = {1, 2, 3};
        array<int, 3> arr2;
        arr2 = fl::move(arr1);
        FL_CHECK_EQ(arr2[0], 1);
        FL_CHECK_EQ(arr2[1], 2);
        FL_CHECK_EQ(arr2[2], 3);
    }
}

FL_TEST_CASE("fl::array - Element access") {
    FL_SUBCASE("operator[] access") {
        array<int, 5> arr = {10, 20, 30, 40, 50};
        FL_CHECK_EQ(arr[0], 10);
        FL_CHECK_EQ(arr[2], 30);
        FL_CHECK_EQ(arr[4], 50);
    }

    FL_SUBCASE("operator[] modification") {
        array<int, 3> arr;
        arr[0] = 100;
        arr[1] = 200;
        arr[2] = 300;
        FL_CHECK_EQ(arr[0], 100);
        FL_CHECK_EQ(arr[1], 200);
        FL_CHECK_EQ(arr[2], 300);
    }

    FL_SUBCASE("at() with valid index") {
        array<int, 5> arr = {10, 20, 30, 40, 50};
        FL_CHECK_EQ(arr.at(0), 10);
        FL_CHECK_EQ(arr.at(2), 30);
        FL_CHECK_EQ(arr.at(4), 50);
    }

    FL_SUBCASE("at() with out-of-bounds index returns error value") {
        array<int, 3> arr = {1, 2, 3};
        // at() returns a static error_value when out of bounds
        // This is different from std::array which throws
        int& ref = arr.at(10);
        (void)ref; // Verify it doesn't crash, just returns error value
    }

    FL_SUBCASE("front() and back()") {
        array<int, 5> arr = {10, 20, 30, 40, 50};
        FL_CHECK_EQ(arr.front(), 10);
        FL_CHECK_EQ(arr.back(), 50);
    }

    FL_SUBCASE("front() and back() modification") {
        array<int, 3> arr = {1, 2, 3};
        arr.front() = 100;
        arr.back() = 300;
        FL_CHECK_EQ(arr[0], 100);
        FL_CHECK_EQ(arr[2], 300);
    }

    FL_SUBCASE("data() pointer access") {
        array<int, 3> arr = {10, 20, 30};
        int* ptr = arr.data();
        FL_CHECK_EQ(ptr[0], 10);
        FL_CHECK_EQ(ptr[1], 20);
        FL_CHECK_EQ(ptr[2], 30);
    }

    FL_SUBCASE("const data() pointer access") {
        const array<int, 3> arr = {10, 20, 30};
        const int* ptr = arr.data();
        FL_CHECK_EQ(ptr[0], 10);
        FL_CHECK_EQ(ptr[1], 20);
        FL_CHECK_EQ(ptr[2], 30);
    }
}

FL_TEST_CASE("fl::array - Iterators") {
    FL_SUBCASE("begin() and end()") {
        array<int, 5> arr = {1, 2, 3, 4, 5};
        auto it = arr.begin();
        FL_CHECK_EQ(*it, 1);
        ++it;
        FL_CHECK_EQ(*it, 2);

        // Check distance
        FL_CHECK_EQ(arr.end() - arr.begin(), 5);
    }

    FL_SUBCASE("Range-based for loop") {
        array<int, 5> arr = {10, 20, 30, 40, 50};
        int sum = 0;
        for (int val : arr) {
            sum += val;
        }
        FL_CHECK_EQ(sum, 150);
    }

    FL_SUBCASE("const iterators") {
        const array<int, 3> arr = {1, 2, 3};
        int count = 0;
        for (auto it = arr.begin(); it != arr.end(); ++it) {
            count++;
        }
        FL_CHECK_EQ(count, 3);
    }

    FL_SUBCASE("cbegin() and cend()") {
        array<int, 3> arr = {5, 10, 15};
        int sum = 0;
        for (auto it = arr.cbegin(); it != arr.cend(); ++it) {
            sum += *it;
        }
        FL_CHECK_EQ(sum, 30);
    }

    FL_SUBCASE("Iterator modification") {
        array<int, 3> arr = {1, 2, 3};
        for (auto it = arr.begin(); it != arr.end(); ++it) {
            *it *= 2;
        }
        FL_CHECK_EQ(arr[0], 2);
        FL_CHECK_EQ(arr[1], 4);
        FL_CHECK_EQ(arr[2], 6);
    }
}

FL_TEST_CASE("fl::array - Capacity") {
    FL_SUBCASE("size() and max_size()") {
        array<int, 10> arr;
        FL_CHECK_EQ(arr.size(), 10);
        FL_CHECK_EQ(arr.max_size(), 10);
    }

    FL_SUBCASE("empty() for non-empty array") {
        array<int, 5> arr;
        FL_CHECK_FALSE(arr.empty());
    }

    FL_SUBCASE("empty() for zero-size array") {
        array<int, 0> arr;
        FL_CHECK(arr.empty());
    }
}

FL_TEST_CASE("fl::array - Operations") {
    FL_SUBCASE("fill() method") {
        array<int, 5> arr;
        arr.fill(42);
        for (size i = 0; i < 5; ++i) {
            FL_CHECK_EQ(arr[i], 42);
        }
    }

    FL_SUBCASE("fill() with different types") {
        array<double, 3> arr;
        arr.fill(3.14);
        FL_CHECK_EQ(arr[0], doctest::Approx(3.14));
        FL_CHECK_EQ(arr[1], doctest::Approx(3.14));
        FL_CHECK_EQ(arr[2], doctest::Approx(3.14));
    }

    FL_SUBCASE("swap() member function") {
        array<int, 3> arr1 = {1, 2, 3};
        array<int, 3> arr2 = {10, 20, 30};
        arr1.swap(arr2);

        FL_CHECK_EQ(arr1[0], 10);
        FL_CHECK_EQ(arr1[1], 20);
        FL_CHECK_EQ(arr1[2], 30);

        FL_CHECK_EQ(arr2[0], 1);
        FL_CHECK_EQ(arr2[1], 2);
        FL_CHECK_EQ(arr2[2], 3);
    }

    FL_SUBCASE("swap() non-member function") {
        array<int, 3> arr1 = {1, 2, 3};
        array<int, 3> arr2 = {10, 20, 30};
        fl::swap(arr1, arr2);

        FL_CHECK_EQ(arr1[0], 10);
        FL_CHECK_EQ(arr1[1], 20);
        FL_CHECK_EQ(arr1[2], 30);

        FL_CHECK_EQ(arr2[0], 1);
        FL_CHECK_EQ(arr2[1], 2);
        FL_CHECK_EQ(arr2[2], 3);
    }
}

FL_TEST_CASE("fl::array - Comparison operators") {
    FL_SUBCASE("operator== for equal arrays") {
        array<int, 3> arr1 = {1, 2, 3};
        array<int, 3> arr2 = {1, 2, 3};
        FL_CHECK(arr1 == arr2);
    }

    FL_SUBCASE("operator== for different arrays") {
        array<int, 3> arr1 = {1, 2, 3};
        array<int, 3> arr2 = {1, 2, 4};
        FL_CHECK_FALSE(arr1 == arr2);
    }

    FL_SUBCASE("operator!= for different arrays") {
        array<int, 3> arr1 = {1, 2, 3};
        array<int, 3> arr2 = {1, 2, 4};
        FL_CHECK(arr1 != arr2);
    }

    FL_SUBCASE("operator!= for equal arrays") {
        array<int, 3> arr1 = {1, 2, 3};
        array<int, 3> arr2 = {1, 2, 3};
        FL_CHECK_FALSE(arr1 != arr2);
    }

    FL_SUBCASE("operator< for arrays") {
        array<int, 3> arr1 = {1, 2, 3};
        array<int, 3> arr2 = {1, 2, 4};
        FL_CHECK(arr1 < arr2);
        FL_CHECK_FALSE(arr2 < arr1);
    }

    FL_SUBCASE("operator<= for arrays") {
        array<int, 3> arr1 = {1, 2, 3};
        array<int, 3> arr2 = {1, 2, 3};
        array<int, 3> arr3 = {1, 2, 4};
        FL_CHECK(arr1 <= arr2);
        FL_CHECK(arr1 <= arr3);
        FL_CHECK_FALSE(arr3 <= arr1);
    }

    FL_SUBCASE("operator> for arrays") {
        array<int, 3> arr1 = {1, 2, 3};
        array<int, 3> arr2 = {1, 2, 4};
        FL_CHECK(arr2 > arr1);
        FL_CHECK_FALSE(arr1 > arr2);
    }

    FL_SUBCASE("operator>= for arrays") {
        array<int, 3> arr1 = {1, 2, 3};
        array<int, 3> arr2 = {1, 2, 3};
        array<int, 3> arr3 = {1, 2, 4};
        FL_CHECK(arr1 >= arr2);
        FL_CHECK(arr3 >= arr1);
        FL_CHECK_FALSE(arr1 >= arr3);
    }
}

FL_TEST_CASE("fl::array - Different types") {
    FL_SUBCASE("array<float>") {
        array<float, 3> arr = {1.5f, 2.5f, 3.5f};
        FL_CHECK_EQ(arr[0], doctest::Approx(1.5f));
        FL_CHECK_EQ(arr[1], doctest::Approx(2.5f));
        FL_CHECK_EQ(arr[2], doctest::Approx(3.5f));
    }

    FL_SUBCASE("array<double>") {
        array<double, 3> arr = {1.5, 2.5, 3.5};
        FL_CHECK_EQ(arr[0], doctest::Approx(1.5));
        FL_CHECK_EQ(arr[1], doctest::Approx(2.5));
        FL_CHECK_EQ(arr[2], doctest::Approx(3.5));
    }

    FL_SUBCASE("array<char>") {
        array<char, 5> arr = {'H', 'e', 'l', 'l', 'o'};
        FL_CHECK_EQ(arr[0], 'H');
        FL_CHECK_EQ(arr[4], 'o');
    }

    FL_SUBCASE("array<bool>") {
        array<bool, 3> arr = {true, false, true};
        FL_CHECK(arr[0]);
        FL_CHECK_FALSE(arr[1]);
        FL_CHECK(arr[2]);
    }
}

FL_TEST_CASE("fl::array - Edge cases") {
    FL_SUBCASE("Single element array") {
        array<int, 1> arr = {42};
        FL_CHECK_EQ(arr.size(), 1);
        FL_CHECK_EQ(arr[0], 42);
        FL_CHECK_EQ(arr.front(), 42);
        FL_CHECK_EQ(arr.back(), 42);
    }

    FL_SUBCASE("Large array") {
        array<int, 100> arr;
        arr.fill(7);
        FL_CHECK_EQ(arr.size(), 100);
        FL_CHECK_EQ(arr[0], 7);
        FL_CHECK_EQ(arr[99], 7);
    }

    FL_SUBCASE("Array of arrays") {
        array<array<int, 2>, 2> arr;
        arr[0][0] = 1;
        arr[0][1] = 2;
        arr[1][0] = 3;
        arr[1][1] = 4;

        FL_CHECK_EQ(arr[0][0], 1);
        FL_CHECK_EQ(arr[0][1], 2);
        FL_CHECK_EQ(arr[1][0], 3);
        FL_CHECK_EQ(arr[1][1], 4);
    }
}

FL_TEST_CASE("fl::array - Type traits") {
    FL_SUBCASE("value_type") {
        using ArrayType = array<int, 5>;
        FL_STATIC_ASSERT(fl::is_same<ArrayType::value_type, int>::value, "value_type should be int");
    }

    FL_SUBCASE("size_type") {
        using ArrayType = array<int, 5>;
        FL_STATIC_ASSERT(fl::is_same<ArrayType::size_type, fl::size>::value, "size_type should be fl::size");
    }

    FL_SUBCASE("iterator types") {
        using ArrayType = array<int, 5>;
        FL_STATIC_ASSERT(fl::is_same<ArrayType::iterator, int*>::value, "iterator should be int*");
        FL_STATIC_ASSERT(fl::is_same<ArrayType::const_iterator, const int*>::value, "const_iterator should be const int*");
    }
}

FL_TEST_CASE("fl::array - Const correctness") {
    FL_SUBCASE("const array element access") {
        const array<int, 3> arr = {10, 20, 30};
        FL_CHECK_EQ(arr[0], 10);
        FL_CHECK_EQ(arr.at(1), 20);
        FL_CHECK_EQ(arr.front(), 10);
        FL_CHECK_EQ(arr.back(), 30);
    }

    FL_SUBCASE("const array data pointer") {
        const array<int, 3> arr = {10, 20, 30};
        const int* ptr = arr.data();
        FL_CHECK_EQ(ptr[0], 10);
        FL_CHECK_EQ(ptr[1], 20);
        FL_CHECK_EQ(ptr[2], 30);
    }

    FL_SUBCASE("const array iterators") {
        const array<int, 3> arr = {1, 2, 3};
        int sum = 0;
        for (const auto& val : arr) {
            sum += val;
        }
        FL_CHECK_EQ(sum, 6);
    }
}

FL_TEST_CASE("fl::array - to_array() helper function from span") {
    FL_SUBCASE("from C array via dynamic span") {
        int source_data[] = {10, 20, 30, 40, 50};
        fl::span<const int> s(source_data, 5);

        // Convert span to array using to_array helper
        fl::array<int, 5> arr = fl::to_array<5>(s);

        FL_CHECK_EQ(arr.size(), 5);
        FL_CHECK_EQ(arr[0], 10);
        FL_CHECK_EQ(arr[1], 20);
        FL_CHECK_EQ(arr[2], 30);
        FL_CHECK_EQ(arr[3], 40);
        FL_CHECK_EQ(arr[4], 50);

        // Verify it's a copy
        arr[0] = 99;
        FL_CHECK_EQ(source_data[0], 10);
    }

    FL_SUBCASE("from C array via static extent span") {
        int source_data[] = {100, 200, 300};
        fl::span<const int, 3> s(source_data, 3);

        // Convert static extent span to array using to_array helper
        fl::array<int, 3> arr = fl::to_array(s);

        FL_CHECK_EQ(arr.size(), 3);
        FL_CHECK_EQ(arr[0], 100);
        FL_CHECK_EQ(arr[1], 200);
        FL_CHECK_EQ(arr[2], 300);
    }

    FL_SUBCASE("from vector via span") {
        fl::vector<int> heap_vec;
        heap_vec.push_back(1);
        heap_vec.push_back(2);
        heap_vec.push_back(3);
        heap_vec.push_back(4);

        fl::span<const int> s(heap_vec);
        fl::array<int, 4> arr = fl::to_array<4>(s);

        FL_CHECK_EQ(arr.size(), 4);
        FL_CHECK_EQ(arr[0], 1);
        FL_CHECK_EQ(arr[1], 2);
        FL_CHECK_EQ(arr[2], 3);
        FL_CHECK_EQ(arr[3], 4);
    }

    FL_SUBCASE("from FixedVector via span") {
        fl::FixedVector<int, 10> fixed_vec;
        fixed_vec.push_back(5);
        fixed_vec.push_back(6);
        fixed_vec.push_back(7);

        fl::span<const int> s(fixed_vec);
        fl::array<int, 3> arr = fl::to_array<3>(s);

        FL_CHECK_EQ(arr.size(), 3);
        FL_CHECK_EQ(arr[0], 5);
        FL_CHECK_EQ(arr[1], 6);
        FL_CHECK_EQ(arr[2], 7);
    }
}

} // FL_TEST_FILE
